from dash import Dash, dcc, html, Input, Output
import dash
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
# --- Data loading and cleaning ---
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2025/week-23/steak-risk-survey.csv"
)
# Clean and prepare data
df = df.dropna(
subset=[
"How do you like your steak prepared?",
"Do you ever smoke cigarettes?",
"Do you ever drink alcohol?",
"Gender",
"Age",
"Location (Census Region)",
"Household Income",
"Education",
]
)
df = df.drop_duplicates()
# Rename columns for clarity
df = df.rename(
columns={
"Do you ever smoke cigarettes?": "Smokes",
"Do you ever drink alcohol?": "Drinks",
"How do you like your steak prepared?": "Steak_Preference",
}
)
# Clean string values
for col in [
"Smokes",
"Drinks",
"Gender",
"Age",
"Steak_Preference",
"Location (Census Region)",
"Household Income",
"Education",
]:
df[col] = df[col].astype(str).str.strip().str.title()
# Theme colors
BG_COLOR = "#000000" # Black background
CARD_BG = "#1a1a1a" # Dark gray card background
TEXT_COLOR = "#ffffff"
ACCENT_COLOR = "#ff8c00" # Dark orange accent
KPI_BG = "#262626" # Darker gray for KPIs
SIDEBAR_BG = "#1a1a1a" # Dark gray sidebar
app = Dash(
__name__,
external_stylesheets=[
dbc.themes.DARKLY,
"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css",
],
meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}],
)
def create_kpi_card(title, value, icon):
return dbc.Card(
dbc.CardBody(
[
html.Div(
[
html.I(
className=f"bi bi-{icon} me-2",
style={"fontSize": "2rem", "color": ACCENT_COLOR},
),
html.Span(
title, className="fw-bold", style={"color": ACCENT_COLOR}
),
],
className="mb-1 d-flex align-items-center justify-content-center",
),
html.H3(
value,
className="fw-bold text-center",
style={"color": ACCENT_COLOR},
),
]
),
className="shadow rounded-4 border-0",
style={"background": KPI_BG, "minHeight": "120px"},
)
# --- Layout ---
app.layout = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
html.H1(
"Steak Preferences Analysis",
className="text-center text-white my-4",
style={"fontWeight": "bold"},
)
]
)
]
),
dbc.Row(
[
# Sidebar
dbc.Col(
[
dbc.Card(
dbc.CardBody(
[
html.Label(
"Select Age Group:", className="text-white"
),
dbc.Select(
id="age-filter",
options=[
{"label": "All Ages", "value": "All"}
]
+ [
{"label": age, "value": age}
for age in sorted(df["Age"].unique())
],
value="All",
className="mb-3 bg-dark text-white w-100",
),
html.Br(),
html.Label(
"Select Region:", className="text-white"
),
dbc.Select(
id="region-filter",
options=[
{"label": "All Regions", "value": "All"}
]
+ [
{
"label": region,
"value": region,
}
for region in sorted(
df["Location (Census Region)"].unique()
)
],
value="All",
className="mb-3 bg-dark text-white w-100",
),
html.Br(),
html.Label(
"Select Education:", className="text-white"
),
dbc.Select(
id="education-filter",
options=[
{
"label": "All Education Levels",
"value": "All",
}
]
+ [
{"label": edu, "value": edu}
for edu in sorted(df["Education"].unique())
],
value="All",
className="mb-3 bg-dark text-white w-100",
),
]
),
className="mb-4",
style={
"backgroundColor": SIDEBAR_BG,
"borderRadius": "15px",
"boxShadow": "0 4px 6px rgba(0, 0, 0, 0.1)",
"height": "100vh",
"position": "sticky",
"top": "20px",
},
)
],
width=2,
),
# Main content
dbc.Col(
[
# KPI Cards Row
dbc.Row(
[
dbc.Col(id="kpi-total", md=4),
dbc.Col(id="kpi-yes", md=4),
dbc.Col(id="kpi-no", md=4),
],
className="mb-4 justify-content-center",
),
dbc.Row(
[
dbc.Col(
[
dbc.Card(
dbc.CardBody(
[
dcc.Graph(
id="steak-preference-chart"
)
]
),
style={"backgroundColor": CARD_BG},
)
],
width=6,
),
dbc.Col(
[
dbc.Card(
dbc.CardBody(
[dcc.Graph(id="lifestyle-chart")]
),
style={"backgroundColor": CARD_BG},
)
],
width=6,
),
],
className="mb-4",
),
dbc.Row(
[
dbc.Col(
[
dbc.Card(
dbc.CardBody(
[dcc.Graph(id="bar-dim-chart")]
),
style={"backgroundColor": CARD_BG},
)
]
)
]
),
],
width=10,
),
]
),
],
fluid=True,
style={"backgroundColor": BG_COLOR, "minHeight": "100vh"},
)
# --- Callback ---
@app.callback(
[
Output("steak-preference-chart", "figure"),
Output("lifestyle-chart", "figure"),
Output("bar-dim-chart", "figure"),
Output("kpi-total", "children"),
Output("kpi-yes", "children"),
Output("kpi-no", "children"),
],
[
Input("age-filter", "value"),
Input("region-filter", "value"),
Input("education-filter", "value"),
],
)
def update_charts(selected_age, selected_region, selected_education):
# Filter data
filtered_df = df.copy()
if selected_age != "All":
filtered_df = filtered_df[filtered_df["Age"] == selected_age]
if selected_region != "All":
filtered_df = filtered_df[
filtered_df["Location (Census Region)"] == selected_region
]
if selected_education != "All":
filtered_df = filtered_df[filtered_df["Education"] == selected_education]
# Calculate KPIs
total_responses = len(filtered_df)
yes_smokers = (filtered_df["Smokes"] == "Yes").sum()
no_smokers = (filtered_df["Smokes"] == "No").sum()
kpi1 = create_kpi_card("Total Responses", f"{total_responses:,}", "people-fill")
kpi2 = create_kpi_card(
"Smokers (%)",
f"{(yes_smokers/total_responses*100) if total_responses else 0:.1f}%",
"check-circle",
)
kpi3 = create_kpi_card(
"Non-Smokers (%)",
f"{(no_smokers/total_responses*100) if total_responses else 0:.1f}%",
"x-circle",
)
# Steak Preference Chart
steak_counts = filtered_df["Steak_Preference"].value_counts()
fig1 = go.Figure(
data=[
go.Pie(
labels=steak_counts.index,
values=steak_counts.values,
hole=0.4,
marker_colors=px.colors.sequential.Oranges,
)
]
)
fig1.update_layout(
title="Steak Preparation Preferences",
paper_bgcolor=CARD_BG,
plot_bgcolor=CARD_BG,
font_color=TEXT_COLOR,
showlegend=True,
legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="center",
x=0.5,
),
)
# Lifestyle Chart
if (
filtered_df["Smokes"].nunique() > 0
and filtered_df["Drinks"].nunique() > 0
):
lifestyle_data = (
pd.crosstab(
filtered_df["Smokes"],
filtered_df["Drinks"],
normalize="index",
)
* 100
)
z = lifestyle_data.values
x = lifestyle_data.columns
y = lifestyle_data.index
text = lifestyle_data.values.round(1)
else:
z = [[0]]
x = ["No Data"]
y = ["No Data"]
text = [[0]]
fig2 = go.Figure(
data=[
go.Heatmap(
z=z,
x=x,
y=y,
colorscale="Oranges",
text=text,
texttemplate="%{text}%",
textfont={"size": 14},
)
]
)
fig2.update_layout(
title="Smoking vs Drinking Habits",
paper_bgcolor=CARD_BG,
plot_bgcolor=CARD_BG,
font_color=TEXT_COLOR,
xaxis_title="Drinks",
yaxis_title="Smokes",
)
# Region by Age Grouped Bar Chart
if "Location (Census Region)" in filtered_df.columns and "Age" in filtered_df.columns:
region_age_counts = (
filtered_df.groupby(["Location (Census Region)", "Age"])
.size()
.reset_index(name="Count")
)
# Sorrend az életkorokhoz
age_order = sorted(df["Age"].unique())
region_age_counts["Age"] = pd.Categorical(
region_age_counts["Age"], categories=age_order, ordered=True
)
fig3 = px.bar(
region_age_counts,
x="Location (Census Region)",
y="Count",
color="Age",
barmode="group",
color_discrete_sequence=px.colors.sequential.Oranges,
category_orders={"Age": age_order}
)
fig3.update_layout(
title="Responses by Region and Age Group",
paper_bgcolor=CARD_BG,
plot_bgcolor=CARD_BG,
font_color=TEXT_COLOR,
xaxis_title="Region",
yaxis_title="Count",
xaxis_tickangle=-45,
)
else:
fig3 = go.Figure()
fig3.update_layout(
title="No data for region-age chart",
paper_bgcolor=CARD_BG,
plot_bgcolor=CARD_BG,
font_color=TEXT_COLOR,
)
return fig1, fig2, fig3, kpi1, kpi2, kpi3
if __name__ == "__main__":
app.run(debug=True)