from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
import dash_bootstrap_components as dbc
# Load the data
df = pd.read_csv('https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2025/week-11/us-hurricanes.csv')
# Convert the row value of `TS` under the `category` column to a NaN
df['category'] = df['category'].replace('TS', pd.NA)
# Create year ranges for temporal analysis
df['decade'] = (df['year'] // 10) * 10
df['decade'] = df['decade'].astype(str) + 's'
# Create a more detailed dataframe for the bar chart
yearly_data = df.groupby('year').agg({
'max-wind-(kt)': 'mean',
'central-pressure-(mb)': 'mean',
'name': lambda x: ', '.join(set(x.dropna())) # Get unique names
}).reset_index()
# Count hurricanes per year
yearly_count = df.groupby('year').size().reset_index(name='count')
# Merge the data
yearly_data = pd.merge(yearly_data, yearly_count, on='year')
# Create a bar chart with enhanced tooltip
year_count_fig = px.bar(
yearly_data,
x='year',
y='count',
title='',
height=500,
hover_data={
'year': True,
'count': True,
'max-wind-(kt)': ':.1f',
'central-pressure-(mb)': ':.1f',
'name': True
},
labels={
'year': 'Year',
'count': 'Number of Hurricanes',
'max-wind-(kt)': 'Avg. Wind Speed (kt)',
'central-pressure-(mb)': 'Avg. Central Pressure (mb)',
'name': 'Hurricane Names'
}
)
# Set the background color to dark grey for the bar chart
year_count_fig.update_layout(
plot_bgcolor='#2E2E2E', # Dark grey background
xaxis_title='',
yaxis_title='Number of Hurricanes',
hovermode='closest',
hoverlabel=dict(
bgcolor='black',
font_size=12
),
xaxis=dict(showgrid=False), # Remove gridlines on x-axis
yaxis=dict(showgrid=False), # Remove gridlines on y-axis
autosize=True, # Fontos: Hozzáadtuk az autosize paramétert
margin=dict(l=20, r=20, t=20, b=20) # Csökkentett margók
)
year_count_fig.update_traces(marker=dict(color='rgb(0, 115, 230)'))
# Create a heatmap of hurricanes by decade and category
heatmap_data = df.dropna(subset=['category']).groupby(['decade', 'category']).size().reset_index(name='count')
# Initial heatmap figure with color scale
initial_color_scale = px.colors.sequential.Bluered
heatmap_fig = px.density_heatmap(
heatmap_data,
x='decade',
y='category',
z='count',
color_continuous_scale=initial_color_scale,
title='',
height=400
)
# Set background color to dark grey for the heatmap
heatmap_fig.update_layout(
plot_bgcolor='#2E2E2E', # Dark grey background
xaxis_title='',
yaxis_title='Hurricane Category',
coloraxis_colorbar_title='Count of Categories',
xaxis=dict(showgrid=False), # Remove gridlines on x-axis
yaxis=dict(showgrid=False), # Remove gridlines on y-axis
autosize=True, # Autosize engedélyezése
margin=dict(l=20, r=20, t=20, b=20) # Csökkentett margók
)
# Create a dashboard with the 2 visualizations using Bootstrap
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
# Teljes képernyő (viewport) szélességű konténert használunk
app.layout = dbc.Container([
dbc.Row([
dbc.Col([
html.H1(
'US Hurricanes Analysis',
className='text-center mt-4 mb-2',
style={
'color': 'black',
'font-family': 'Arial, sans-serif',
'font-weight': 'bold',
'font-size': '40px',
'letter-spacing': '1px',
}
),
html.Div(
'1851 - 2023',
className='text-center mb-4',
style={
'fontSize': '20px',
'color': 'black'
}
),
], width=12)
]),
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader([
html.H3('Number of Hurricanes by Year', className='d-inline me-3'),
html.Span(
'This chart shows the annual frequency of hurricanes with detailed information available on hover.',
className='text-muted fst-italic'
)
]),
dbc.CardBody([
# Fontos: style tulajdonságot adtunk hozzá a graph-hoz
dcc.Graph(
id='bar-chart',
figure=year_count_fig,
style={'width': '100%', 'height': '500px'}, # Explicit szélesség és magasság
config={'responsive': True} # Responsive beállítás
)
], style={'padding': '0'}) # Csökkentjük a CardBody padding-jét
], className='mb-4', style={'width': '100%'}) # Explicit szélesség a card-nak
], width=12, style={'padding': '0 5px'}) # Minimális padding az oszlopnak
], className='g-0'), # Kikapcsoljuk a grid gutter-eket
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader([
html.H3('Hurricane Frequency by Decade and Category', className='d-inline me-3'),
html.Span(
'This heatmap visualizes the distribution of hurricane categories across different decades.',
className='text-muted fst-italic'
)
]),
dbc.CardBody([
html.Label('Select Color Scale:'),
dcc.Slider(
id='color-scale-slider',
min=0,
max=10,
marks={i: {'label': scale} for i, scale in enumerate(px.colors.named_colorscales()[:11])},
value=0,
step=1,
tooltip={"placement": "bottom", "always_visible": True},
updatemode='drag',
className='mb-4'
),
# Fontos: style tulajdonságot adtunk hozzá a graph-hoz
dcc.Graph(
id='heatmap-graph',
figure=heatmap_fig,
style={'width': '100%', 'height': '400px'}, # Explicit szélesség és magasság
config={'responsive': True} # Responsive beállítás
)
], style={'padding': '10px 0'}) # Csökkentjük a CardBody padding-jét
], style={'width': '100%'}) # Explicit szélesség a card-nak
], width=12, style={'padding': '0 5px'}) # Minimális padding az oszlopnak
], className='g-0') # Kikapcsoljuk a grid gutter-eket
], fluid=True, style={'backgroundColor': '#add8e6', 'padding': '20px', 'maxWidth': '100%'}) # Explicit maxWidth
# Callback to update both the heatmap and bar chart color scale
@app.callback(
[Output('heatmap-graph', 'figure'),
Output('bar-chart', 'figure')],
[Input('color-scale-slider', 'value')]
)
def update_color_scale(slider_value):
selected_scale = px.colors.named_colorscales()[slider_value]
# Update heatmap
updated_heatmap = px.density_heatmap(
heatmap_data,
x='decade',
y='category',
z='count',
color_continuous_scale=selected_scale,
title='',
height=400
)
updated_heatmap.update_layout(
plot_bgcolor='#2E2E2E',
xaxis_title='',
yaxis_title='Hurricane Category',
coloraxis_colorbar_title='Count of Category',
xaxis=dict(showgrid=False),
yaxis=dict(showgrid=False),
autosize=True, # Autosize engedélyezése
margin=dict(l=20, r=20, t=20, b=20) # Csökkentett margók
)
# Update bar chart with the new selected color scale
updated_bar_chart = px.bar(
yearly_data,
x='year',
y='count',
title='',
height=500,
hover_data={
'year': True,
'count': True,
'max-wind-(kt)': ':.1f',
'central-pressure-(mb)': ':.1f',
'name': True
},
labels={
'year': 'Year',
'count': 'Number of Hurricanes',
'max-wind-(kt)': 'Avg. Wind Speed (kt)',
'central-pressure-(mb)': 'Avg. Central Pressure (mb)',
'name': 'Hurricane Names'
},
color=yearly_data['count'],
color_continuous_scale=selected_scale
)
updated_bar_chart.update_layout(
plot_bgcolor='#2E2E2E',
xaxis_title='',
yaxis_title='Number of Hurricanes',
hovermode='closest',
hoverlabel=dict(
bgcolor='black',
font_size=12
),
xaxis=dict(showgrid=False),
yaxis=dict(showgrid=False),
autosize=True, # Autosize engedélyezése
margin=dict(l=20, r=20, t=20, b=20) # Csökkentett margók
)
return updated_heatmap, updated_bar_chart
if __name__ == '__main__':
app.run(debug=False)