Skip to content

Commit 1de7827

Browse files
update(bump-basic): plotnine — comprehensive quality review (#4336)
## Summary Updated **plotnine** implementation for **bump-basic**. ### Changes - Switched from scale_color_brewer to scale_color_manual for controlled palette - Added element_blank, element_line, element_rect for refined theming - Cleaner data construction - 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 3617948 commit 1de7827

File tree

2 files changed

+236
-168
lines changed

2 files changed

+236
-168
lines changed

plots/bump-basic/implementations/plotnine.py

Lines changed: 92 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,123 @@
11
""" pyplots.ai
22
bump-basic: Basic Bump Chart
3-
Library: plotnine 0.15.2 | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-23
3+
Library: plotnine 0.15.3 | Python 3.14.3
4+
Quality: 94/100 | Updated: 2026-02-22
55
"""
66

77
import pandas as pd
88
from plotnine import (
99
aes,
10+
element_blank,
11+
element_line,
12+
element_rect,
1013
element_text,
1114
geom_line,
1215
geom_point,
1316
geom_text,
1417
ggplot,
1518
labs,
16-
scale_color_brewer,
19+
scale_color_manual,
1720
scale_x_continuous,
1821
scale_y_reverse,
1922
theme,
2023
theme_minimal,
2124
)
2225

2326

24-
# Data - Tech company rankings over 6 quarters
25-
data = {
26-
"entity": ["Alpha Corp"] * 6 + ["Beta Inc"] * 6 + ["Gamma Tech"] * 6 + ["Delta Systems"] * 6 + ["Epsilon Labs"] * 6,
27-
"period": ["Q1", "Q2", "Q3", "Q4", "Q5", "Q6"] * 5,
28-
"period_num": [1, 2, 3, 4, 5, 6] * 5,
29-
"rank": [
30-
1,
31-
1,
32-
2,
33-
2,
34-
1,
35-
1, # Alpha Corp - starts strong, slight dip, recovers
36-
2,
37-
3,
38-
1,
39-
1,
40-
2,
41-
3, # Beta Inc - rises to top mid-year, then falls
42-
3,
43-
2,
44-
3,
45-
4,
46-
4,
47-
2, # Gamma Tech - volatile movement
48-
4,
49-
4,
50-
4,
51-
3,
52-
3,
53-
4, # Delta Systems - stable middle performer
54-
5,
55-
5,
56-
5,
57-
5,
58-
5,
59-
5, # Epsilon Labs - consistently last
60-
],
27+
# Data - Streaming platform market share rankings over 8 quarters
28+
platforms = ["StreamVue", "WavePlay", "CloudCast", "PixelFlix", "SonicNet", "EchoTV"]
29+
quarters = ["Q1'24", "Q2'24", "Q3'24", "Q4'24", "Q1'25", "Q2'25", "Q3'25", "Q4'25"]
30+
n_periods = len(quarters)
31+
32+
rankings = {
33+
"StreamVue": [1, 1, 1, 2, 2, 3, 3, 4],
34+
"WavePlay": [2, 3, 3, 1, 1, 1, 1, 1],
35+
"CloudCast": [4, 2, 2, 3, 3, 2, 2, 2],
36+
"PixelFlix": [3, 4, 4, 4, 5, 5, 4, 3],
37+
"SonicNet": [5, 5, 5, 5, 4, 4, 5, 5],
38+
"EchoTV": [6, 6, 6, 6, 6, 6, 6, 6],
6139
}
62-
df = pd.DataFrame(data)
40+
41+
rows = []
42+
for platform, ranks in rankings.items():
43+
for i, rank in enumerate(ranks):
44+
rows.append({"platform": platform, "quarter": quarters[i], "qnum": i + 1, "rank": rank})
45+
df = pd.DataFrame(rows)
6346

6447
# Subset for end labels
65-
df_end = df[df["period_num"] == 6].copy()
48+
df_end = df[df["qnum"] == n_periods].copy()
49+
50+
# Visual hierarchy: protagonist entities vs supporting cast
51+
protagonists = ["StreamVue", "WavePlay"]
52+
supporting = ["CloudCast", "PixelFlix", "SonicNet", "EchoTV"]
53+
54+
df_hero = df[df["platform"].isin(protagonists)]
55+
df_support = df[df["platform"].isin(supporting)]
56+
57+
# Crossover emphasis at Q4'24 (qnum=4) where WavePlay overtakes StreamVue
58+
df_crossover = pd.DataFrame(
59+
[{"qnum": 4, "rank": 1, "platform": "WavePlay"}, {"qnum": 4, "rank": 2, "platform": "StreamVue"}]
60+
)
61+
62+
# Colorblind-safe palette — Python Blue first, warm orange for WavePlay
63+
# Replaced red with teal (#17becf) for deuteranopia safety
64+
palette = {
65+
"StreamVue": "#306998",
66+
"WavePlay": "#e8963e",
67+
"CloudCast": "#59a14f",
68+
"PixelFlix": "#17becf",
69+
"SonicNet": "#9d7660",
70+
"EchoTV": "#bab0ac",
71+
}
6672

67-
# Plot
73+
# Plot — layered for visual hierarchy
6874
plot = (
69-
ggplot(df, aes(x="period_num", y="rank", color="entity", group="entity"))
70-
+ geom_line(size=2.5, alpha=0.8)
71-
+ geom_point(size=6)
72-
+ geom_text(aes(label="entity"), data=df_end, nudge_x=0.3, ha="left", size=12)
73-
+ scale_y_reverse(breaks=[1, 2, 3, 4, 5])
74-
+ scale_x_continuous(breaks=[1, 2, 3, 4, 5, 6], labels=["Q1", "Q2", "Q3", "Q4", "Q5", "Q6"], limits=(0.5, 7.5))
75-
+ scale_color_brewer(type="qual", palette="Set2")
76-
+ labs(x="Quarter", y="Rank", title="bump-basic · plotnine · pyplots.ai", color="Company")
75+
ggplot(df, aes(x="qnum", y="rank", color="platform", group="platform"))
76+
# Supporting lines: thinner, more transparent
77+
+ geom_line(data=df_support, size=1.8, alpha=0.4)
78+
+ geom_point(data=df_support, size=4, stroke=0.6, fill="white")
79+
+ geom_point(data=df_support, size=2.5, alpha=0.5)
80+
# Protagonist lines: bold and saturated
81+
+ geom_line(data=df_hero, size=3.5, alpha=0.95)
82+
+ geom_point(data=df_hero, size=7, stroke=1.0, fill="white")
83+
+ geom_point(data=df_hero, size=4.5)
84+
# Crossover emphasis at Q4'24
85+
+ geom_point(data=df_crossover, size=12, alpha=0.15)
86+
# End labels — bold for protagonists, italic for supporting
87+
+ geom_text(
88+
aes(label="platform"),
89+
data=df_end[df_end["platform"].isin(protagonists)],
90+
nudge_x=0.35,
91+
ha="left",
92+
size=13,
93+
fontweight="bold",
94+
)
95+
+ geom_text(
96+
aes(label="platform"),
97+
data=df_end[df_end["platform"].isin(supporting)],
98+
nudge_x=0.35,
99+
ha="left",
100+
size=11,
101+
fontstyle="italic",
102+
alpha=0.7,
103+
)
104+
+ scale_y_reverse(breaks=range(1, len(platforms) + 1))
105+
+ scale_x_continuous(breaks=range(1, n_periods + 1), labels=quarters, limits=(0.5, n_periods + 2))
106+
+ scale_color_manual(values=palette)
107+
+ labs(x="Quarter", y="Market Share Ranking", title="bump-basic · plotnine · pyplots.ai")
77108
+ theme_minimal()
78109
+ theme(
79110
figure_size=(16, 9),
80-
text=element_text(size=14),
81-
axis_title=element_text(size=20),
82-
axis_text=element_text(size=16),
83-
plot_title=element_text(size=24),
84-
legend_text=element_text(size=16),
85-
legend_title=element_text(size=18),
111+
text=element_text(size=14, color="#3c3c3c"),
112+
axis_title=element_text(size=20, color="#555555"),
113+
axis_text=element_text(size=16, color="#666666"),
114+
axis_text_x=element_text(rotation=0),
115+
plot_title=element_text(size=24, weight="bold", color="#2b2b2b"),
116+
panel_grid_major_x=element_blank(),
117+
panel_grid_minor=element_blank(),
118+
panel_grid_major_y=element_line(alpha=0.15, size=0.4, color="#cccccc"),
119+
panel_background=element_rect(fill="white", color="none"),
120+
plot_background=element_rect(fill="#fafafa", color="none"),
86121
legend_position="none",
87122
)
88123
)

0 commit comments

Comments
 (0)