Py.Cafe

marie-anne/

2025-w1-figurefriday

Small dash app with one cardfunction serving 4 cards.

DocsPricing
  • assets/
  • app.py
  • ff2025-week1.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# -*- coding: utf-8 -*-
"""
Created on Sat Jan  4 11:51:17 2025

@author: win11
"""

import plotly.express as px
import pandas as pd
from dash import Dash, html, dcc, Input, Output, callback
import dash_bootstrap_components as dbc
import dash_daq as daq


###############################################################

# DATAPROCESSING #

###############################################################

df_raw = pd.read_csv('https://raw.githubusercontent.com/tigi/figurefriday/refs/heads/main/2025-figurefriday-week1/NYC%20Marathon%20Results%2C%202024%20-%20Marathon%20Runner%20Results.csv')


df = df_raw[['runnerId', 'age', 'gender','countryCode','racesCount']].copy()
#drop age = 0, with all the nans
df = df.dropna()
#add true/false column for usa citizen
df['Usa citizen'] = df['countryCode'].apply(lambda x: 1 if x=='USA' else 0)



df_grouped_age =df.groupby(['age', 'gender','Usa citizen'])['age'].count().rename('Count').to_frame().reset_index()

#drop gender=x because I want to end with 4 groups, total of 119 runners dropped out of 55000+
df_grouped_age = df_grouped_age[(df_grouped_age['gender'] != 'X')].sort_values(by=['age'])



###############################################################

# BUCKET LIST THING FOR PEOPLE ABROAD?#

###############################################################



# Create plot
fig = px.area(df_grouped_age, x="age", y="Count", color= 'gender', color_discrete_map= {'M': '#1261a0',
                                      'W': '#0895d3'}, line_group='Usa citizen',
              pattern_shape_sequence=["|", ""],pattern_shape = 'Usa citizen', line_shape='spline')


#customize texts
fig.update_layout(
    title="You are never too old to run a marathon",
    xaxis_title="Runners age",
    yaxis_title="Total number of runners",
    legend_title="Profile",

)



#make legend for traces understandable
for tr in fig.select_traces():
    tr['name'] = tr['name'].replace('W,','Female')
    tr['name'] = tr['name'].replace('M,','Male')
    tr['name'] = tr['name'].replace('0','from abroad')
    tr['name'] = tr['name'].replace('1','from the USA')
    
    
    
def create_age_card(age):
    
    #imagesources
    
    age_image = age
    if age not in (40,50,60):
        age_image = "unknown"
    
    
    
    
    #calculate some values
    #%firsttimes
    
    total = len(df[(df['age'] == age)])
    total_first = len(df[(df['age'] == age) & (df['racesCount'] == 1)])
    if total > 0: 
        first_count=str(round(100*(total_first/total),1)) 
    else: 
        first_count='unknown'
    races_median = df[(df['age'] == age)]['racesCount'].median()
    races_max = df[(df['age'] == age)]['racesCount'].max()
    total_abroad = len(df[(df['age'] == age) & (df['Usa citizen'] == 0)])
    if total > 0: 
        foreigners  =str(round(100*(total_abroad/total),1))
    else: 
        foreigners = "unknown"
    
    
    card = dbc.Card(
    [
        dbc.Row(
            [
                dbc.Col(
                    dbc.CardImg(
                        src=f"assets/{age_image}.jpg",
                        className="img-fluid rounded-start",
                    ),
                    className="col-md-4",
                ),
                dbc.Col(
                    dbc.CardBody(
                        [
                            html.H4(f"Profile {age} years", className="card-title"),
                            html.Span(
                               "First USA official marathon?",
                                className="card-text",style={"fontSize": "14px"}
                            ),
                            html.Span(
                               f" {first_count} %",
                                className="card-text", style={"fontWeight":"bold","fontSize": "14px"}
                            ),
                            html.Br(),
                            html.Span('Runner with most registered races: ', style={"fontSize": "14px"}),
                            html.Span(
                               f" {races_max} races",
                                className="card-text", style={"fontWeight":"bold","fontSize": "14px"}
                            ),
                            html.Br(),
                            html.Span("Coming from abroad? ",style={"fontSize": "14px"}),
                            html.Span(
                               f"{foreigners}% of runners",
                                className="card-text", style={"fontWeight":"bold","fontSize": "14px"}
                            ),

                        ]
                    ),
                    className="col-md-8",
                ),
            ],
            className="g-0 d-flex align-items-center",
        )
    ],
    className="mb-4",
    style={"maxWidth": "540px"},
)
    
    
    
    
    return card

 


dbc_css = "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates/dbc.min.css"
app = Dash(__name__, external_stylesheets=[dbc.themes.SANDSTONE, dbc_css])



app.layout = dbc.Container(
    [   dbc.Row([
        html.H1("Is the New York City Marathon on your bucketlist?"),
        dbc.Col([

            dcc.Graph(id="scatter-plot", figure = fig),
      
            
            ], className = 'col-md-12'),
        

          
        ]),
        
        dbc.Row([
            dbc.Col([create_age_card(40)],className='col-md-4'),
            dbc.Col([create_age_card(50)],className='col-md-4'),
            dbc.Col([create_age_card(60)],className='col-md-4')
            
            
            ]),
        
        
        dbc.Row([
            
            dbc.Col( 
                [html.H4('Enter your age and see your potential based on 2024 runner information of the NYCM'),
                    daq.NumericInput(
        id='my-numeric-input-1', min=18, max=88, value=30),
                    
#                     dcc.Input(
#     id="my-numeric-input-1", type="number", placeholder="Enter your age",
#     min=18, max=88, step=1,value=30
# ),
                    
                    
                    html.Br(),
    ], style={"backgroundColor": "#0895d3", "borderRadius": "4px", "color": "white", "padding": "1rem","textAlign":"center"}),
            dbc.Col([html.Div(id='numeric-input-output-1', children=create_age_card(30))]),
            dbc.Col([html.H2('Go for it!')], style={"textAlign": "Center"})
            
            ])
        
        
        
        
        
        
], style={"marginTop": "2rem"})


@callback(
    Output('numeric-input-output-1', 'children'),
    Input('my-numeric-input-1', 'value')
)
def update_output(value):
    return create_age_card(value)


app.run_server(debug=True)