Skip to content

Commit f661d1c

Browse files
feat(matplotlib): implement heatmap-interactive (#3302)
## Implementation: `heatmap-interactive` - matplotlib Implements the **matplotlib** version of `heatmap-interactive`. **File:** `plots/heatmap-interactive/implementations/matplotlib.py` **Parent Issue:** #3289 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20822846025)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent f2f01a1 commit f661d1c

2 files changed

Lines changed: 297 additions & 0 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
""" pyplots.ai
2+
heatmap-interactive: Interactive Heatmap with Hover and Zoom
3+
Library: matplotlib 3.10.8 | Python 3.13.11
4+
Quality: 90/100 | Created: 2026-01-08
5+
"""
6+
7+
import matplotlib.pyplot as plt
8+
import numpy as np
9+
10+
11+
# Data - Server performance metrics matrix (12x16 for clear display with annotations)
12+
np.random.seed(42)
13+
14+
# Row labels: Servers (12 servers for clear exploration)
15+
rows = [f"Server {i + 1:02d}" for i in range(12)]
16+
17+
# Column labels: Time periods (16 hours, 6AM-9PM)
18+
cols = [f"{h:02d}:00" for h in range(6, 22)]
19+
20+
# Generate realistic server load data (0-100%)
21+
# Base pattern: lower at night, peaks during business hours
22+
hour_idx = np.arange(16)
23+
hour_pattern = 20 + 60 * np.exp(-((hour_idx - 6) ** 2) / 25) # Peak around noon (idx 6 = 12:00)
24+
hour_pattern = hour_pattern / hour_pattern.max()
25+
26+
server_base = np.random.uniform(35, 85, size=12)
27+
28+
data = np.zeros((12, 16))
29+
for i in range(12):
30+
data[i, :] = server_base[i] * hour_pattern + np.random.normal(0, 8, 16)
31+
32+
data = np.clip(data, 0, 100) # Clamp to 0-100%
33+
34+
# Create plot with landscape format (4800x2700 at dpi=300 = 16x9 inches)
35+
fig, ax = plt.subplots(figsize=(16, 9))
36+
37+
# Create heatmap with imshow - using viridis (colorblind-safe)
38+
im = ax.imshow(data, cmap="viridis", aspect="auto", vmin=0, vmax=100)
39+
40+
# Set ticks and labels with proper sizing
41+
ax.set_xticks(np.arange(len(cols)))
42+
ax.set_yticks(np.arange(len(rows)))
43+
ax.set_xticklabels(cols, fontsize=16)
44+
ax.set_yticklabels(rows, fontsize=16)
45+
46+
# Rotate x-axis labels for better readability
47+
plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")
48+
49+
# Add cell annotations showing exact values (simulating hover information)
50+
# Threshold for viridis: values < 50 appear dark, >= 50 appear lighter
51+
for i in range(len(rows)):
52+
for j in range(len(cols)):
53+
value = data[i, j]
54+
# Viridis goes from dark purple (low) to yellow (high)
55+
# Use white text on dark cells (low values), black on bright cells (high values)
56+
text_color = "white" if value < 50 else "black"
57+
ax.text(j, i, f"{value:.0f}", ha="center", va="center", fontsize=14, fontweight="bold", color=text_color)
58+
59+
# Add colorbar with proper sizing
60+
cbar = fig.colorbar(im, ax=ax, shrink=0.8, pad=0.02, aspect=25)
61+
cbar.set_label("CPU Load (%)", fontsize=20)
62+
cbar.ax.tick_params(labelsize=16)
63+
64+
# Labels and title
65+
ax.set_xlabel("Time of Day", fontsize=20)
66+
ax.set_ylabel("Server", fontsize=20)
67+
ax.set_title("heatmap-interactive · matplotlib · pyplots.ai", fontsize=24, pad=15)
68+
69+
# Remove grid entirely for cleaner appearance - cell colors provide enough visual separation
70+
ax.tick_params(which="both", bottom=False, left=False)
71+
72+
# Add instruction text for interactivity context
73+
fig.text(
74+
0.5,
75+
0.01,
76+
"Cell values shown | For interactive hover/zoom, see Plotly or Bokeh implementations",
77+
ha="center",
78+
fontsize=14,
79+
style="italic",
80+
color="gray",
81+
)
82+
83+
plt.tight_layout(rect=[0, 0.03, 1, 1]) # Leave room for instruction text
84+
plt.savefig("plot.png", dpi=300, bbox_inches="tight")
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
library: matplotlib
2+
specification_id: heatmap-interactive
3+
created: '2026-01-08T15:57:54Z'
4+
updated: '2026-01-08T16:30:22Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20822846025
7+
issue: 3289
8+
python_version: 3.13.11
9+
library_version: 3.10.8
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/heatmap-interactive/matplotlib/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/heatmap-interactive/matplotlib/plot_thumb.png
12+
preview_html: null
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Excellent visual clarity with perfectly readable cell annotations using adaptive
17+
text colors
18+
- Viridis colormap provides excellent colorblind accessibility
19+
- Realistic server monitoring scenario with appropriate hourly patterns
20+
- Clean KISS code structure with proper reproducibility seed
21+
- Helpful note directing users to truly interactive alternatives (Plotly/Bokeh)
22+
- Professional formatting with rotated x-labels and well-sized colorbar
23+
weaknesses:
24+
- Grid intentionally removed but the criteria expects grid styling consideration
25+
- True hover/zoom interactivity not possible in matplotlib - the annotation approach
26+
is a reasonable workaround
27+
image_description: The plot displays a 12×16 heatmap matrix representing server
28+
CPU load percentages across 12 servers (Server 01-12 on the y-axis) over a 16-hour
29+
time period (06:00-21:00 on the x-axis). The viridis colormap ranges from dark
30+
purple (low load ~0%) through teal/green (medium ~40-60%) to bright yellow (high
31+
load ~100%). Each cell contains its numerical value with appropriate text contrast
32+
- white text on dark cells (values <50) and black text on bright cells (values
33+
≥50). The title "heatmap-interactive · matplotlib · pyplots.ai" appears at the
34+
top in the correct format. A vertical colorbar on the right displays the "CPU
35+
Load (%)" scale from 0-100. The x-axis label reads "Time of Day" and y-axis label
36+
reads "Server". An italic gray note at the bottom states "Cell values shown |
37+
For interactive hover/zoom, see Plotly or Bokeh implementations" - appropriately
38+
acknowledging matplotlib's static nature while providing the best approximation
39+
of interactivity through visible cell values.
40+
criteria_checklist:
41+
visual_quality:
42+
score: 37
43+
max: 40
44+
items:
45+
- id: VQ-01
46+
name: Text Legibility
47+
score: 10
48+
max: 10
49+
passed: true
50+
comment: Title at 24pt, axis labels at 20pt, tick labels at 16pt, cell annotations
51+
at 14pt bold - all perfectly readable
52+
- id: VQ-02
53+
name: No Overlap
54+
score: 8
55+
max: 8
56+
passed: true
57+
comment: No overlapping text, x-axis labels rotated 45° for clarity
58+
- id: VQ-03
59+
name: Element Visibility
60+
score: 8
61+
max: 8
62+
passed: true
63+
comment: Cell sizes optimal for data density, annotations clearly visible
64+
with good contrast
65+
- id: VQ-04
66+
name: Color Accessibility
67+
score: 5
68+
max: 5
69+
passed: true
70+
comment: Viridis colormap is colorblind-safe
71+
- id: VQ-05
72+
name: Layout Balance
73+
score: 4
74+
max: 5
75+
passed: true
76+
comment: Good proportions, tight_layout applied, slight deduction for bottom
77+
text area
78+
- id: VQ-06
79+
name: Axis Labels
80+
score: 2
81+
max: 2
82+
passed: true
83+
comment: 'Descriptive labels with units: Time of Day, Server, CPU Load (%)'
84+
- id: VQ-07
85+
name: Grid & Legend
86+
score: 0
87+
max: 2
88+
passed: false
89+
comment: No grid (intentionally removed for cleaner appearance), colorbar
90+
well placed
91+
spec_compliance:
92+
score: 22
93+
max: 25
94+
items:
95+
- id: SC-01
96+
name: Plot Type
97+
score: 8
98+
max: 8
99+
passed: true
100+
comment: Correct heatmap visualization using imshow
101+
- id: SC-02
102+
name: Data Mapping
103+
score: 5
104+
max: 5
105+
passed: true
106+
comment: X=time columns, Y=server rows, color=CPU load values
107+
- id: SC-03
108+
name: Required Features
109+
score: 3
110+
max: 5
111+
passed: false
112+
comment: Cell annotations simulate hover tooltips, but interactive zoom/pan
113+
not possible in static matplotlib
114+
- id: SC-04
115+
name: Data Range
116+
score: 3
117+
max: 3
118+
passed: true
119+
comment: Values 0-100% properly displayed with matching colorbar scale
120+
- id: SC-05
121+
name: Legend Accuracy
122+
score: 1
123+
max: 2
124+
passed: true
125+
comment: Colorbar present and accurate, no other legend needed
126+
- id: SC-06
127+
name: Title Format
128+
score: 2
129+
max: 2
130+
passed: true
131+
comment: 'Correct format: heatmap-interactive · matplotlib · pyplots.ai'
132+
data_quality:
133+
score: 18
134+
max: 20
135+
items:
136+
- id: DQ-01
137+
name: Feature Coverage
138+
score: 7
139+
max: 8
140+
passed: true
141+
comment: Shows variation across servers and time, demonstrates hourly patterns
142+
with peak around midday
143+
- id: DQ-02
144+
name: Realistic Context
145+
score: 7
146+
max: 7
147+
passed: true
148+
comment: Server performance metrics is a neutral, appropriate scenario
149+
- id: DQ-03
150+
name: Appropriate Scale
151+
score: 4
152+
max: 5
153+
passed: true
154+
comment: CPU load 0-100% is realistic range
155+
code_quality:
156+
score: 10
157+
max: 10
158+
items:
159+
- id: CQ-01
160+
name: KISS Structure
161+
score: 3
162+
max: 3
163+
passed: true
164+
comment: 'Simple script: imports → data → plot → save, no functions/classes'
165+
- id: CQ-02
166+
name: Reproducibility
167+
score: 3
168+
max: 3
169+
passed: true
170+
comment: np.random.seed(42) set at start
171+
- id: CQ-03
172+
name: Clean Imports
173+
score: 2
174+
max: 2
175+
passed: true
176+
comment: Only matplotlib.pyplot and numpy imported
177+
- id: CQ-04
178+
name: No Deprecated API
179+
score: 1
180+
max: 1
181+
passed: true
182+
comment: Modern matplotlib API used throughout
183+
- id: CQ-05
184+
name: Output Correct
185+
score: 1
186+
max: 1
187+
passed: true
188+
comment: Saves as plot.png with dpi=300
189+
library_features:
190+
score: 3
191+
max: 5
192+
items:
193+
- id: LF-01
194+
name: Distinctive Features
195+
score: 3
196+
max: 5
197+
passed: true
198+
comment: Uses imshow with viridis cmap, colorbar, and text annotations. Good
199+
matplotlib usage but no advanced features like mplcursors
200+
verdict: APPROVED
201+
impl_tags:
202+
dependencies: []
203+
techniques:
204+
- colorbar
205+
- annotations
206+
- manual-ticks
207+
patterns:
208+
- data-generation
209+
- matrix-construction
210+
- iteration-over-groups
211+
dataprep: []
212+
styling:
213+
- custom-colormap

0 commit comments

Comments
 (0)