# -*- 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)