diff --git a/plots/heatmap-basic/implementations/bokeh.py b/plots/heatmap-basic/implementations/bokeh.py index 6992bb6c3f..62b343c600 100644 --- a/plots/heatmap-basic/implementations/bokeh.py +++ b/plots/heatmap-basic/implementations/bokeh.py @@ -1,100 +1,111 @@ """ pyplots.ai heatmap-basic: Basic Heatmap -Library: bokeh 3.8.1 | Python 3.13.11 -Quality: 92/100 | Created: 2025-12-23 +Library: bokeh 3.8.2 | Python 3.14.3 +Quality: 91/100 | Updated: 2026-02-15 """ import numpy as np +import pandas as pd from bokeh.io import export_png, save -from bokeh.models import BasicTicker, ColorBar, ColumnDataSource, LabelSet, LinearColorMapper -from bokeh.palettes import Viridis256 +from bokeh.models import BasicTicker, ColumnDataSource, HoverTool, LabelSet from bokeh.plotting import figure from bokeh.resources import CDN +from bokeh.transform import linear_cmap -# Data - Monthly sales performance by product category +# Data - Monthly temperature anomalies (°C) by city np.random.seed(42) -x_labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug"] -y_labels = ["Product A", "Product B", "Product C", "Product D", "Product E", "Product F"] - -# Generate heatmap values (sales performance 0-100) -values = np.random.rand(len(y_labels), len(x_labels)) * 100 - -# Flatten data for ColumnDataSource -x_data = [] -y_data = [] -value_data = [] -text_data = [] -text_color_data = [] - -for i, y in enumerate(y_labels): - for j, x in enumerate(x_labels): - x_data.append(x) - y_data.append(y) +months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct"] +cities = ["Oslo", "Berlin", "Madrid", "Cairo", "Mumbai", "Tokyo", "Sydney"] + +# Generate realistic temperature anomalies with geographic patterns +base_anomalies = np.random.randn(len(cities), len(months)) * 0.6 +# Northern cities: colder winters, warmer summers +for i, city in enumerate(cities): + seasonal = np.sin(np.linspace(-np.pi / 2, 3 * np.pi / 4, len(months))) + if city in ("Oslo", "Berlin"): + base_anomalies[i] += seasonal * 1.5 - 0.3 + elif city in ("Madrid", "Cairo"): + base_anomalies[i] += seasonal * 1.2 + 0.4 + elif city == "Mumbai": + base_anomalies[i] += 0.8 + elif city == "Sydney": + base_anomalies[i] -= seasonal * 0.9 + elif city == "Tokyo": + base_anomalies[i] += seasonal * 0.7 + +values = np.round(base_anomalies, 1) + +# Flatten to DataFrame for ColumnDataSource +records = [] +for i, city in enumerate(cities): + for j, month in enumerate(months): val = values[i, j] - value_data.append(val) - text_data.append(f"{val:.0f}") - # Use white text on dark cells, black on light cells - text_color_data.append("white" if val > 50 else "black") - -source = ColumnDataSource( - data={"x": x_data, "y": y_data, "value": value_data, "text": text_data, "text_color": text_color_data} -) - -# Color mapper -color_mapper = LinearColorMapper(palette=Viridis256, low=0, high=100) - -# Create figure with categorical axes + records.append( + { + "month": month, + "city": city, + "anomaly": val, + "label": f"{val:+.1f}", + "text_color": "white" if abs(val) > 1.2 else "#333333", + } + ) + +source = ColumnDataSource(pd.DataFrame(records)) + +# Color mapping — diverging palette for positive/negative anomalies +blues = ["#2166ac", "#4393c3", "#92c5de", "#d1e5f0"] +reds = ["#fddbc7", "#f4a582", "#d6604d", "#b2182b"] +diverging_palette = blues[::-1] + ["#f7f7f7"] + reds + +# Create figure p = figure( width=4800, height=2700, - x_range=x_labels, - y_range=y_labels, + x_range=months, + y_range=list(reversed(cities)), title="heatmap-basic · bokeh · pyplots.ai", - x_axis_label="Month", - y_axis_label="Product", + x_axis_label="Month (2024)", + y_axis_label="City", toolbar_location=None, tools="", ) -# Plot heatmap rectangles -p.rect( - x="x", - y="y", - width=1, - height=1, - source=source, - fill_color={"field": "value", "transform": color_mapper}, - line_color=None, -) +# Plot heatmap rectangles with linear_cmap +cmap = linear_cmap("anomaly", diverging_palette, low=-2.5, high=2.5) +r = p.rect(x="month", y="city", width=1, height=1, source=source, fill_color=cmap, line_color="white", line_width=2) -# Add value annotations in cells +# Add value annotations labels = LabelSet( - x="x", - y="y", - text="text", + x="month", + y="city", + text="label", text_color="text_color", source=source, text_align="center", text_baseline="middle", - text_font_size="24pt", + text_font_size="22pt", ) p.add_layout(labels) -# Add color bar -color_bar = ColorBar( - color_mapper=color_mapper, +# Color bar from renderer (idiomatic Bokeh pattern) +color_bar = r.construct_color_bar( + width=40, ticker=BasicTicker(desired_num_ticks=10), label_standoff=16, major_label_text_font_size="18pt", border_line_color=None, - location=(0, 0), - width=40, - title="Sales Score", + padding=10, + title="Anomaly (°C)", title_text_font_size="20pt", + title_standoff=20, ) p.add_layout(color_bar, "right") +# HoverTool for interactive HTML version +hover = HoverTool(tooltips=[("City", "@city"), ("Month", "@month"), ("Anomaly", "@anomaly{+0.0} °C")], renderers=[r]) +p.add_tools(hover) + # Styling for 4800x2700 px p.title.text_font_size = "28pt" p.xaxis.axis_label_text_font_size = "22pt" @@ -102,21 +113,20 @@ p.xaxis.major_label_text_font_size = "18pt" p.yaxis.major_label_text_font_size = "18pt" -# Grid styling - disabled for heatmap +# Grid and axes p.xgrid.grid_line_color = None p.ygrid.grid_line_color = None - -# Axis styling -p.axis.axis_line_color = "#cccccc" -p.axis.major_tick_line_color = "#cccccc" +p.axis.axis_line_color = None +p.axis.major_tick_line_color = None +p.outline_line_color = None # Background -p.background_fill_color = "#f8f8f8" +p.min_border_right = 120 +p.background_fill_color = "#fafafa" p.border_fill_color = "white" -p.outline_line_color = None # Save PNG export_png(p, filename="plot.png") -# Save HTML for interactive version +# Save HTML with interactive hover save(p, filename="plot.html", resources=CDN, title="heatmap-basic · bokeh · pyplots.ai") diff --git a/plots/heatmap-basic/metadata/bokeh.yaml b/plots/heatmap-basic/metadata/bokeh.yaml index 8d1c8f234a..f08aa535da 100644 --- a/plots/heatmap-basic/metadata/bokeh.yaml +++ b/plots/heatmap-basic/metadata/bokeh.yaml @@ -1,164 +1,191 @@ library: bokeh specification_id: heatmap-basic created: '2025-12-23T00:48:06Z' -updated: '2025-12-23T01:21:11Z' -generated_by: claude-opus-4-5-20251101 +updated: '2026-02-15T21:30:42Z' +generated_by: claude-opus-4-6 workflow_run: 20447966194 issue: 0 -python_version: 3.13.11 -library_version: 3.8.1 +python_version: 3.14.3 +library_version: 3.8.2 preview_url: https://storage.googleapis.com/pyplots-images/plots/heatmap-basic/bokeh/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/heatmap-basic/bokeh/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/heatmap-basic/bokeh/plot.html -quality_score: 92 +quality_score: 91 impl_tags: dependencies: [] techniques: - colorbar - annotations + - hover-tooltips - html-export patterns: - - columndatasource - data-generation + - columndatasource - iteration-over-groups dataprep: [] styling: + - custom-colormap - edge-highlighting + - minimal-chrome review: strengths: - - Excellent use of Viridis colormap for colorblind accessibility - - Smart adaptive text colors (white on dark, black on light cells) for optimal readability - - Clean KISS code structure with proper seed for reproducibility - - Correct title format and all spec requirements met - - Generates both PNG and HTML output leveraging Bokeh interactivity capability - - Well-sized text elements for 4800x2700 resolution + - Excellent use of conditional text colors (white on dark cells, dark gray on light) + for optimal readability across all intensity levels + - Hand-crafted diverging palette with smooth blue to white to red progression, well-calibrated + symmetric range + - Clean removal of all chart chrome (grid, axis lines, outline) with white cell + borders creating a polished, modern look + - Dual output (PNG + HTML with HoverTool) leverages Bokeh interactive strengths + - Data tells a coherent geographic/seasonal story without requiring annotations + - construct_color_bar from renderer is idiomatic modern Bokeh weaknesses: - - Axis labels could include more context (e.g., Month 2024 or Product Category) - - Could add HoverTool for enhanced interactivity in HTML version - - Random data does not show clear patterns/clusters typical in real sales data - image_description: The plot displays a 6x8 heatmap showing "Monthly Sales Performance - by Product Category." The y-axis shows Product A through Product F, and the x-axis - shows months Jan through Aug. Each cell contains a numeric value (0-100) representing - sales scores. The Viridis colormap is used (purple for low values ~0, teal/green - for mid values ~50, yellow for high values ~100). Cell annotations use adaptive - text colors - white text on dark purple cells, black text on light yellow/green - cells. A vertical colorbar on the right is labeled "Sales Score" with tick marks - from 0 to 100. The title "heatmap-basic · bokeh · pyplots.ai" appears in the top-left. - Axis labels show "Month" and "Product". Background is light gray (#f8f8f8). + - Color bar title Anomaly (°C) is slightly cramped at the right edge — could benefit + from more right margin or repositioning + - Blue-red diverging palette could be replaced with a more colorblind-safe alternative + (e.g., blue-orange or a perceptually uniform diverging scheme) + - Only 10 months shown (Jan-Oct) instead of full 12 — the data would be more complete + with all months + image_description: The plot displays a heatmap of monthly temperature anomalies + (°C) for 7 cities (Oslo, Berlin, Madrid, Cairo, Mumbai, Tokyo, Sydney) across + 10 months (Jan–Oct 2024). A hand-crafted diverging blue-to-red color palette maps + negative anomalies (blue/cool) to positive anomalies (red/warm), centered on white + at zero. Each cell contains a value annotation with +/- sign, using conditional + text colors — white text on dark cells and dark gray text on light cells for readability. + A vertical color bar on the right shows the "Anomaly (°C)" scale from -2.5 to + 2.5. The title reads "heatmap-basic · bokeh · pyplots.ai" at the top left. The + x-axis is labeled "Month (2024)" and the y-axis "City". Grid lines, axis lines, + and outlines are removed. White borders separate cells. The background is a subtle + off-white (#fafafa) with white plot borders. Northern cities (Oslo, Berlin) show + clear cold-winter/warm-summer seasonal patterns in blue-to-red, while Sydney shows + a reversed Southern Hemisphere pattern. Mumbai shows consistently warm anomalies. criteria_checklist: visual_quality: - score: 36 - max: 40 + score: 28 + max: 30 items: - id: VQ-01 name: Text Legibility - score: 9 - max: 10 + score: 8 + max: 8 passed: true - comment: Title, axis labels, and cell annotations are all clearly readable. - Text sizes are well-scaled for 4800x2700. + comment: 'All font sizes explicitly set: title 28pt, axis labels 22pt, tick + labels 18pt, cell annotations 22pt. All perfectly readable.' - id: VQ-02 name: No Overlap - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: No overlapping text anywhere; cell annotations are perfectly centered. + comment: No overlapping text anywhere in the plot. - id: VQ-03 name: Element Visibility - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: Heatmap cells are clearly visible with good color differentiation. + comment: Heatmap rectangles fill grid perfectly, all cells clearly distinguishable + with white borders. - id: VQ-04 name: Color Accessibility - score: 5 - max: 5 - passed: true - comment: Viridis is a colorblind-safe palette with excellent perceptual uniformity. + score: 3 + max: 4 + passed: false + comment: Blue-red diverging palette is standard but not one of the recommended + colorblind-safe palettes. Works for most colorblind types but deuteranopia + could find subtle mid-range values harder to differentiate. - id: VQ-05 - name: Layout Balance - score: 4 - max: 5 - passed: true - comment: Good proportions, though slight asymmetry with colorbar placement. + name: Layout & Canvas + score: 3 + max: 4 + passed: false + comment: Good overall layout with plot filling most of canvas. Color bar title + slightly cramped at right edge. - id: VQ-06 - name: Axis Labels - score: 1 + name: Axis Labels & Title + score: 2 max: 2 passed: true - comment: Labels are descriptive ("Month", "Product") but lack units. - - id: VQ-07 - name: Grid & Legend - score: 1 - max: 2 + comment: Month (2024) with context, City descriptive for categorical axis. + design_excellence: + score: 15 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Custom hand-crafted diverging palette, white cell borders, conditional + text colors, removed chrome — clearly above defaults, approaching publication + quality. + - id: DE-02 + name: Visual Refinement + score: 5 + max: 6 passed: true - comment: Colorbar is present and functional; grid is disabled (appropriate - for heatmap). + comment: Grid removed, axis/tick lines removed, outline removed, custom background + colors, generous whitespace between cells. Very polished. + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: 'Clear seasonal narrative: northern cities show cold winters to warm + summers, Sydney reversed, Mumbai consistently warm. Diverging colormap draws + eye to extremes.' spec_compliance: - score: 25 - max: 25 + score: 15 + max: 15 items: - id: SC-01 name: Plot Type - score: 8 - max: 8 - passed: true - comment: Correct heatmap visualization. - - id: SC-02 - name: Data Mapping score: 5 max: 5 passed: true - comment: X (months), Y (products), values correctly mapped to color. - - id: SC-03 + comment: Correct heatmap with color-mapped matrix cells. + - id: SC-02 name: Required Features - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Has colorbar legend, value annotations in cells as spec suggests. - - id: SC-04 - name: Data Range + comment: Diverging colormap, value annotations, colorbar legend, logical ordering + — all present. + - id: SC-03 + name: Data Mapping score: 3 max: 3 passed: true - comment: All data visible, axes show complete range. - - id: SC-05 - name: Legend Accuracy - score: 2 - max: 2 - passed: true - comment: Colorbar correctly labeled "Sales Score". - - id: SC-06 - name: Title Format - score: 2 - max: 2 + comment: X=months, Y=cities, values mapped to fill color correctly. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 passed: true - comment: 'Correct format: "heatmap-basic · bokeh · pyplots.ai".' + comment: Title format correct. Colorbar serves as legend with proper title. data_quality: - score: 18 - max: 20 + score: 14 + max: 15 items: - id: DQ-01 name: Feature Coverage - score: 7 - max: 8 + score: 5 + max: 6 passed: true - comment: Shows variation across cells with both high and low values; good - range coverage. Could show more clustering patterns. + comment: Shows positive and negative values, seasonal patterns, geographic + variation, reversed Southern Hemisphere. Could show wider extremes or outlier + city. - id: DQ-02 name: Realistic Context - score: 7 - max: 7 + score: 5 + max: 5 passed: true - comment: Monthly sales performance by product is a realistic, comprehensible - business scenario. + comment: Monthly temperature anomalies by city — real, neutral, comprehensible + scientific scenario. - id: DQ-03 name: Appropriate Scale score: 4 - max: 5 + max: 4 passed: true - comment: 0-100 sales score is reasonable; 6 products × 8 months is within - spec range (5-50). + comment: Anomalies range from -2.1 to +1.9 C — very realistic for monthly + temperature anomalies. code_quality: score: 10 max: 10 @@ -168,41 +195,47 @@ review: score: 3 max: 3 passed: true - comment: 'Linear flow: imports → data → plot → save. No functions/classes.' + comment: Imports, Data, Plot, Save structure. No functions or classes. - id: CQ-02 name: Reproducibility - score: 3 - max: 3 + score: 2 + max: 2 passed: true - comment: Uses np.random.seed(42). + comment: np.random.seed(42) set. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: All imports are used. + comment: All imports used, no unused imports. - id: CQ-04 - name: No Deprecated API - score: 1 - max: 1 + name: Code Elegance + score: 2 + max: 2 passed: true - comment: Current Bokeh API used. + comment: Clean, Pythonic code. Appropriate complexity. - id: CQ-05 - name: Output Correct + name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot.png (and plot.html for interactivity). - library_features: - score: 3 - max: 5 + comment: Saves as plot.png and plot.html. No deprecated functions. + library_mastery: + score: 9 + max: 10 items: - - id: LF-01 - name: Uses distinctive library features - score: 3 + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: Uses ColumnDataSource, linear_cmap, LabelSet, HoverTool, construct_color_bar + — all idiomatic Bokeh patterns. + - id: LM-02 + name: Distinctive Features + score: 4 max: 5 passed: true - comment: Uses ColumnDataSource, LinearColorMapper, ColorBar, LabelSet correctly. - Good use of Bokeh's rect glyph for heatmap. Also generates HTML for interactivity. - Could leverage more Bokeh-specific features like HoverTool. + comment: HoverTool for interactive HTML, construct_color_bar (modern Bokeh + API), linear_cmap transform, dual PNG+HTML output. verdict: APPROVED diff --git a/plots/heatmap-basic/specification.md b/plots/heatmap-basic/specification.md index cef917e2dc..4b790ff068 100644 --- a/plots/heatmap-basic/specification.md +++ b/plots/heatmap-basic/specification.md @@ -23,4 +23,4 @@ A heatmap displaying values in a matrix format using color intensity. Each cell' - Use a diverging colormap for data with positive/negative values - Add value annotations in cells when readable - Include a colorbar legend -- Consider clustering rows/columns for better pattern visibility +- Order rows/columns logically (alphabetical, by magnitude, or by similarity) diff --git a/plots/heatmap-basic/specification.yaml b/plots/heatmap-basic/specification.yaml index 8c02cfc6a5..82b0687684 100644 --- a/plots/heatmap-basic/specification.yaml +++ b/plots/heatmap-basic/specification.yaml @@ -6,7 +6,7 @@ title: Basic Heatmap # Specification tracking created: 2025-12-14T09:02:34Z -updated: 2025-12-14T09:02:34Z +updated: 2026-02-15T12:00:00Z issue: 691 suggested: MarkusNeusinger @@ -18,6 +18,7 @@ tags: data_type: - numeric - categorical + - matrix domain: - statistics - general