Spaces:
Sleeping
Sleeping
File size: 4,852 Bytes
ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 68167e5 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 ec6e9a7 3e12d11 746aaaa 3e12d11 ec6e9a7 | 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 | """Plotly figure helpers used by the Shiny app.
The app passes a filtered pipeline DataFrame into these helpers and receives
back a fully configured Plotly figure (including placeholders for empty
selections).
"""
from __future__ import annotations
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from .config import AGE_ORDER
def _placeholder(msg: str) -> go.Figure:
"""Return a simple placeholder figure (used when there is nothing to plot)."""
fig = make_subplots(rows=1, cols=1)
fig.add_annotation(
text=msg,
xref="paper",
yref="paper",
x=0.5,
y=0.5,
showarrow=False,
font=dict(size=16),
)
fig.update_xaxes(visible=False)
fig.update_yaxes(visible=False)
fig.update_layout(height=700, width=1200, margin=dict(t=80), showlegend=False)
return fig
def employment_multi_plot(
df: pd.DataFrame,
*,
level: str | int | None = None,
empty_message: str = "Select an occupation to load the charts.",
no_age_message: str = "No age data available for this selection.",
) -> go.Figure:
"""Build the multi-panel employment chart.
Expects `df` to contain (at minimum) the columns: `year`, `age`, `label`,
and `employment`.
"""
if df is None or df.empty:
return _placeholder(empty_message)
# Order ages by AGE_ORDER, append any extra groups alphabetically.
unique_ages = [a for a in df["age"].dropna().unique() if a]
age_set = set(unique_ages)
age_groups = [age for age in AGE_ORDER if age in age_set]
age_groups.extend(sorted(age_set - set(AGE_ORDER)))
if not age_groups:
return _placeholder(no_age_message)
n_rows = len(age_groups)
ssyk_level = (
f"(🇸🇪 SSYK 2012, Level {level})" if level is not None else "(SSYK 2012)"
)
subplot_titles = [
(
f"<b>Employed Persons Aged {age} Years by Occupation</b><br>"
f"<span style='font-size:13px; color:#6b7280;'display:inline-block;'>"
f"{ssyk_level}"
f"</span>"
)
for age in age_groups
]
occupations = sorted(df["label"].dropna().unique())
palette = px.colors.qualitative.Plotly
occ_color_map = {
occ: palette[i % len(palette)] for i, occ in enumerate(occupations)
}
fig = make_subplots(
rows=n_rows,
cols=1,
shared_xaxes=False,
vertical_spacing=0.03,
subplot_titles=subplot_titles,
)
for i, age in enumerate(age_groups, start=1):
df_age = df[df["age"] == age]
df_plot = df_age.groupby(["year", "label"], as_index=False)["employment"].sum()
for occ_title, sub in df_plot.groupby("label"):
fig.add_trace(
go.Scatter(
x=sub["year"],
y=sub["employment"],
mode="lines+markers",
showlegend=(i == 1),
name=occ_title,
line=dict(color=occ_color_map[occ_title], width=3),
hovertemplate=f"Age: {age}<br>Year: %{{x}}<br>Count: %{{y:,}}<extra>{occ_title}</extra>",
),
row=i,
col=1,
)
fig.update_yaxes(
title_text="Number of Employed Persons",
tickformat=",",
rangemode="tozero",
row=i,
col=1,
)
fig.update_xaxes(
title_text="Year",
tickmode="linear",
dtick=1,
row=i,
col=1,
)
BASE_PLOT_WIDTH = 1200
LEFT_LEGEND_MARGIN = 260
fig.update_annotations(yshift=36)
fig.update_layout(
height=700 * n_rows,
width=BASE_PLOT_WIDTH + LEFT_LEGEND_MARGIN,
legend_traceorder="normal",
legend=dict(
title="<b>Occupation Title(s)</b><br>",
orientation="v",
x=-0.1, # left edge of plotting area
xanchor="right", # legend sits just outside-left
y=0.98,
yanchor="top",
itemsizing="constant",
itemwidth=35, # keeps items compact
tracegroupgap=6,
bordercolor="rgba(0,0,0,0.15)",
borderwidth=1,
bgcolor="rgba(255,255,255,0.85)",
font=dict(size=12),
indentation=10,
yref="paper",
),
margin=dict(
t=170,
l=LEFT_LEGEND_MARGIN,
r=60,
b=60,
),
xaxis_showgrid=True,
yaxis_showgrid=True,
template="plotly_white",
)
return fig
def multi_plot(df: pd.DataFrame) -> go.Figure:
"""Backwards-compatible alias for `employment_multi_plot`."""
return employment_multi_plot(df)
|