Skip to content

Commit 1f22955

Browse files
feat(altair): implement bar-race-animated (#7323)
## Implementation: `bar-race-animated` - python/altair Implements the **python/altair** version of `bar-race-animated`. **File:** `plots/bar-race-animated/implementations/python/altair.py` **Parent Issue:** #3653 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/26071184942)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent b82561d commit 1f22955

2 files changed

Lines changed: 239 additions & 152 deletions

File tree

plots/bar-race-animated/implementations/python/altair.py

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,37 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
bar-race-animated: Animated Bar Chart Race
3-
Library: altair 6.0.0 | Python 3.13.11
4-
Quality: 91/100 | Created: 2026-01-11
3+
Library: altair 6.1.0 | Python 3.13.13
4+
Quality: 89/100 | Updated: 2026-05-19
55
"""
66

7+
import os
8+
79
import altair as alt
810
import numpy as np
911
import pandas as pd
1012

1113

14+
# Theme tokens
15+
THEME = os.getenv("ANYPLOT_THEME", "light")
16+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
17+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
18+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
19+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
20+
21+
# Okabe-Ito palette (first series always #009E73) + accessible extensions for 10 entities
22+
COLORS = [
23+
"#009E73", # Okabe-Ito pos 1 — brand green, always first
24+
"#D55E00", # Okabe-Ito pos 2 — vermillion
25+
"#0072B2", # Okabe-Ito pos 3 — blue
26+
"#CC79A7", # Okabe-Ito pos 4 — reddish purple
27+
"#E69F00", # Okabe-Ito pos 5 — orange
28+
"#56B4E9", # Okabe-Ito pos 6 — sky blue
29+
"#F0E442", # Okabe-Ito pos 7 — yellow
30+
"#332288", # accessible extension — deep indigo
31+
"#117733", # accessible extension — deep green
32+
"#882255", # accessible extension — deep crimson
33+
]
34+
1235
# Data: Simulated streaming platform subscribers (millions) over time
1336
np.random.seed(42)
1437

@@ -26,8 +49,6 @@
2649
]
2750
years = list(range(2015, 2025))
2851

29-
# Generate evolving subscriber counts with different growth trajectories
30-
data = []
3152
base_values = {
3253
"StreamFlix": 40,
3354
"ViewMax": 35,
@@ -53,6 +74,7 @@
5374
"CloudTV": 1.06,
5475
}
5576

77+
data = []
5678
for platform in platforms:
5779
value = base_values[platform]
5880
for year in years:
@@ -65,25 +87,26 @@
6587
# Select key years for small multiples
6688
key_years = [2015, 2018, 2021, 2024]
6789
df_key = df[df["Year"].isin(key_years)].copy()
68-
69-
# Add rank for each year (used for sorting within each facet)
7090
df_key["Rank"] = df_key.groupby("Year")["Subscribers"].rank(ascending=True, method="first").astype(int)
7191

72-
# Color palette with Python Blue and Yellow first
73-
colors = ["#306998", "#FFD43B", "#4A90D9", "#2ECC71", "#E74C3C", "#9B59B6", "#F39C12", "#1ABC9C", "#34495E", "#E67E22"]
74-
75-
# Create single bar chart that will be faceted
92+
# Bar chart layer
7693
bar_chart = (
7794
alt.Chart(df_key)
7895
.mark_bar(cornerRadiusEnd=6, height=45)
7996
.encode(
8097
x=alt.X(
8198
"Subscribers:Q",
8299
title="Subscribers (millions)",
83-
axis=alt.Axis(labelFontSize=16, titleFontSize=20, grid=True, gridOpacity=0.3),
100+
axis=alt.Axis(labelFontSize=16, titleFontSize=20, grid=True, gridOpacity=0.25),
84101
),
85102
y=alt.Y("Rank:O", title=None, axis=None, sort="descending"),
86-
color=alt.Color("Platform:N", scale=alt.Scale(domain=platforms, range=colors), legend=None),
103+
color=alt.Color(
104+
"Platform:N",
105+
scale=alt.Scale(domain=platforms, range=COLORS),
106+
legend=alt.Legend(
107+
title="Platform", labelFontSize=16, titleFontSize=18, symbolSize=160, padding=14, cornerRadius=4
108+
),
109+
),
87110
tooltip=[
88111
alt.Tooltip("Platform:N", title="Platform"),
89112
alt.Tooltip("Subscribers:Q", format=".1f", title="Subscribers (M)"),
@@ -92,22 +115,22 @@
92115
)
93116
)
94117

95-
# Add text labels on bars
118+
# Platform name labels outside bars
96119
text_labels = (
97120
alt.Chart(df_key)
98121
.mark_text(align="left", dx=8, fontSize=14, fontWeight="bold")
99122
.encode(
100123
x=alt.X("Subscribers:Q"),
101124
y=alt.Y("Rank:O", sort="descending"),
102125
text=alt.Text("Platform:N"),
103-
color=alt.value("#333333"),
126+
color=alt.value(INK),
104127
)
105128
)
106129

107-
# Value labels at end of bars
130+
# Value labels inside bars
108131
value_labels = (
109132
alt.Chart(df_key)
110-
.mark_text(align="right", dx=-8, fontSize=13, fontWeight="normal")
133+
.mark_text(align="right", dx=-8, fontSize=16, fontWeight="normal")
111134
.encode(
112135
x=alt.X("Subscribers:Q"),
113136
y=alt.Y("Rank:O", sort="descending"),
@@ -116,29 +139,41 @@
116139
)
117140
)
118141

119-
# Combine layers - target 4800x2700 at scale 3x = 1600x900 base
142+
# Combine layers and facet by year
120143
combined = (bar_chart + text_labels + value_labels).properties(width=340, height=750)
121144

122-
# Facet by year
123145
chart = (
124146
combined.facet(
125-
column=alt.Column("Year:O", header=alt.Header(labelFontSize=26, title=None, labelPadding=15)), spacing=20
147+
column=alt.Column("Year:O", header=alt.Header(labelFontSize=26, title=None, labelPadding=15, labelColor=INK)),
148+
spacing=20,
126149
)
127-
.configure_view(strokeWidth=0)
128-
.configure_axis(labelFontSize=18, titleFontSize=22)
129150
.properties(
130151
title=alt.Title(
131-
"bar-race-animated · altair · pyplots.ai",
152+
"bar-race-animated · python · altair · anyplot.ai",
132153
fontSize=34,
133154
anchor="middle",
134155
subtitle="Streaming Platform Subscribers Over Time (millions)",
135156
subtitleFontSize=22,
136157
dy=-10,
137-
)
158+
),
159+
background=PAGE_BG,
160+
)
161+
.configure_view(strokeWidth=0, fill=PAGE_BG)
162+
.configure_axis(
163+
labelFontSize=18,
164+
titleFontSize=22,
165+
domainColor=INK_SOFT,
166+
tickColor=INK_SOFT,
167+
gridColor=INK_SOFT,
168+
gridOpacity=0.20,
169+
labelColor=INK_SOFT,
170+
titleColor=INK,
138171
)
172+
.configure_title(color=INK, subtitleColor=INK_SOFT)
173+
.configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)
139174
.resolve_scale(x="independent")
140175
)
141176

142-
# Save outputs
143-
chart.save("plot.png", scale_factor=3.0)
144-
chart.save("plot.html")
177+
# Save
178+
chart.save(f"plot-{THEME}.png", scale_factor=3.0)
179+
chart.save(f"plot-{THEME}.html")

0 commit comments

Comments
 (0)