Skip to content

Commit 2a7a621

Browse files
update(heatmap-basic): plotnine — comprehensive quality review (#4257)
## Summary Updated **plotnine** implementation for **heatmap-basic**. **Changes:** Comprehensive quality review — fix weaknesses from prior reviews, preserve strengths, improve quality across all dimensions. ### Changes - Addressed review weaknesses from prior quality assessment - Improved data choice and visual design - Enhanced library-specific feature usage - Updated to current library and Python versions - Quality self-assessment: pending CI review ## 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 9c97479 commit 2a7a621

2 files changed

Lines changed: 194 additions & 156 deletions

File tree

plots/heatmap-basic/implementations/plotnine.py

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,98 @@
11
""" pyplots.ai
22
heatmap-basic: Basic Heatmap
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: 92/100 | Updated: 2026-02-16
55
"""
66

77
import numpy as np
88
import pandas as pd
99
from plotnine import (
1010
aes,
1111
element_blank,
12+
element_rect,
1213
element_text,
1314
geom_text,
1415
geom_tile,
1516
ggplot,
1617
labs,
18+
scale_color_identity,
1719
scale_fill_gradient2,
20+
scale_x_discrete,
21+
scale_y_discrete,
1822
theme,
1923
theme_minimal,
2024
)
2125

2226

23-
# Data - 8x8 matrix with meaningful patterns (performance metrics by region and quarter)
27+
# Data - 8x8 matrix: quarterly growth rates (%) by department
2428
np.random.seed(42)
25-
rows = ["Region A", "Region B", "Region C", "Region D", "Region E", "Region F", "Region G", "Region H"]
26-
cols = ["Q1 2023", "Q2 2023", "Q3 2023", "Q4 2023", "Q1 2024", "Q2 2024", "Q3 2024", "Q4 2024"]
29+
departments = ["Engineering", "Marketing", "Sales", "Finance", "Operations", "HR", "Research", "Support"]
30+
quarters = ["Q1 '23", "Q2 '23", "Q3 '23", "Q4 '23", "Q1 '24", "Q2 '24", "Q3 '24", "Q4 '24"]
2731

28-
# Create data with a trend and variation
29-
base_values = np.linspace(-30, 40, 8) # Trend across columns
30-
row_effects = np.random.uniform(-10, 10, 8) # Row-specific offsets
31-
values = np.zeros((8, 8))
32-
for i in range(8):
33-
for j in range(8):
34-
values[i, j] = base_values[j] + row_effects[i] + np.random.uniform(-8, 8)
32+
# Vectorized growth rates with recovery trend and departmental variation
33+
base_trend = np.linspace(-18, 22, 8)
34+
dept_offsets = np.array([-6, 10, 14, -3, 4, -10, 8, -5])
35+
values = np.round(base_trend[np.newaxis, :] + dept_offsets[:, np.newaxis] + np.random.normal(0, 4, (8, 8)), 1)
3536

36-
# Create long-form DataFrame for plotnine
37-
data = []
38-
for i, row in enumerate(rows):
39-
for j, col in enumerate(cols):
40-
data.append({"x": col, "y": row, "value": round(values[i, j], 1)})
37+
# Inject distinctive extreme values for storytelling focal points
38+
values[5, 0] = -32.5 # HR deep crisis in Q1 '23
39+
values[2, 7] = 38.2 # Sales strong recovery in Q4 '24
40+
values[6, 6] = 33.7 # Research surge in Q3 '24
4141

42-
df = pd.DataFrame(data)
42+
# Build long-form DataFrame via meshgrid indexing
43+
dept_idx, qtr_idx = np.meshgrid(np.arange(8), np.arange(8), indexing="ij")
44+
df = pd.DataFrame(
45+
{
46+
"Department": pd.Categorical(
47+
[departments[i] for i in dept_idx.ravel()], categories=departments[::-1], ordered=True
48+
),
49+
"Quarter": pd.Categorical([quarters[j] for j in qtr_idx.ravel()], categories=quarters, ordered=True),
50+
"Growth (%)": values.ravel(),
51+
}
52+
)
53+
54+
# Conditional text color: white on dark blue, dark gray on mid, dark brown on gold
55+
df["text_color"] = np.where(df["Growth (%)"] < -12, "white", np.where(df["Growth (%)"] < 18, "#3a3a3a", "#4a2e00"))
4356

44-
# Preserve ordering
45-
df["x"] = pd.Categorical(df["x"], categories=cols, ordered=True)
46-
df["y"] = pd.Categorical(df["y"], categories=rows[::-1], ordered=True) # Reverse for top-to-bottom
57+
# Signed annotation labels
58+
df["label"] = [f"{v:+.1f}" for v in df["Growth (%)"]]
4759

4860
# Plot
4961
plot = (
50-
ggplot(df, aes(x="x", y="y", fill="value"))
51-
+ geom_tile(color="white", size=0.5)
52-
+ geom_text(aes(label="value"), size=12, color="black")
62+
ggplot(df, aes(x="Quarter", y="Department"))
63+
+ geom_tile(aes(fill="Growth (%)"), color="white", size=1.2)
64+
+ geom_text(aes(label="label", color="text_color"), size=10, fontweight="bold", show_legend=False)
5365
+ scale_fill_gradient2(
54-
low="#306998", # Python Blue for negative
55-
mid="white",
56-
high="#FFD43B", # Python Yellow for positive
57-
midpoint=0,
58-
name="Value",
66+
low="#14405e", mid="#ede8e3", high="#c47d00", midpoint=0, name="Growth (%)", limits=(-35, 40)
67+
)
68+
+ scale_color_identity()
69+
+ scale_x_discrete(expand=(0, 0.5))
70+
+ scale_y_discrete(expand=(0, 0.5))
71+
+ labs(
72+
x="Quarter",
73+
y="Department",
74+
title="Quarterly Growth by Department · heatmap-basic · plotnine · pyplots.ai",
75+
subtitle="Year-over-year growth rate (%) across departments, Q1 2023 – Q4 2024",
5976
)
60-
+ labs(x="Time Period", y="Region", title="heatmap-basic · plotnine · pyplots.ai")
6177
+ theme_minimal()
6278
+ theme(
6379
figure_size=(16, 9),
64-
text=element_text(size=14),
65-
axis_title=element_text(size=20),
66-
axis_text_x=element_text(size=14, rotation=45, ha="right"),
67-
axis_text_y=element_text(size=16),
68-
plot_title=element_text(size=24),
69-
legend_title=element_text(size=16),
80+
text=element_text(family="sans-serif"),
81+
plot_title=element_text(size=24, ha="center", weight="bold", margin={"b": 2}),
82+
plot_subtitle=element_text(size=16, ha="center", color="#555555", margin={"b": 8}),
83+
axis_title_x=element_text(size=20, margin={"t": 10}),
84+
axis_title_y=element_text(size=20, margin={"r": 8}),
85+
axis_text_x=element_text(size=16, rotation=45, ha="right", margin={"t": 4}),
86+
axis_text_y=element_text(size=16, ha="right", margin={"r": 4}),
87+
legend_title=element_text(size=16, weight="bold"),
7088
legend_text=element_text(size=14),
89+
legend_position="right",
90+
legend_key_height=40,
7191
panel_grid_major=element_blank(),
7292
panel_grid_minor=element_blank(),
93+
panel_background=element_rect(fill="white", color="none"),
94+
plot_background=element_rect(fill="#f7f7f7", color="none"),
95+
plot_margin=0.02,
7396
)
7497
)
7598

0 commit comments

Comments
 (0)