Skip to content

Commit 70c6f61

Browse files
feat(matplotlib): implement bar-race-animated (#7319)
## Implementation: `bar-race-animated` - python/matplotlib Implements the **python/matplotlib** version of `bar-race-animated`. **File:** `plots/bar-race-animated/implementations/python/matplotlib.py` **Parent Issue:** #3653 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/26071101066)* --------- 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 3c04057 commit 70c6f61

2 files changed

Lines changed: 403 additions & 0 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
""" anyplot.ai
2+
bar-race-animated: Animated Bar Chart Race
3+
Library: matplotlib 3.10.9 | Python 3.13.13
4+
Quality: 85/100 | Created: 2026-05-19
5+
"""
6+
7+
import os
8+
9+
import matplotlib.patheffects as pe
10+
import matplotlib.pyplot as plt
11+
from matplotlib.gridspec import GridSpec
12+
13+
14+
THEME = os.getenv("ANYPLOT_THEME", "light")
15+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
16+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
17+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
18+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
19+
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
20+
21+
OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"]
22+
23+
# Data: Tech Company Market Cap ($B) — year-end snapshots (2019–2024)
24+
companies = ["Alphabet", "Amazon", "Apple", "Meta", "Microsoft", "Nvidia", "Tesla"]
25+
company_colors = {c: OKABE_ITO[i] for i, c in enumerate(companies)}
26+
27+
market_cap = {
28+
"Alphabet": [920, 1200, 1960, 1140, 1800, 2150],
29+
"Amazon": [940, 1640, 1730, 855, 1570, 2170],
30+
"Apple": [1050, 2250, 2950, 2070, 2990, 3750],
31+
"Meta": [580, 780, 890, 335, 940, 1440],
32+
"Microsoft": [1200, 1680, 2530, 1790, 2800, 3130],
33+
"Nvidia": [145, 325, 745, 365, 1220, 3280],
34+
"Tesla": [75, 670, 1060, 388, 790, 695],
35+
}
36+
years = [2019, 2020, 2021, 2022, 2023, 2024]
37+
38+
max_val = max(v for vlist in market_cap.values() for v in vlist)
39+
40+
# GridSpec gives precise control over subplot spacing
41+
fig = plt.figure(figsize=(20, 11), facecolor=PAGE_BG)
42+
gs = GridSpec(2, 3, figure=fig)
43+
axes = [fig.add_subplot(gs[i // 3, i % 3]) for i in range(6)]
44+
45+
for idx, (year, ax) in enumerate(zip(years, axes, strict=False)):
46+
ax.set_facecolor(PAGE_BG)
47+
48+
# Sort ascending so highest-value company tops the chart
49+
snapshot = {c: market_cap[c][idx] for c in companies}
50+
sorted_items = sorted(snapshot.items(), key=lambda x: x[1])
51+
names = [item[0] for item in sorted_items]
52+
values = [item[1] for item in sorted_items]
53+
bar_colors = [company_colors[n] for n in names]
54+
55+
bars = ax.barh(names, values, color=bar_colors, height=0.72, edgecolor=PAGE_BG, linewidth=0.8)
56+
57+
# Nvidia tracked across all panels with a distinctive colored border
58+
for name, bar in zip(names, bars, strict=False):
59+
if name == "Nvidia":
60+
bar.set_edgecolor(company_colors["Nvidia"])
61+
bar.set_linewidth(2.0)
62+
63+
# Value labels — Nvidia's label rendered in brand color with stroke for legibility
64+
offset = max_val * 0.015
65+
for i, (name, val) in enumerate(zip(names, values, strict=False)):
66+
is_nvidia = name == "Nvidia"
67+
txt = ax.text(
68+
val + offset,
69+
i,
70+
f"${val:,}B",
71+
va="center",
72+
ha="left",
73+
fontsize=13,
74+
color=company_colors["Nvidia"] if is_nvidia else INK_SOFT,
75+
fontweight="bold" if is_nvidia else "normal",
76+
)
77+
if is_nvidia:
78+
txt.set_path_effects([pe.withStroke(linewidth=2, foreground=PAGE_BG), pe.Normal()])
79+
80+
ax.set_title(str(year), fontsize=20, fontweight="bold", color=INK, pad=8)
81+
ax.set_xlim(0, max_val * 1.38)
82+
ax.set_xlabel("Market Cap ($B)", fontsize=14, color=INK_MUTED, labelpad=4)
83+
ax.set_xticks([])
84+
ax.tick_params(axis="y", labelsize=16, colors=INK_SOFT, length=0)
85+
for spine in ("top", "right", "bottom"):
86+
ax.spines[spine].set_visible(False)
87+
ax.spines["left"].set_color(INK_SOFT)
88+
89+
# Final panel: annotate Nvidia's historic rise + inset sparkline
90+
if idx == 5 and "Nvidia" in names:
91+
nv_pos = names.index("Nvidia")
92+
nv_val = values[nv_pos]
93+
nv_color = company_colors["Nvidia"]
94+
growth = nv_val / market_cap["Nvidia"][0]
95+
96+
# Arrow annotation pointing to Nvidia's bar from safe empty space between rows
97+
ax.annotate(
98+
f" ×{growth:.0f} since 2019 ",
99+
xy=(nv_val, nv_pos),
100+
xytext=(max_val * 0.72, 2.5),
101+
fontsize=14,
102+
fontweight="bold",
103+
color=nv_color,
104+
arrowprops={"arrowstyle": "-|>", "color": nv_color, "lw": 2.0, "connectionstyle": "arc3,rad=-0.25"},
105+
bbox={
106+
"boxstyle": "round,pad=0.35",
107+
"facecolor": ELEVATED_BG,
108+
"edgecolor": nv_color,
109+
"linewidth": 1.5,
110+
"alpha": 0.92,
111+
},
112+
)
113+
114+
# Inset sparkline in the empty lower-right area: Nvidia 2019→2024 trajectory
115+
ax_spark = ax.inset_axes([0.35, 0.04, 0.60, 0.20])
116+
ax_spark.set_facecolor(ELEVATED_BG)
117+
nv_vals = [market_cap["Nvidia"][y_idx] for y_idx in range(len(years))]
118+
ax_spark.plot(years, nv_vals, color=nv_color, lw=2.0, marker="o", markersize=4.5, solid_capstyle="round")
119+
ax_spark.fill_between(years, nv_vals, alpha=0.15, color=nv_color)
120+
ax_spark.set_xticks(years)
121+
ax_spark.set_xticklabels([str(y) for y in years], fontsize=9, color=INK_MUTED, rotation=30, ha="right")
122+
ax_spark.set_yticks([])
123+
ax_spark.tick_params(length=0)
124+
for sp in ax_spark.spines.values():
125+
sp.set_color(INK_SOFT)
126+
sp.set_linewidth(0.5)
127+
ax_spark.text(
128+
0.5,
129+
1.08,
130+
"Nvidia $145B → $3,280B",
131+
transform=ax_spark.transAxes,
132+
fontsize=9,
133+
ha="center",
134+
color=INK_MUTED,
135+
fontweight="bold",
136+
)
137+
138+
fig.suptitle(
139+
"Tech Giant Market Cap · bar-race-animated · python · matplotlib · anyplot.ai",
140+
fontsize=24,
141+
fontweight="medium",
142+
color=INK,
143+
)
144+
plt.tight_layout(rect=[0, 0, 1, 0.96])
145+
plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG)
146+
plt.close()

0 commit comments

Comments
 (0)