Skip to content

Commit b88dedc

Browse files
update(heatmap-basic): letsplot — comprehensive quality review (#4260)
## Summary Updated **letsplot** 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 fcc167f commit b88dedc

2 files changed

Lines changed: 191 additions & 149 deletions

File tree

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
""" pyplots.ai
22
heatmap-basic: Basic Heatmap
3-
Library: letsplot 4.8.1 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: letsplot 4.8.2 | Python 3.14.3
4+
Quality: 91/100 | Updated: 2026-02-15
55
"""
66

77
import numpy as np
8-
import pandas as pd
98
from lets_plot import (
109
LetsPlot,
1110
aes,
11+
element_blank,
12+
element_rect,
1213
element_text,
1314
geom_text,
1415
geom_tile,
1516
ggplot,
1617
ggsize,
18+
guide_colorbar,
1719
labs,
18-
scale_fill_gradient2,
20+
layer_tooltips,
21+
scale_color_identity,
22+
scale_fill_viridis,
23+
scale_x_discrete,
24+
scale_y_discrete,
1925
theme,
2026
theme_minimal,
2127
)
@@ -24,55 +30,67 @@
2430

2531
LetsPlot.setup_html()
2632

27-
# Data - Department performance scores by quarter
33+
# Data - Monthly energy consumption (kWh) by building zone
2834
np.random.seed(42)
29-
departments = ["Sales", "Marketing", "R&D", "Operations", "Finance", "HR"]
30-
quarters = ["Q1", "Q2", "Q3", "Q4", "Q5", "Q6", "Q7", "Q8"]
35+
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
36+
zones = ["Lobby", "Offices", "Lab", "Server Room", "Cafeteria", "Warehouse"]
3137

32-
# Generate performance scores with some patterns
33-
n_rows = len(quarters)
34-
n_cols = len(departments)
35-
base_scores = np.random.rand(n_rows, n_cols) * 60 + 20 # Range 20-80
38+
# Realistic patterns: seasonal variation + zone-specific baselines
39+
baselines = np.array([120, 280, 350, 520, 180, 90])
40+
seasonal = np.array([1.3, 1.2, 1.0, 0.8, 0.7, 0.75, 0.85, 0.9, 0.8, 0.9, 1.1, 1.25])
3641

37-
# Add some trend patterns to make data more interesting
38-
trend = np.linspace(0, 15, n_rows).reshape(-1, 1) # Slight upward trend
39-
values = base_scores + trend
40-
values = np.clip(values, 15, 95) # Keep in realistic range
41-
values = np.round(values, 1)
42+
values = np.outer(seasonal, baselines)
43+
noise = np.random.normal(0, 15, (len(months), len(zones)))
44+
values = np.round(values + noise, 0).astype(int)
4245

43-
# Create long-form DataFrame for lets-plot
44-
rows = []
45-
for i, quarter in enumerate(quarters):
46-
for j, dept in enumerate(departments):
47-
rows.append({"Department": dept, "Quarter": quarter, "Score": values[i, j]})
46+
# Build long-form data using vectorized operations
47+
n_months, n_zones = len(months), len(zones)
48+
zone_col = np.tile(zones, n_months).tolist()
49+
month_col = np.repeat(months, n_zones).tolist()
50+
kwh_col = values.flatten().tolist()
4851

49-
df = pd.DataFrame(rows)
52+
# Adaptive text color: white on dark cells, dark on light cells
53+
median_val = int(np.median(kwh_col))
54+
text_color = ["#ffffff" if v > median_val else "#1a1a2e" for v in kwh_col]
5055

51-
# Preserve category order (reverse quarters for top-to-bottom display)
52-
df["Department"] = pd.Categorical(df["Department"], categories=departments, ordered=True)
53-
df["Quarter"] = pd.Categorical(df["Quarter"], categories=quarters[::-1], ordered=True)
56+
data = {"Zone": zone_col, "Month": month_col, "kWh": kwh_col, "label_color": text_color}
5457

55-
# Create heatmap using geom_tile with value annotations
58+
# Heatmap with perceptually-uniform viridis colormap and interactive tooltips
5659
plot = (
57-
ggplot(df, aes(x="Department", y="Quarter", fill="Score"))
58-
+ geom_tile(width=0.95, height=0.95)
59-
+ geom_text(aes(label="Score"), size=14, color="white", fontface="bold")
60-
+ scale_fill_gradient2(low="#306998", mid="#FFD43B", high="#DC2626", midpoint=55, name="Performance\nScore")
61-
+ labs(x="Department", y="Quarter", title="heatmap-basic · letsplot · pyplots.ai")
60+
ggplot(data, aes(x="Zone", y="Month", fill="kWh"))
61+
+ geom_tile(
62+
width=0.92,
63+
height=0.92,
64+
tooltips=layer_tooltips()
65+
.line("@Zone | @Month")
66+
.line("Energy: @kWh kWh")
67+
.line("Median: " + str(median_val) + " kWh"),
68+
)
69+
+ geom_text(aes(label="kWh", color="label_color"), size=14, fontface="bold")
70+
+ scale_color_identity()
71+
+ scale_fill_viridis(
72+
option="viridis", direction=-1, name="Energy (kWh)", guide=guide_colorbar(barwidth=18, barheight=300, nbin=256)
73+
)
74+
+ scale_x_discrete(limits=zones)
75+
+ scale_y_discrete(limits=months[::-1])
76+
+ labs(x="Building Zone", y="Month", title="heatmap-basic · letsplot · pyplots.ai")
6277
+ theme_minimal()
6378
+ theme(
64-
axis_title=element_text(size=20),
65-
axis_text=element_text(size=16),
66-
axis_text_x=element_text(angle=45),
67-
plot_title=element_text(size=24),
79+
plot_title=element_text(size=26, face="bold", color="#1a1a2e"),
80+
axis_title=element_text(size=20, color="#2d2d44"),
81+
axis_text_x=element_text(size=16, face="bold", color="#2d2d44"),
82+
axis_text_y=element_text(size=16, color="#2d2d44"),
6883
legend_text=element_text(size=14),
69-
legend_title=element_text(size=16),
84+
legend_title=element_text(size=16, face="bold"),
85+
panel_grid=element_blank(),
86+
plot_background=element_rect(fill="#fafafa", color="#fafafa"),
87+
plot_margin=[40, 20, 20, 20],
7088
)
7189
+ ggsize(1600, 900)
7290
)
7391

7492
# Save PNG (scale=3 gives 4800x2700)
7593
ggsave(plot, "plot.png", path=".", scale=3)
7694

77-
# Save HTML for interactivity
95+
# Save HTML for interactive tooltips (lets-plot distinctive feature)
7896
ggsave(plot, "plot.html", path=".")

0 commit comments

Comments
 (0)