Py.Cafe

marie-anne/

2025-figurefriday-w38

ATP end of year top3, 1992 - 2024

DocsPricing
  • assets/
  • app.py
  • app5.py
  • die-anderen.csv
  • one-hit-wonders.csv
  • requirements.txt
  • top20mannen.csv
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# -*- coding: utf-8 -*-
"""
Created on Thu Feb 20 15:00:14 2025

@author: win11
"""

import dash
from dash import dcc, html, callback,clientside_callback, Input, Output, Patch
import pandas as pd
import dash_bootstrap_components as dbc
import plotly.graph_objects as go




#load figure template
from dash_bootstrap_templates import load_figure_template
load_figure_template("cyborg")

#load theme bootstrap css

dbc_css = "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates/dbc.min.css"



colors13  =  [
    '#FFB347',  # Peach Orange
    '#87CEEB',  # Sky Blue
    '#FFE900',  # Bright Yellow
    '#DDA0DD',  # Plum
    '#FF8C00',  # Dark Orange
    '#ADD8E6',  # Light Blue
    '#FFA500',  # Orange
    '#E6E6FA',  # Lavender
    '#FFFF99',  # Light Yellow
    '#FF6347',  # Tomato
    '#FFE135',  # Bright Yellow
    '#FF7F50',  # Coral
    '#F0E68C'   # Khaki
]


# READ AND PROCESS DATA STATIONS
data_all = pd.read_csv('one-hit-wonders.csv')
#convert year to pandas year
data_all['year'] = pd.to_datetime(data_all['year'], format='%Y', errors='coerce')
#convert rank from float to int
data_all['rank'] = data_all['rank'].astype('Int64')


#filter male tennis
data_male = data_all[data_all['league'] == 'atp']
#keep only name, year and rank
data_male_small = data_male[['name', 'year', 'rank']].copy()

#read the rest, no idea why this is called one hit wonders but people
#like Agassi were missing.
data_2_merge = pd.read_csv('die-anderen.csv')
data_2_merge ['year'] = pd.to_datetime(data_2_merge ['year'], format='%Y', errors='coerce') 
data_2_merge['rank'] = data_2_merge['rank'].astype('Int64')
#final contains the ones we got from the dataset + some important additions
data_male_final = pd.concat([data_male_small, data_2_merge]).reset_index(drop=True)

#general player contains image + general info
df_players = pd.read_csv('top20mannen.csv')




#male, keep only rankings in top-23

data_male_top3 = data_male_final[data_male_final['rank'] <= 3]



#male players who made it to the top of the monkeyrock and whose data
#we will use.

#list of players who reached the top3
players = sorted(list(data_male_top3['name'].unique()))

select_players = dcc.Dropdown(
    players,
    ['Pete Sampras','Andre Agassi', 'Roger Federer'],
    multi=True,
    id='select_players'
)

#filter data_final tot alleen de records van de mannen die de top 3 haalden
data_male_final = data_male_final[data_male_final['name'].isin(players)].sort_values(['name', 'year'])


# Initialize the Dash app








app = dash.Dash(__name__, suppress_callback_exceptions=True,external_stylesheets=[dbc.themes.CYBORG, dbc.icons.FONT_AWESOME, dbc_css])

# Define the app layout
app.layout = dbc.Container([
    dbc.Row([
        dbc.Col([
            html.H1('ATP end of year top-3 ranking, 1992 - 2024', style={"fontSize":"24px"})
            ], width=4),
        dbc.Col([select_players], width = 8)

        
        ],style={"margin":"0 1rem 2rem 1rem"}),
    dbc.Row([
        dbc.Col(dcc.Graph(id='top-ranking'), width=12),
        
        ], className = "setborder" ),
    dbc.Row([dbc.Col(html.Div(id="ranking-cards", style={"display":"flex", "flexWrap":"wrap"}), width=12),
        ]),
    dbc.Row([dbc.Col(html.P("Plotly Figure Friday, funproject, headshots are borrowed from the ATP tour website"), style={"textAlign":"center","fontSize":"12px","marginTop":"2rem"},width=12)])


],fluid=True, style={'padding': '20px'})



@app.callback(
    
    Output('top-ranking', 'figure'),
    Input('select_players', 'value')
)
def update_selected_players(selected_players):
    if not selected_players:
        selected_players = ["Andre Agassi"]
    
    
    #top3 scatterplot

    fig = go.Figure()


    for player in players:
        
        dff=data_male_final[(data_male_final['name'] == player) & (data_male_final['year'].dt.year >= 1992)]
        if player in selected_players:
            color_index = selected_players.index(player)

        fig.add_trace(go.Scatter(x=dff['year'], 
                            y=dff['rank'].apply(lambda x: x if x <= 3 else 4),
                            mode='lines+markers',
                            name=player,
                            line_shape='spline',
                            line_color= colors13[color_index] if player in selected_players else '#9d9d9d',
                            line=dict(dash='dot' if player not in selected_players else 'solid'),
                            marker=dict(
                               color=colors13[color_index] if player in selected_players else '#9d9d9d',
                               size=12 if player in selected_players else 6,
                           ),
                           showlegend=False,
                           connectgaps=False
                            )
                      )
    fig.update_layout(margin=dict(l=40, r=20, t=20, b=20),
                      height=400,
                      #plot_bgcolor='rgba(0, 0, 0, 0)',
                      paper_bgcolor='rgba(0, 0, 0, 0)'
                      )
    fig.update_yaxes(autorange='reversed', 
                     tickvals = [1,2,3,4],
                     ticktext = ["Rank 1","Rank 2","Rank 3", ""],
                     tickfont=dict(color='#e1e1e1', size=16)
                     )
    fig.update_xaxes(
                     tickfont=dict(color='#e1e1e1', size=16),
                     tickformat="%Y",
                     hoverformat="%Y" 
                     )
    fig.add_shape(type="rect",
    xref="x domain", yref="y domain",
    x0=0, y0=0, x1=1, y1=.25,
    line=dict(
        color="#050505",
        width=2,
    ),
    fillcolor="#050505",
    )
    
    
    text = html.Div([
        html.H4("Selected Players:"),
        html.Ul([
            html.Li(player) for player in selected_players
        ])
    ])
    
    return fig



#callback update visuals based on filters
@callback(

    Output('ranking-cards', 'children'),
    Input('select_players', 'value'))
def update_grid(selected_players):
    
    if not selected_players:
        selected_players = ["Andre Agassi"]
    
       
    #update card for player, general info from df_players
    #ranking info from data_male
    
    cardgrid = []
    
    for player in selected_players:
    
        player_data = df_players[df_players['Player'] == player]
        color_index = selected_players.index(player)

        
        ranking_card = dbc.Card(
        [
            dbc.Row(
                [
                    dbc.Col(
                        dbc.CardImg(
                            src=player_data['ImageURL'],
                            className="img-fluid rounded-start gaweg",
                        ),
                        className="col-md-4",
                    ),
                    dbc.Col(
                        dbc.CardBody(
                            [
                                html.H4(player_data['Player'], className="card-title"),
                                html.P(player_data['FunFact'],
                                    className="card-text",
                                ),
                               html.Small(player_data['Results']),
                            ]
                        ),
                        className="col-md-8",
                    ),
                ],
                className="g-0 d-flex align-items-center",
            )
        ],
        className="mb-3",
        style={"maxWidth": "540px", "borderColor": colors13[color_index],"borderWidth":"3px","margin":"1rem"},
        )
        
        cardgrid.append(ranking_card)    

    
    
    return cardgrid








# Run the app
if __name__ == '__main__':
    app.run(debug=False)