diff --git a/plots/waffle-basic/implementations/python/seaborn.py b/plots/waffle-basic/implementations/python/seaborn.py index 5870e3a464..969c6bea2d 100644 --- a/plots/waffle-basic/implementations/python/seaborn.py +++ b/plots/waffle-basic/implementations/python/seaborn.py @@ -1,7 +1,7 @@ -""" anyplot.ai +"""anyplot.ai waffle-basic: Basic Waffle Chart -Library: seaborn 0.13.2 | Python 3.13.13 -Quality: 85/100 | Updated: 2026-05-05 +Library: seaborn 0.13.2 | Python 3.13.14 +Quality: 89/100 | Updated: 2026-07-26 """ import os @@ -20,87 +20,94 @@ ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" + +sns.set_theme( + style="white", + rc={ + "figure.facecolor": PAGE_BG, + "axes.facecolor": PAGE_BG, + "text.color": INK, + "legend.facecolor": ELEVATED_BG, + "legend.edgecolor": INK_SOFT, + }, +) -# Data - Budget allocation example -categories = ["Housing", "Food", "Transportation", "Utilities", "Entertainment"] -values = [35, 25, 20, 12, 8] # Percentages, sum to 100 +# Data - customer satisfaction survey (n=500 respondents) +categories = ["Very Satisfied", "Satisfied", "Neutral", "Dissatisfied", "Very Dissatisfied"] +values = [42, 31, 15, 8, 4] # Percentages, sum to 100 -# Okabe-Ito palette - first series always #009E73 (brand) -okabe_ito = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] -colors = okabe_ito[: len(categories)] +# Imprint categorical palette — canonical order, first series always #009E73 (brand) +IMPRINT_PALETTE = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] +colors = IMPRINT_PALETTE[: len(categories)] -# Grid dimensions (10x10 = 100 squares for percentage representation) +# Grid dimensions (10x10 = 100 squares, each square = 1%) grid_size = 10 total_squares = grid_size * grid_size -# Create grid data - filling bottom-to-top (like filling a glass) +# Fill bottom-to-top, left-to-right within each row (like filling a glass) grid = np.zeros((grid_size, grid_size), dtype=int) square_idx = 0 - for cat_idx, value in enumerate(values): for _ in range(value): - if square_idx < total_squares: - # Fill from bottom-left, going right then up - row = grid_size - 1 - (square_idx // grid_size) - col = square_idx % grid_size - grid[row, col] = cat_idx - square_idx += 1 - -# Create DataFrame for seaborn heatmap + row = grid_size - 1 - (square_idx // grid_size) + col = square_idx % grid_size + grid[row, col] = cat_idx + square_idx += 1 + rows, cols = np.meshgrid(range(grid_size), range(grid_size), indexing="ij") df = pd.DataFrame({"row": rows.flatten(), "col": cols.flatten(), "category": grid.flatten()}) +pivot_data = df.pivot(index="row", columns="col", values="category") -# Create plot (square format better for waffle chart) -fig, ax = plt.subplots(figsize=(16, 16), facecolor=PAGE_BG) -ax.set_facecolor(PAGE_BG) +# Square canvas (10x10 grid is symmetric) — 6in x 6in @ dpi=400 -> 2400x2400 px. +# No bbox_inches='tight' / tight_layout(): manual axes placement keeps the canvas exact. +fig = plt.figure(figsize=(6, 6), dpi=400, facecolor=PAGE_BG) -# Create a pivot table for the heatmap-style display -pivot_data = df.pivot(index="row", columns="col", values="category") +# Square axes, centered horizontally, room reserved above for title/caption and +# below for the legend. +ax = fig.add_axes([0.19, 0.20, 0.62, 0.62]) +ax.set_facecolor(PAGE_BG) -# Create custom colormap from Okabe-Ito colors cmap = ListedColormap(colors) - -# Plot using seaborn heatmap sns.heatmap( pivot_data, cmap=cmap, vmin=0, vmax=len(categories) - 1, cbar=False, - linewidths=2, + linewidths=2.5, linecolor=PAGE_BG, square=True, ax=ax, ) -# Remove axis labels and ticks ax.set_xlabel("") ax.set_ylabel("") ax.set_xticks([]) ax.set_yticks([]) - -# Style the spines -for spine in ax.spines.values(): - spine.set_visible(True) - spine.set_color(INK_SOFT) +sns.despine(ax=ax, left=True, bottom=True, top=True, right=True) # clean floating grid, no frame # Title -ax.set_title("waffle-basic · seaborn · anyplot.ai", fontsize=24, fontweight="medium", color=INK, pad=30) +fig.text( + 0.5, 0.95, "waffle-basic · python · seaborn · anyplot.ai", ha="center", fontsize=12, fontweight="bold", color=INK +) + +# Caption — spells out the waffle-chart metaphor (each square = one unit of the whole) +fig.text(0.5, 0.885, "Each square = 1% of 500 survey respondents", ha="center", fontsize=8, color=INK_MUTED) -# Create legend with category names and percentages +# Legend with category names and percentages legend_handles = [ - mpatches.Patch(color=colors[i], label=f"{categories[i]} ({values[i]}%)") for i in range(len(categories)) + mpatches.Patch(facecolor=colors[i], edgecolor=PAGE_BG, label=f"{categories[i]} ({values[i]}%)") + for i in range(len(categories)) ] -ax.legend( +fig.legend( handles=legend_handles, - loc="upper center", - bbox_to_anchor=(0.5, -0.02), + loc="lower center", + bbox_to_anchor=(0.5, 0.03), ncol=3, - fontsize=18, + fontsize=8, frameon=False, - facecolor=ELEVATED_BG, labelcolor=INK, ) -plt.tight_layout() -plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG) +plt.savefig(f"plot-{THEME}.png", dpi=400, facecolor=PAGE_BG) diff --git a/plots/waffle-basic/metadata/python/seaborn.yaml b/plots/waffle-basic/metadata/python/seaborn.yaml index 7b0f0b0dd3..318eb22df7 100644 --- a/plots/waffle-basic/metadata/python/seaborn.yaml +++ b/plots/waffle-basic/metadata/python/seaborn.yaml @@ -2,120 +2,137 @@ library: seaborn language: python specification_id: waffle-basic created: '2025-12-24T09:51:26Z' -updated: '2026-05-05T18:14:29Z' -generated_by: claude-haiku -workflow_run: 25393294952 +updated: '2026-07-26T10:28:10Z' +generated_by: claude-sonnet +workflow_run: 30198068571 issue: 998 -python_version: 3.13.13 +language_version: 3.13.14 library_version: 0.13.2 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/waffle-basic/python/seaborn/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waffle-basic/python/seaborn/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 85 +quality_score: 89 review: strengths: - - Correct 10x10 waffle grid with 1% per square representation - - Perfect Okabe-Ito palette compliance with brand green first - - Theme-adaptive styling works flawlessly in both light and dark renders - - Legend clearly shows category names and percentages - - All text readable in both themes - - Realistic and neutral data example + - Clever repurposing of sns.heatmap with a ListedColormap and manual grid construction + to render a native seaborn waffle chart, since the library has no dedicated waffle + function + - Floating-grid effect (linecolor=PAGE_BG, linewidths=2.5) with all spines removed + gives clean square separation without a heavy frame + - Deliberate bottom-to-top 'filling a glass' fill order anchors the dominant category + (Very Satisfied, 42%) at the base, giving the grid a visual hierarchy beyond a + flat category dump + - 'Correct Imprint categorical palette in canonical order with brand green #009E73 + first, identical across both theme renders' + - 'Theme-adaptive chrome is fully correct in both renders: warm off-white/near-black + backgrounds, readable ink/soft-ink text, no dark-on-dark or light-on-light failures' + - Legend cleanly lists each category with its percentage, satisfying the spec's + requirement to identify categories with their percentages + - Deterministic, KISS-structured code with clean imports and no fake interactivity weaknesses: - - Design sophistication limited to functional requirements - - No visual refinement beyond basic heatmap styling - - Spines remain visible without creative L-shape or spine removal - - No distinctive seaborn features leveraged + - 'Title omits the mandated language segment: it renders ''waffle-basic · seaborn + · anyplot.ai'' but the required format is ''{spec-id} · {language} · {library} + · anyplot.ai'' -> should read ''waffle-basic · python · seaborn · anyplot.ai''. + Fix the fig.text() string literal on the title line.' + - 'Design Excellence is solid but not exceptional: the composition is clean yet + fairly minimal (flat color blocks, no secondary emphasis like a callout on the + largest segment) - stops short of publication-grade polish.' + - 'Library Mastery is only moderate: sns.heatmap is being used as a workaround for + a chart type seaborn doesn''t natively support, which is a smart trick but still + a generic underlying primitive rather than a seaborn-distinctive feature.' image_description: |- Light render (plot-light.png): - Background: Warm off-white surface (#FAF8F1) — correct theme background - Chrome: Title "waffle-basic · seaborn · anyplot.ai" in dark text at top (clearly readable); legend at bottom with category names and percentages in dark text on light legend background (clearly readable) - Data: 10x10 grid with squares colored in Okabe-Ito order — green (#009E73) for Housing dominates bottom section, orange (#D55E00) for Food in middle-bottom, blue (#0072B2) for Transportation in middle, pink (#CC79A7) for Utilities upper-middle, yellow (#E69F00) for Entertainment at top. Grid lines are dark and clearly visible between all squares. - Legibility verdict: PASS — all text is readable; no "light on light" conflicts; gridlines clearly distinguish squares + Background: Warm off-white, consistent with #FAF8F1, not pure white and not dark. + Chrome: Bold dark title "waffle-basic · seaborn · anyplot.ai" at top, clearly legible; muted-gray caption "Each square = 1% of 500 survey respondents" below it, also legible; a 10x10 grid of colored squares with thin off-white gutters between them (no axes, no spines); a two-row legend below the grid with color swatches and category + percentage labels in dark ink. + Data: Five colors fill the grid — brand green (#009E73, Very Satisfied 42%), lavender (#C475FD, Satisfied 31%), blue (#4467A3, Neutral 15%), ochre (#BD8233, Dissatisfied 8%), matte red (#AE3030, Very Dissatisfied 4%) — first series is confirmed brand green, and the fill runs bottom-up so the majority category anchors the base. + Legibility verdict: PASS — all text (title, caption, legend) is clearly readable against the light background, no light-on-light issues. Dark render (plot-dark.png): - Background: Warm near-black surface (#1A1A17) — correct theme background - Chrome: Title in light text (clearly readable against dark background); legend at bottom with light text on dark elevated background (clearly readable); no dark-on-dark failures detected - Data: Identical 10x10 grid with squares in same positions and identical colors to light render (green, orange, blue, pink, yellow); grid lines are light/white, providing appropriate contrast against dark background. Brand green (#009E73) Housing squares are distinctly visible and accessible. - Legibility verdict: PASS — all text is readable in light colors appropriate for dark theme; no legibility failures + Background: Warm near-black, consistent with #1A1A17, not pure black and not light. + Chrome: Same title now rendered in light ink, caption in muted light gray, legend text in light ink — all clearly legible against the dark surface. + Data: Identical five colors (green/lavender/blue/ochre/red) in the identical grid layout — confirms the Imprint data colors are unchanged between themes, only chrome flipped. + Legibility verdict: PASS — no dark-on-dark failures; every text element (title, caption, legend) remains fully legible on the near-black background. criteria_checklist: visual_quality: - score: 30 + score: 29 max: 30 items: - id: VQ-01 name: Text Legibility - score: 8 + score: 7 max: 8 passed: true - comment: Title (24pt) and legend (18pt) fonts clearly visible in both themes; - readable at full resolution + comment: Explicit font sizes (title 12pt bold, caption 8pt, legend 8pt); readable + in both themes; title width proportion is appropriate for the shorter (no-descriptive-prefix) + mandated title - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: Grid is clean with no collisions; legend positioned clearly below - grid + comment: Grid, caption, and legend are cleanly separated with no collisions - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: All 100 squares clearly distinguished by color and gridlines; density-appropriate + comment: Squares are large and clearly distinguishable, appropriate for a + 10x10 grid - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Okabe-Ito palette is CVD-safe; no red-green as sole signal + comment: Imprint palette is CVD-safe; five distinguishable hues, no red-green-only + signaling - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Square format (16x16 figsize) ideal for waffle chart; excellent proportions; - nothing cut off + comment: Balanced margins, grid comfortably fills the square canvas, legend + sits close to the grid, nothing clipped - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: 'Title format correct: ''waffle-basic · seaborn · anyplot.ai''; no - axis labels (appropriate for waffle)' + comment: No axes needed for a waffle chart; title and caption are fully descriptive - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series #009E73; Okabe-Ito order; backgrounds #FAF8F1/#1A1A17; - both renders theme-correct' + comment: 'First series is #009E73, remaining series follow canonical Imprint + order, data colors identical across themes, backgrounds theme-correct' design_excellence: - score: 10 + score: 15 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 4 + score: 6 max: 8 - passed: false - comment: Generic heatmap approach; no custom design sophistication beyond - requirements + passed: true + comment: Custom palette, deliberate typography hierarchy, floating-grid treatment + — clearly above library defaults but not fully publication-grade - id: DE-02 name: Visual Refinement - score: 3 + score: 5 max: 6 - passed: false - comment: Spines visible and styled with INK_SOFT; functional but minimal refinement + passed: true + comment: All spines removed, generous whitespace, subtle off-white gutters + between squares, frameless legend - id: DE-03 name: Data Storytelling - score: 3 + score: 4 max: 6 - passed: false - comment: Waffle format clearly shows proportions; legend adds context; no - special emphasis + passed: true + comment: Bottom-up 'filling a glass' order anchors the dominant category and + creates visual hierarchy, though no explicit annotation calls out the insight spec_compliance: - score: 15 + score: 13 max: 15 items: - id: SC-01 @@ -123,27 +140,28 @@ review: score: 5 max: 5 passed: true - comment: Correct 10x10 waffle grid; 1% per square representation + comment: Correct 10x10 waffle grid, one square per percentage point - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Categories, values, distinct colors, legend with percentages all - present + comment: Distinct colors, legend with percentages, values sum to 100, whole-square + rounding - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Categories map correctly; values render as correct square counts - (35,25,20,12,8) + comment: Each category maps to exactly its percentage count of squares - id: SC-04 name: Title & Legend - score: 3 + score: 1 max: 3 - passed: true - comment: Title and legend format correct; all categories with percentages + passed: false + comment: 'Title is missing the mandated language segment: renders ''waffle-basic + · seaborn · anyplot.ai'' instead of ''waffle-basic · python · seaborn · + anyplot.ai''; legend labels/percentages are correct' data_quality: score: 15 max: 15 @@ -153,20 +171,21 @@ review: score: 6 max: 6 passed: true - comment: 'All aspects of waffle plot type present: grid, proportions, categories, - colors' + comment: Five categories spanning the full satisfaction spectrum with varied + proportions - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Budget allocation realistic and neutral; percentages plausible + comment: Neutral, plausible customer-satisfaction survey (n=500) - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Percentages match 10x10 grid perfectly; 1% = 1 square + comment: Percentages sum exactly to 100 with a realistic satisfaction-skewed + distribution code_quality: score: 10 max: 10 @@ -176,33 +195,36 @@ review: score: 3 max: 3 passed: true - comment: No functions/classes; straightforward direct plot generation + comment: Linear imports -> theme -> data -> grid construction -> plot -> save, + no functions/classes - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Deterministic hardcoded data; no randomness + comment: Fully deterministic, hardcoded values, no randomness - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: All imports used; no unused dependencies + comment: All imports (os, mpatches, plt, numpy, pandas, seaborn, ListedColormap) + are used - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Appropriate complexity; no fake UI + comment: Appropriate complexity for constructing the grid; no fake functionality - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot-{THEME}.png; current API + comment: Saves as plot-{THEME}.png with default bbox_inches, no deprecated + API library_mastery: - score: 5 + score: 7 max: 10 items: - id: LM-01 @@ -210,22 +232,28 @@ review: score: 4 max: 5 passed: true - comment: Proper seaborn heatmap usage; theme tokens correctly implemented + comment: sns.heatmap, sns.set_theme, and sns.despine used correctly and idiomatically + as the vehicle for the waffle grid - id: LM-02 name: Distinctive Features - score: 1 + score: 3 max: 5 - passed: false - comment: No distinctive seaborn features; straightforward heatmap application - verdict: APPROVED + passed: true + comment: Repurposing sns.heatmap + ListedColormap for a non-heatmap chart + type is a seaborn-specific workaround, though the underlying primitive is + generic + verdict: REJECTED impl_tags: dependencies: [] techniques: - custom-legend + - patches patterns: - - data-generation - matrix-construction - - explicit-figure + - iteration-over-groups + - long-to-wide dataprep: [] styling: - - custom-colormap + - publication-ready + - minimal-chrome + - grid-styling