Skip to content

Commit 042dba5

Browse files
update(bump-basic): matplotlib — comprehensive quality review (#4331)
## Summary Updated **matplotlib** implementation for **bump-basic**. **Changes:** Comprehensive quality review ### Changes - Switched to F1 driver standings data (7 drivers, 8 races) with dramatic overtakes - Added matplotlib.patheffects for text stroke on end labels - Improved colorblind-safe palette - Increased marker sizes and line widths for 4800px canvas - Updated spec: added timeseries data_type, comparison feature, Example line - Quality self-assessment: 93/100 ## Test Plan - [x] Preview images uploaded to GCS staging - [x] Implementation file passes ruff format/check - [x] Metadata YAML updated with current versions - [ ] Automated review triggered --- Generated with [Claude Code](https://claude.com/claude-code) `/update` command --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent bab5386 commit 042dba5

File tree

2 files changed

+248
-136
lines changed

2 files changed

+248
-136
lines changed
Lines changed: 97 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,124 @@
11
""" pyplots.ai
22
bump-basic: Basic Bump Chart
3-
Library: matplotlib 3.10.8 | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-23
3+
Library: matplotlib 3.10.8 | Python 3.14.3
4+
Quality: 90/100 | Updated: 2026-02-22
55
"""
66

7+
import matplotlib.patheffects as pe
78
import matplotlib.pyplot as plt
89
import numpy as np
910

1011

11-
# Data - Sports league standings over a season
12-
entities = ["Team Alpha", "Team Beta", "Team Gamma", "Team Delta", "Team Epsilon"]
13-
periods = ["Week 1", "Week 2", "Week 3", "Week 4", "Week 5", "Week 6"]
12+
# Data - Formula 1 driver standings over an 8-race season
13+
drivers = ["Verstappen", "Hamilton", "Norris", "Leclerc", "Sainz", "Piastri", "Russell"]
14+
races = ["Bahrain", "Jeddah", "Melbourne", "Suzuka", "Shanghai", "Miami", "Imola", "Monaco"]
1415

15-
# Rankings for each team across periods (1 = best)
16+
# Rankings per driver across races (1 = championship leader)
1617
rankings = {
17-
"Team Alpha": [3, 2, 1, 1, 2, 1],
18-
"Team Beta": [1, 1, 2, 3, 3, 2],
19-
"Team Gamma": [2, 3, 3, 2, 1, 3],
20-
"Team Delta": [4, 4, 5, 4, 4, 4],
21-
"Team Epsilon": [5, 5, 4, 5, 5, 5],
18+
"Verstappen": [1, 1, 1, 2, 3, 3, 2, 1],
19+
"Hamilton": [4, 3, 2, 1, 1, 2, 1, 2],
20+
"Norris": [5, 5, 4, 3, 2, 1, 3, 3],
21+
"Leclerc": [2, 2, 3, 4, 5, 5, 4, 4],
22+
"Sainz": [3, 4, 5, 5, 4, 4, 5, 5],
23+
"Piastri": [6, 6, 7, 7, 6, 6, 6, 7],
24+
"Russell": [7, 7, 6, 6, 7, 7, 7, 6],
2225
}
2326

24-
# Colors - Python Blue first, then colorblind-safe palette
25-
colors = ["#306998", "#FFD43B", "#2ecc71", "#e74c3c", "#9b59b6"]
27+
# Colorblind-safe palette — distinct hues, no similar oranges
28+
colors = {
29+
"Verstappen": "#306998",
30+
"Hamilton": "#9467bd",
31+
"Norris": "#17becf",
32+
"Leclerc": "#d62728",
33+
"Sainz": "#e8963e",
34+
"Piastri": "#8c564b",
35+
"Russell": "#7f7f7f",
36+
}
37+
38+
# Top-3 finishers get visual emphasis for storytelling hierarchy
39+
top_drivers = {"Verstappen", "Hamilton", "Norris"}
2640

27-
# Create plot (4800x2700 px)
41+
# Plot
2842
fig, ax = plt.subplots(figsize=(16, 9))
43+
x = np.arange(len(races))
44+
45+
for driver, ranks in rankings.items():
46+
is_top = driver in top_drivers
47+
lw = 4.0 if is_top else 2.5
48+
ms = 16 if is_top else 10
49+
zo = 4 if is_top else 3
50+
alpha = 1.0 if is_top else 0.55
2951

30-
x = np.arange(len(periods))
52+
ax.plot(
53+
x,
54+
ranks,
55+
marker="o",
56+
markersize=ms,
57+
linewidth=lw,
58+
color=colors[driver],
59+
zorder=zo,
60+
alpha=alpha,
61+
path_effects=[pe.Stroke(linewidth=lw + 2, foreground="white"), pe.Normal()],
62+
)
63+
# End-of-line labels (replaces legend, more direct)
64+
ax.text(
65+
x[-1] + 0.15,
66+
ranks[-1],
67+
driver,
68+
fontsize=16,
69+
fontweight="bold",
70+
color=colors[driver],
71+
va="center",
72+
alpha=1.0 if is_top else 0.8,
73+
path_effects=[pe.withStroke(linewidth=3, foreground="white")],
74+
)
3175

32-
for i, (entity, ranks) in enumerate(rankings.items()):
33-
ax.plot(x, ranks, marker="o", markersize=15, linewidth=3, color=colors[i], label=entity)
76+
# Annotate key lead changes for data storytelling
77+
ax.annotate(
78+
"Hamilton\ntakes the lead",
79+
xy=(3, 1),
80+
xytext=(1.5, -0.6),
81+
fontsize=12,
82+
fontweight="bold",
83+
color=colors["Hamilton"],
84+
ha="center",
85+
va="bottom",
86+
path_effects=[pe.withStroke(linewidth=2, foreground="white")],
87+
arrowprops={"arrowstyle": "->", "color": colors["Hamilton"], "lw": 1.5, "connectionstyle": "arc3,rad=-0.15"},
88+
)
89+
90+
ax.annotate(
91+
"Norris\npeaks at P1",
92+
xy=(5, 1),
93+
xytext=(5.8, -0.6),
94+
fontsize=12,
95+
fontweight="bold",
96+
color=colors["Norris"],
97+
ha="center",
98+
va="bottom",
99+
path_effects=[pe.withStroke(linewidth=2, foreground="white")],
100+
arrowprops={"arrowstyle": "->", "color": colors["Norris"], "lw": 1.5, "connectionstyle": "arc3,rad=0.15"},
101+
)
34102

35103
# Invert Y-axis so rank 1 is at top
104+
ax.set_ylim(-1.2, len(drivers) + 0.5)
36105
ax.invert_yaxis()
37106

38-
# Labels and styling
39-
ax.set_xlabel("Period", fontsize=20)
40-
ax.set_ylabel("Rank", fontsize=20)
41-
ax.set_title("bump-basic · matplotlib · pyplots.ai", fontsize=24)
107+
# Style
108+
ax.set_xlabel("Grand Prix", fontsize=20)
109+
ax.set_ylabel("Championship Position", fontsize=20)
110+
ax.set_title("bump-basic \u00b7 matplotlib \u00b7 pyplots.ai", fontsize=24, fontweight="medium")
42111

43112
ax.set_xticks(x)
44-
ax.set_xticklabels(periods)
45-
ax.set_yticks(range(1, len(entities) + 1))
113+
ax.set_xticklabels(races, rotation=25, ha="right")
114+
ax.set_yticks(range(1, len(drivers) + 1))
46115
ax.tick_params(axis="both", labelsize=16)
47116

48-
ax.grid(True, alpha=0.3, linestyle="--")
49-
ax.legend(fontsize=16, loc="upper left", bbox_to_anchor=(1.02, 1))
117+
ax.yaxis.grid(True, alpha=0.2, linewidth=0.8)
118+
ax.spines["top"].set_visible(False)
119+
ax.spines["right"].set_visible(False)
120+
121+
ax.set_xlim(-0.3, len(races) - 1 + 1.5)
50122

51123
plt.tight_layout()
52124
plt.savefig("plot.png", dpi=300, bbox_inches="tight")

0 commit comments

Comments
 (0)