diff --git a/plots/streamgraph-basic/implementations/python/plotly.py b/plots/streamgraph-basic/implementations/python/plotly.py index 0ce769be03..41d0f521dc 100644 --- a/plots/streamgraph-basic/implementations/python/plotly.py +++ b/plots/streamgraph-basic/implementations/python/plotly.py @@ -1,66 +1,79 @@ -""" pyplots.ai +""" anyplot.ai streamgraph-basic: Basic Stream Graph -Library: plotly 6.5.0 | Python 3.13.11 -Quality: 92/100 | Created: 2025-12-23 +Library: plotly 6.7.0 | Python 3.13.13 +Quality: 88/100 | Updated: 2026-05-05 """ +import sys + + +sys.path.pop(0) + +import os + import numpy as np import pandas as pd import plotly.graph_objects as go +# Theme tokens +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" + +# Okabe-Ito palette — first series always #009E73 +OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9"] + # Data - Monthly streaming hours by music genre over 2 years np.random.seed(42) months = pd.date_range(start="2022-01-01", periods=24, freq="ME") genres = ["Pop", "Rock", "Hip-Hop", "Electronic", "Jazz", "Classical"] -# Generate smooth, realistic streaming data base_values = {"Pop": 45, "Rock": 35, "Hip-Hop": 40, "Electronic": 25, "Jazz": 15, "Classical": 12} data = {} for genre in genres: - # Create smooth trends with seasonal variation trend = np.cumsum(np.random.randn(24) * 2) seasonal = 5 * np.sin(np.linspace(0, 4 * np.pi, 24)) noise = np.random.randn(24) * 3 values = base_values[genre] + trend + seasonal + noise - values = np.maximum(values, 5) # Ensure positive values + values = np.maximum(values, 5) data[genre] = values df = pd.DataFrame(data, index=months) +month_labels = months.strftime("%Y-%m").tolist() # Calculate streamgraph layout (centered baseline) values_array = df.values.T # Shape: (n_genres, n_time_points) n_genres, n_time = values_array.shape -# Calculate cumulative sums for stacking cumsum = np.vstack([np.zeros(n_time), np.cumsum(values_array, axis=0)]) - -# Center the baseline symmetrically around the x-axis total = cumsum[-1] offset = total / 2 -# Colors - Python Blue and Yellow first, then colorblind-safe palette -colors = ["#306998", "#FFD43B", "#E24A33", "#8EBA42", "#988ED5", "#348ABD"] - -# Create figure +# Plot fig = go.Figure() -# Add each genre as a filled area with smooth spline curves for i, genre in enumerate(genres): y_lower = cumsum[i] - offset y_upper = cumsum[i + 1] - offset - # Create coordinates for fill (forward then backward) - x_fill = list(months) + list(months)[::-1] + x_fill = month_labels + month_labels[::-1] y_fill = list(y_upper) + list(y_lower)[::-1] + # Dominant stream (Pop) at full opacity; others slightly dimmed for visual hierarchy + opacity = 1.0 if i == 0 else 0.80 + fig.add_trace( go.Scatter( x=x_fill, y=y_fill, fill="toself", - fillcolor=colors[i], - line={"color": colors[i], "width": 0.5, "shape": "spline", "smoothing": 1.0}, + fillcolor=OKABE_ITO[i], + opacity=opacity, + line={"color": OKABE_ITO[i], "width": 0.5, "shape": "spline", "smoothing": 1.0}, name=genre, mode="none", hoverinfo="name+x", @@ -68,36 +81,52 @@ ) ) -# Update layout for 4800x2700 px +subtitle = f"Monthly streaming hours by music genre, 2022–2023" + fig.update_layout( - title={"text": "streamgraph-basic · plotly · pyplots.ai", "font": {"size": 36}, "x": 0.5, "xanchor": "center"}, + title={ + "text": f"streamgraph-basic · plotly · anyplot.ai
{subtitle}", + "font": {"size": 28, "color": INK}, + "x": 0.5, + "xanchor": "center", + }, xaxis={ - "title": {"text": "Month", "font": {"size": 28}}, - "tickfont": {"size": 22}, - "showgrid": True, - "gridcolor": "rgba(128,128,128,0.2)", - "gridwidth": 1, + "title": {"text": "Month", "font": {"size": 22, "color": INK}}, + "tickfont": {"size": 18, "color": INK_SOFT}, + "showgrid": False, + "showline": True, + "linecolor": INK_SOFT, + "mirror": False, # bottom spine only — no top spine + "zeroline": False, }, yaxis={ - "title": {"text": "Streaming Hours (Millions)", "font": {"size": 28}}, - "tickfont": {"size": 22}, + "showticklabels": False, # hide confusing negative offset values "showgrid": True, - "gridcolor": "rgba(128,128,128,0.2)", + "gridcolor": GRID, "gridwidth": 1, "zeroline": True, - "zerolinecolor": "rgba(128,128,128,0.3)", + "zerolinecolor": INK_SOFT, "zerolinewidth": 1, + "showline": False, # no left spine (y labels hidden anyway) + "mirror": False, + }, + legend={ + "font": {"size": 18, "color": INK_SOFT}, + "bgcolor": ELEVATED_BG, + "bordercolor": INK_SOFT, + "borderwidth": 1, + "orientation": "h", + "yanchor": "top", + "y": -0.12, + "xanchor": "center", + "x": 0.5, }, - legend={"font": {"size": 22}, "orientation": "h", "yanchor": "bottom", "y": 1.02, "xanchor": "center", "x": 0.5}, - template="plotly_white", - plot_bgcolor="white", - paper_bgcolor="white", + paper_bgcolor=PAGE_BG, + plot_bgcolor=PAGE_BG, hovermode="x unified", - margin={"l": 100, "r": 50, "t": 120, "b": 80}, + margin={"l": 60, "r": 50, "t": 140, "b": 120}, ) -# Save as PNG (4800x2700 px) -fig.write_image("plot.png", width=1600, height=900, scale=3) - -# Save interactive HTML -fig.write_html("plot.html", include_plotlyjs="cdn") +# Save +fig.write_image(f"plot-{THEME}.png", width=1600, height=900, scale=3) +fig.write_html(f"plot-{THEME}.html", include_plotlyjs="cdn") diff --git a/plots/streamgraph-basic/metadata/python/plotly.yaml b/plots/streamgraph-basic/metadata/python/plotly.yaml index 2078a4b04a..8a6f6260ae 100644 --- a/plots/streamgraph-basic/metadata/python/plotly.yaml +++ b/plots/streamgraph-basic/metadata/python/plotly.yaml @@ -1,164 +1,179 @@ library: plotly +language: python specification_id: streamgraph-basic created: '2025-12-23T21:54:18Z' -updated: '2025-12-23T22:00:35Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 20472382233 -issue: 0 -python_version: 3.13.11 -library_version: 6.5.0 -preview_url: https://storage.googleapis.com/anyplot-images/plots/streamgraph-basic/plotly/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/streamgraph-basic/plotly/plot.html -quality_score: 92 -impl_tags: - dependencies: [] - techniques: - - html-export - patterns: - - data-generation - - matrix-construction - dataprep: [] - styling: - - alpha-blending +updated: '2026-05-05T03:40:17Z' +generated_by: claude-sonnet +workflow_run: 25355962769 +issue: 856 +python_version: 3.13.13 +library_version: 6.7.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/streamgraph-basic/python/plotly/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/streamgraph-basic/python/plotly/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/streamgraph-basic/python/plotly/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/streamgraph-basic/python/plotly/plot-dark.html +quality_score: 88 review: strengths: - - Excellent implementation of centered baseline algorithm creating true streamgraph - appearance - - Smooth spline curves create the flowing, organic look specified - - Python Blue/Yellow color scheme with harmonious colorblind-safe palette - - Proper use of Plotly fill=toself for creating the stacked areas - - Both PNG and HTML outputs generated correctly - - Well-structured code following KISS principles - - Good font sizing for all text elements at target resolution + - Perfect spec compliance — streamgraph with centered baseline, smooth splines, + correct data mapping + - 'Flawless theme adaptation: all chrome tokens (INK, INK_SOFT, GRID, PAGE_BG, ELEVATED_BG) + correctly applied in both themes' + - 'Excellent code quality: clean, flat, reproducible, idiomatic Plotly' + - 'Correct Okabe-Ito order starting with #009E73 for Pop' + - 'Appropriate use of Plotly-specific features: fill-hover interaction and HTML + export' weaknesses: - - Y-axis label Streaming Hours (Millions) is slightly confusing when the centered - baseline creates negative values - image_description: 'The plot displays a streamgraph showing monthly streaming hours - by music genre (Pop, Rock, Hip-Hop, Electronic, Jazz, Classical) over a 2-year - period from early 2022 to late 2023. The streamgraph uses a centered baseline - creating a symmetric, river-like appearance around the x-axis. Colors used are: - Python Blue (#306998) for Pop at the bottom, Yellow (#FFD43B) for Rock, Red/Orange - (#E24A33) for Hip-Hop, Green (#8EBA42) for Electronic, Purple (#988ED5) for Jazz, - and Light Blue (#348ABD) for Classical at the top. The title "streamgraph-basic - · plotly · pyplots.ai" is centered at the top. The x-axis shows "Month" with quarterly - date labels, and the y-axis shows "Streaming Hours (Millions)" ranging from -100 - to 100. A horizontal legend is positioned above the plot area. The curves are - smooth and flowing, with subtle grid lines in the background.' + - 'DE-01: Opacity-based visual hierarchy (1.0 vs 0.80) is too subtle to create a + perceptible focal point; a stronger emphasis technique would improve storytelling' + - 'DE-03: No clear narrative focal point or annotation to guide the viewer''s eye; + the plot is informative but doesn''t tell a story' + - 'LM-02: hovertemplate is not customized — default tooltip format misses an opportunity + to show formatted hours or genre share percentage' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct theme surface + Chrome: Title "streamgraph-basic · plotly · anyplot.ai" in dark ink (#1A1A17) at 28px; subtitle "Monthly streaming hours by music genre, 2022–2023" in muted secondary color; x-axis label "Month" clearly visible; tick labels (Jan 2022 … Oct 2023) readable at 18px; y-axis tick labels intentionally hidden + Data: Six flowing streams from bottom to top — Pop (#009E73), Rock (#D55E00), Hip-Hop (#0072B2), Electronic (#CC79A7), Jazz (#E69F00), Classical (#56B4E9); smooth spline curves; symmetric centered baseline with visible zero line; horizontal legend below chart + Legibility verdict: PASS — all text elements clearly readable against the warm off-white background + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct dark theme surface + Chrome: Title, subtitle, axis labels, tick labels, and legend text all rendered in light warm colors (near-white/warm gray); legend uses elevated dark background (#242420) with light border; zero line and subtle grid visible against dark surface + Data: Stream colors identical to light render — #009E73, #D55E00, #0072B2, #CC79A7, #E69F00, #56B4E9 — confirming only chrome flips between themes + Legibility verdict: PASS — no dark-on-dark failures; all text clearly readable against the near-black background criteria_checklist: visual_quality: - score: 37 - max: 40 + score: 30 + max: 30 items: - id: VQ-01 name: Text Legibility - score: 10 - max: 10 + score: 8 + max: 8 passed: true - comment: Title at 36pt, axis labels at 28pt, ticks at 22pt - all perfectly - readable + comment: Title 28px, axis labels 22px, tick labels 18px; all clearly readable + in both themes - id: VQ-02 name: No Overlap - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: No overlapping text, legend is well-spaced + comment: Streams, legend items, and tick labels all non-overlapping - id: VQ-03 name: Element Visibility - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: Stream layers are clearly visible with good distinction + comment: All 6 genre streams clearly distinct and visible in both themes - id: VQ-04 name: Color Accessibility - score: 5 - max: 5 + score: 2 + max: 2 passed: true - comment: Uses Python Blue/Yellow with colorblind-safe palette, good contrast - between adjacent areas + comment: Okabe-Ito palette; CVD-safe by design - id: VQ-05 - name: Layout Balance - score: 5 - max: 5 + name: Layout & Canvas + score: 4 + max: 4 passed: true - comment: Plot fills canvas well, balanced margins + comment: Good 16:9 proportions, generous margins, legend well-placed below - id: VQ-06 - name: Axis Labels - score: 1 + name: Axis Labels & Title + score: 2 max: 2 - passed: false - comment: Y-axis has units "(Millions)" but could be clearer (streaming hours - is somewhat abstract for negative values) + passed: true + comment: Month x-axis label present; y-axis labels intentionally hidden for + streamgraph - id: VQ-07 - name: Grid & Legend + name: Palette Compliance score: 2 max: 2 passed: true - comment: Grid is subtle (alpha 0.2), legend well positioned above plot + comment: 'First series Pop=#009E73; Okabe-Ito order; backgrounds #FAF8F1/#1A1A17 + correct' + design_excellence: + score: 11 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: Opacity hierarchy (Pop 1.0 vs others 0.80), smooth splines, zero-line + anchor, subtitle typography — above defaults but opacity difference barely + perceptible + - id: DE-02 + name: Visual Refinement + score: 3 + max: 6 + passed: true + comment: Bottom-only x spine, y spine removed, y-tick labels hidden, adaptive + subtle grid — clean but not highly refined + - id: DE-03 + name: Data Storytelling + score: 3 + max: 6 + passed: true + comment: Centered baseline correctly shows flow; subtle Pop emphasis present + but not strong enough for clear focal point 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 streamgraph with centered baseline - - id: SC-02 - name: Data Mapping score: 5 max: 5 passed: true - comment: Time on X, categories stacked, values determine area height - - id: SC-03 + comment: Correct streamgraph with symmetric centered baseline using cumsum + offset + - id: SC-02 name: Required Features - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Smooth curves (spline), centered baseline, distinct colors, legend - present - - id: SC-04 - name: Data Range + comment: Smooth spline interpolation, centered baseline, distinct colors, + legend — all present + - id: SC-03 + name: Data Mapping score: 3 max: 3 passed: true - comment: All data visible within axis range - - id: SC-05 - name: Legend Accuracy - score: 2 - max: 2 - passed: true - comment: All 6 genres correctly labeled - - id: SC-06 - name: Title Format - score: 2 - max: 2 + comment: X=time (months), Y=centered stacked values, all 6 categories present + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 passed: true - comment: 'Uses exact format: "streamgraph-basic · plotly · pyplots.ai"' + comment: Title 'streamgraph-basic · plotly · anyplot.ai'; legend labels match + genre names data_quality: - score: 18 - max: 20 + score: 15 + max: 15 items: - id: DQ-01 name: Feature Coverage - score: 7 - max: 8 + score: 6 + max: 6 passed: true - comment: 'Shows variation over time, different category sizes, seasonal patterns - - minor: all categories follow similar trends' + comment: 'All core streamgraph aspects: stacking, centering, smooth flow, + multi-category composition' - id: DQ-02 name: Realistic Context - score: 7 - max: 7 + score: 5 + max: 5 passed: true - comment: Music streaming by genre is a plausible, comprehensible scenario + comment: Music genre streaming hours — well-known, neutral, real-world streamgraph + use case - id: DQ-03 name: Appropriate Scale score: 4 - max: 5 - passed: false - comment: Values are reasonable but "millions" unit on y-axis is somewhat arbitrary - for example data + max: 4 + passed: true + comment: 24 time points (2 years monthly), 6 genres (within 3-8 spec), base + values 12-45 hrs plausible code_quality: score: 10 max: 10 @@ -168,40 +183,63 @@ review: score: 3 max: 3 passed: true - comment: Simple imports → data → plot → save structure, no functions/classes + comment: No functions or classes; flat, readable linear script - 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 before data generation - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only numpy, pandas, plotly.graph_objects used + comment: sys, os, numpy, pandas, plotly.graph_objects — all used - id: CQ-04 - name: No Deprecated API - score: 1 - max: 1 + name: Code Elegance + score: 2 + max: 2 passed: true - comment: All APIs are current + comment: Clean iteration over genres, appropriate numpy vectorization for + baseline computation - id: CQ-05 - name: Output Correct + name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot.png and plot.html - library_features: - score: 5 - max: 5 + comment: Saves plot-{THEME}.png (1600x900 scale=3 = 4800x2700) and plot-{THEME}.html + library_mastery: + score: 7 + max: 10 items: - - id: LF-01 - name: Uses distinctive library features - score: 5 + - id: LM-01 + name: Idiomatic Usage + score: 4 max: 5 passed: true - comment: Uses plotly's spline smoothing, interactive hover, HTML export, go.Scatter - with fill="toself" + comment: go.Scatter with fill=toself, line.shape=spline, hovermode=x unified, + hoveron=fills — idiomatic Plotly area chart patterns + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: Interactive hover on area fills and HTML export present; hovertemplate + not customized — misses showcase of Plotly's tooltip formatting advantage verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - hover-tooltips + - html-export + patterns: + - data-generation + - iteration-over-groups + - matrix-construction + dataprep: + - cumulative-sum + - time-series + styling: + - alpha-blending + - grid-styling