From 2b6ded62139f105548771b264cf4d8ff58d39583 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 05:27:11 +0000 Subject: [PATCH 1/4] chore(seaborn): add metadata for funnel-basic --- .../implementations/python/seaborn.py | 112 +++++----- .../funnel-basic/metadata/python/seaborn.yaml | 200 ++---------------- 2 files changed, 71 insertions(+), 241 deletions(-) diff --git a/plots/funnel-basic/implementations/python/seaborn.py b/plots/funnel-basic/implementations/python/seaborn.py index 22234c6e1f..ce91c7e3bc 100644 --- a/plots/funnel-basic/implementations/python/seaborn.py +++ b/plots/funnel-basic/implementations/python/seaborn.py @@ -1,111 +1,109 @@ -""" pyplots.ai +"""anyplot.ai funnel-basic: Basic Funnel Chart -Library: seaborn 0.13.2 | Python 3.13.11 -Quality: 91/100 | Created: 2025-12-23 +Library: seaborn | Python 3.13 +Quality: pending | Created: 2026-04-26 """ +import os + +import matplotlib.patheffects as pe import matplotlib.pyplot as plt -import numpy as np import seaborn as sns from matplotlib.patches import Polygon -# Set seed for reproducibility -np.random.seed(42) +# Theme tokens +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" + +# Okabe-Ito palette — first series always #009E73 +OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] -# Data - Sales funnel example from specification +# Data — sales funnel from specification stages = ["Awareness", "Interest", "Consideration", "Intent", "Purchase"] values = [1000, 600, 400, 200, 100] max_value = values[0] - -# Calculate percentages percentages = [v / max_value * 100 for v in values] -# Seaborn styling -sns.set_theme(style="white") - -# Create figure -fig, ax = plt.subplots(figsize=(16, 9)) +# Seaborn theme (chrome only — funnel polygons drawn directly) +sns.set_theme(style="white", rc={"figure.facecolor": PAGE_BG, "axes.facecolor": PAGE_BG, "text.color": INK}) -# Color palette using Python Blue to Gold progression -colors = sns.color_palette(["#306998", "#4078A8", "#6AA8D1", "#FFD43B", "#E8C547"]) +# Plot +fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) +ax.set_facecolor(PAGE_BG) -# Funnel parameters +# Funnel geometry n_stages = len(stages) -funnel_height = 0.8 # Total height of funnel -stage_gap = 0.02 # Gap between stages +funnel_height = 0.78 +stage_gap = 0.015 stage_height = (funnel_height - (n_stages - 1) * stage_gap) / n_stages -center_x = 0.5 +center_x = 0.55 # shift right to leave room for stage names on left +max_width = 0.55 -# Draw trapezoidal funnel segments +# Draw trapezoidal segments for i in range(n_stages): - # Calculate widths - proportional to value relative to max - top_width = values[i] / max_value * 0.8 - # Bottom width is the next stage's width, or smaller for last stage + top_width = values[i] / max_value * max_width if i < n_stages - 1: - bottom_width = values[i + 1] / max_value * 0.8 + bottom_width = values[i + 1] / max_value * max_width else: - bottom_width = values[i] / max_value * 0.8 * 0.6 # Narrower bottom for last stage + bottom_width = top_width * 0.6 - # Calculate y positions (top to bottom) - y_top = 1 - 0.1 - i * (stage_height + stage_gap) + y_top = 1 - 0.12 - i * (stage_height + stage_gap) y_bottom = y_top - stage_height - # Create trapezoid vertices (clockwise from top-left) vertices = [ - (center_x - top_width / 2, y_top), # Top-left - (center_x + top_width / 2, y_top), # Top-right - (center_x + bottom_width / 2, y_bottom), # Bottom-right - (center_x - bottom_width / 2, y_bottom), # Bottom-left + (center_x - top_width / 2, y_top), + (center_x + top_width / 2, y_top), + (center_x + bottom_width / 2, y_bottom), + (center_x - bottom_width / 2, y_bottom), ] - - # Draw trapezoid using matplotlib Polygon - trapezoid = Polygon(vertices, facecolor=colors[i], edgecolor="white", linewidth=3, closed=True) + trapezoid = Polygon(vertices, facecolor=OKABE_ITO[i], edgecolor=PAGE_BG, linewidth=3, closed=True) ax.add_patch(trapezoid) - # Calculate center of trapezoid for label placement center_y = (y_top + y_bottom) / 2 - # Add stage name on the left - ax.text( - center_x - top_width / 2 - 0.05, + # Stage name on the left (fixed x to avoid collision with value text in narrow segments) + ax.text(0.20, center_y, stages[i], ha="right", va="center", fontsize=20, fontweight="medium", color=INK) + + # Value + percentage centered on the segment. Path-effect stroke keeps the white + # text readable when narrow segments force it to overflow onto the page background. + value_text = ax.text( + center_x, center_y, - stages[i], - ha="right", + f"{values[i]:,} ({percentages[i]:.0f}%)", + ha="center", va="center", - fontsize=20, + fontsize=18, fontweight="bold", - color="#333333", + color="#FFFFFF", ) + value_text.set_path_effects([pe.withStroke(linewidth=2.5, foreground=OKABE_ITO[i])]) - # Add value and percentage label in center - label_text = f"{values[i]:,} ({percentages[i]:.0f}%)" - # Choose text color based on background brightness - text_color = "white" if i < 3 else "#333333" - ax.text(center_x, center_y, label_text, ha="center", va="center", fontsize=18, fontweight="bold", color=text_color) - - # Add conversion rate between stages + # Conversion rate between stages — placed inside the gap, right of the funnel if i < n_stages - 1: conversion_rate = values[i + 1] / values[i] * 100 ax.text( - center_x + top_width / 2 + 0.05, - y_bottom, + center_x + max_width / 2 + 0.04, + y_bottom - stage_gap / 2, f"↓ {conversion_rate:.0f}%", ha="left", va="center", fontsize=14, - color="#666666", + color=INK_MUTED, style="italic", ) -# Set axis limits and remove decorations +# Axis frame ax.set_xlim(0, 1) ax.set_ylim(0, 1) ax.set_aspect("equal") ax.axis("off") # Title -ax.set_title("funnel-basic · seaborn · pyplots.ai", fontsize=24, fontweight="bold", pad=20) +ax.set_title("funnel-basic · seaborn · anyplot.ai", fontsize=24, fontweight="medium", color=INK, pad=20) plt.tight_layout() -plt.savefig("plot.png", dpi=300, bbox_inches="tight", facecolor="white") +plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG) diff --git a/plots/funnel-basic/metadata/python/seaborn.yaml b/plots/funnel-basic/metadata/python/seaborn.yaml index c2ed5205b0..b9481c4fae 100644 --- a/plots/funnel-basic/metadata/python/seaborn.yaml +++ b/plots/funnel-basic/metadata/python/seaborn.yaml @@ -1,189 +1,21 @@ +# Per-library metadata for seaborn implementation of funnel-basic +# Auto-generated by impl-generate.yml + library: seaborn +language: python specification_id: funnel-basic created: '2025-12-23T13:04:46Z' -updated: '2025-12-23T13:38:26Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 20461374089 -issue: 0 -python_version: 3.13.11 +updated: '2026-04-26T05:27:10Z' +generated_by: claude-opus +workflow_run: 24949059229 +issue: 789 +python_version: 3.14.4 library_version: 0.13.2 -preview_url: https://storage.googleapis.com/anyplot-images/plots/funnel-basic/seaborn/plot.png -preview_html: null -quality_score: 91 -impl_tags: - dependencies: [] - techniques: - - patches - - annotations - patterns: - - data-generation - dataprep: [] - styling: - - minimal-chrome +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/funnel-basic/python/seaborn/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/funnel-basic/python/seaborn/plot-dark.png +preview_html_light: null +preview_html_dark: null +quality_score: null review: - strengths: - - Excellent visual design with well-proportioned trapezoidal segments - - Clear blue-to-gold color progression that aids visual hierarchy - - 'Perfect text contrast: white text on blue, dark text on gold backgrounds' - - Uses exact data from specification (sales funnel example) - - Clean code structure following KISS principles - - Proper title format matching specification requirements - weaknesses: - - Conversion rate annotations (coded in lines 88-99) are not visible in the rendered - output - may be positioned outside canvas bounds - - Could use seaborn color palette more distinctively (e.g., using a named sequential - palette) - image_description: 'The plot displays a funnel chart for a sales pipeline with 5 - stages arranged vertically from top to bottom: Awareness (1,000 / 100%), Interest - (600 / 60%), Consideration (400 / 40%), Intent (200 / 20%), and Purchase (100 - / 10%). Each stage is rendered as a trapezoidal segment with widths proportional - to their values. The color palette transitions from deep blue (#306998) at the - top through lighter blues to gold/yellow (#FFD43B, #E8C547) at the bottom. Stage - names appear on the left side in bold dark gray text, while values with percentages - are centered within each segment in white (for blue segments) or dark text (for - gold segments). The title "funnel-basic · seaborn · pyplots.ai" appears at the - top. The layout is clean with a white background and good spacing between segments.' - criteria_checklist: - visual_quality: - score: 36 - max: 40 - items: - - id: VQ-01 - name: Text Legibility - score: 10 - max: 10 - passed: true - comment: Title at 24pt, stage names at 20pt, values at 18pt - all perfectly - readable - - id: VQ-02 - name: No Overlap - score: 8 - max: 8 - passed: true - comment: No overlapping text, all labels clearly separated - - id: VQ-03 - name: Element Visibility - score: 8 - max: 8 - passed: true - comment: Trapezoid segments are well-sized and clearly visible - - id: VQ-04 - name: Color Accessibility - score: 4 - max: 5 - passed: true - comment: Blue-to-gold gradient is colorblind-friendly, though the gold shades - are similar - - id: VQ-05 - name: Layout Balance - score: 4 - max: 5 - passed: true - comment: Good use of canvas, funnel is well-centered, though conversion rate - annotations on the right are missing from the rendered output - spec_compliance: - score: 23 - max: 25 - items: - - id: SC-01 - name: Plot Type - score: 8 - max: 8 - passed: true - comment: Correct funnel chart with trapezoidal segments narrowing top to bottom - - id: SC-02 - name: Data Mapping - score: 5 - max: 5 - passed: true - comment: Stages correctly ordered from largest to smallest - - id: SC-03 - name: Required Features - score: 4 - max: 5 - passed: true - comment: Has value labels, percentages, distinct colors; conversion rates - coded but not visible in output - - id: SC-04 - name: Data Range - score: 3 - max: 3 - passed: true - comment: All 5 stages displayed with correct proportions - - id: SC-06 - name: Title Format - score: 2 - max: 2 - passed: true - comment: 'Correct format: "funnel-basic · seaborn · pyplots.ai"' - data_quality: - score: 20 - max: 20 - items: - - id: DQ-01 - name: Feature Coverage - score: 8 - max: 8 - passed: true - comment: Shows progressive decrease across all stages, demonstrates funnel - drop-off pattern well - - id: DQ-02 - name: Realistic Context - score: 7 - max: 7 - passed: true - comment: Uses the exact sales funnel example from the specification - - id: DQ-03 - name: Appropriate Scale - score: 5 - max: 5 - passed: true - comment: Values (1000→100) are realistic for a conversion funnel - code_quality: - score: 9 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: 'Linear flow: imports → data → plot → save, no functions/classes' - - id: CQ-02 - name: Reproducibility - score: 3 - max: 3 - passed: true - comment: Uses np.random.seed(42) - - id: CQ-03 - name: Clean Imports - score: 1 - max: 2 - passed: true - comment: numpy imported but seed not strictly necessary for deterministic - data - - id: CQ-04 - name: No Deprecated API - score: 1 - max: 1 - passed: true - comment: Uses current matplotlib/seaborn APIs - - id: CQ-05 - name: Output Correct - score: 1 - max: 1 - passed: true - comment: Saves as plot.png with correct parameters - library_features: - score: 3 - max: 5 - items: - - id: LF-01 - name: Uses seaborn styling - score: 3 - max: 5 - passed: true - comment: Uses sns.set_theme() and sns.color_palette(), but core drawing uses - matplotlib Polygon; this is acceptable since seaborn doesn't have a native - funnel chart - verdict: APPROVED + strengths: [] + weaknesses: [] From 097fccb532bdaf208e5d2bc43202e8c460cedebc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 05:32:28 +0000 Subject: [PATCH 2/4] chore(seaborn): update quality score 83 and review feedback for funnel-basic --- .../implementations/python/seaborn.py | 6 +- .../funnel-basic/metadata/python/seaborn.yaml | 228 +++++++++++++++++- 2 files changed, 224 insertions(+), 10 deletions(-) diff --git a/plots/funnel-basic/implementations/python/seaborn.py b/plots/funnel-basic/implementations/python/seaborn.py index ce91c7e3bc..27755e6b5a 100644 --- a/plots/funnel-basic/implementations/python/seaborn.py +++ b/plots/funnel-basic/implementations/python/seaborn.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai funnel-basic: Basic Funnel Chart -Library: seaborn | Python 3.13 -Quality: pending | Created: 2026-04-26 +Library: seaborn 0.13.2 | Python 3.14.4 +Quality: 83/100 | Updated: 2026-04-26 """ import os diff --git a/plots/funnel-basic/metadata/python/seaborn.yaml b/plots/funnel-basic/metadata/python/seaborn.yaml index b9481c4fae..f01c911793 100644 --- a/plots/funnel-basic/metadata/python/seaborn.yaml +++ b/plots/funnel-basic/metadata/python/seaborn.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for seaborn implementation of funnel-basic -# Auto-generated by impl-generate.yml - library: seaborn language: python specification_id: funnel-basic created: '2025-12-23T13:04:46Z' -updated: '2026-04-26T05:27:10Z' +updated: '2026-04-26T05:32:28Z' generated_by: claude-opus workflow_run: 24949059229 issue: 789 @@ -15,7 +12,224 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/funnel-ba preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/funnel-basic/python/seaborn/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: null +quality_score: 83 review: - strengths: [] - weaknesses: [] + strengths: + - Perfect spec compliance with all required features present + - Strong theme-adaptive implementation with both renders fully readable + - Elegant path-effects for text legibility on narrow segments + - Clean flat code with no unnecessary abstractions + weaknesses: + - Seaborn usage is minimal — only sns.set_theme() provides seaborn-specific contribution; + no seaborn plot functions called + - Narrowest segments (Intent, Purchase) have value text overflowing outside trapezoid + onto background, relying on path-effect stroke workaround + - Slight empty canvas at the bottom below the Purchase segment + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct, not pure white + Chrome: Title "funnel-basic · seaborn · anyplot.ai" in dark ink (fontsize 24) — clearly readable. Stage names in dark INK (fontsize 20) on left side — all readable. Conversion-rate annotations in muted INK_MUTED on right — readable. + Data: Five trapezoidal segments in Okabe-Ito order — #009E73 (Awareness), #D55E00 (Interest), #0072B2 (Consideration), #CC79A7 (Intent), #E69F00 (Purchase). White bold value+percentage text centered in each segment, protected by colored path-effect strokes. + Legibility verdict: PASS — minor issue: for narrowest segments (Intent, Purchase), text overflows outside trapezoid and relies on colored stroke for legibility on cream background; functional but not polished. + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct, not pure black + Chrome: Title in light near-white text — clearly readable. Stage names in light INK token — readable. Conversion rates in muted gray — readable. No dark-on-dark failures detected. + Data: Identical Okabe-Ito colors to light render — #009E73, #D55E00, #0072B2, #CC79A7, #E69F00. White text on dark background reads more cleanly than in light mode. + Legibility verdict: PASS — all text elements readable in dark theme; white value labels on dark background provide excellent contrast. + criteria_checklist: + visual_quality: + score: 28 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: 'Title 24pt, stage names 20pt, value labels 18pt, conversion rates + 14pt — appropriate sizing. Minor: path-effect text overflow on narrowest + segments in light mode functional but imperfect' + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: Stage names, value labels, and conversion annotations cleanly separated + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: All five segments visible including narrowest Intent and Purchase + stages + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Okabe-Ito palette, CVD-safe, no red-green sole signal + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: Funnel well-proportioned; slight empty space at bottom below Purchase + segment + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Title matches required format; no traditional axis labels needed + for funnel chart + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First stage #009E73, Okabe-Ito order, FAF8F1/1A1A17 backgrounds + correct in both themes' + design_excellence: + score: 11 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: 'Clear design intent: custom palette, path effects, conversion-rate + annotations, typography hierarchy' + - id: DE-02 + name: Visual Refinement + score: 3 + max: 6 + passed: true + comment: Axes hidden, segment gaps add polish; conversion-rate annotations + slightly cramped at right edge + - id: DE-03 + name: Data Storytelling + score: 3 + max: 6 + passed: true + comment: Funnel shape narrates conversion; stage-to-stage percentages add + analytical insight; no emphasis on largest drop-off + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct funnel chart with trapezoidal segments narrowing top to bottom + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Ordered stages largest to smallest, distinct colors, value+percentage + labels, proportional widths — all present + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Sales funnel data [1000,600,400,200,100] exactly as specified + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title funnel-basic · seaborn · anyplot.ai correct; no legend needed + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: 'All funnel aspects shown: widths, names, values, percentages, conversion + rates' + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Sales funnel scenario is real-world, neutral, plausible + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 1000 to 100 with 50-67% conversion rates reflects typical sales pipeline + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: No functions or classes; flat script + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fully deterministic hardcoded data + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: 'All imports used: os, patheffects, pyplot, seaborn, Polygon' + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Appropriate complexity for custom chart type; clean loop; no fake + UI + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves as plot-{THEME}.png with correct theme token + library_mastery: + score: 4 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 3 + max: 5 + passed: true + comment: Uses sns.set_theme() for theme/chrome setup idiomatically; actual + polygon drawing is raw matplotlib — unavoidable given no seaborn funnel + primitive + - id: LM-02 + name: Distinctive Features + score: 1 + max: 5 + passed: false + comment: Seaborn usage limited to set_theme() for color tokens; no seaborn + plot functions called; chart is effectively pure matplotlib + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - patches + - annotations + patterns: + - explicit-figure + - iteration-over-groups + dataprep: [] + styling: + - minimal-chrome From 95d779ee31d93dce100317a6c790a9cd389606d4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 05:46:43 +0000 Subject: [PATCH 3/4] fix(seaborn): address review feedback for funnel-basic Attempt 1/3 - fixes based on AI review - Use sns.barplot + sns.despine substantively (LM-02) - Move value labels outside bars (VQ-01) - Highlight worst drop-off (DE-03) - Closing tail under last stage (VQ-05) --- .../implementations/python/seaborn.py | 195 +++++++++++------- 1 file changed, 124 insertions(+), 71 deletions(-) diff --git a/plots/funnel-basic/implementations/python/seaborn.py b/plots/funnel-basic/implementations/python/seaborn.py index 27755e6b5a..6fb1667cd7 100644 --- a/plots/funnel-basic/implementations/python/seaborn.py +++ b/plots/funnel-basic/implementations/python/seaborn.py @@ -1,12 +1,10 @@ -""" anyplot.ai +"""anyplot.ai funnel-basic: Basic Funnel Chart Library: seaborn 0.13.2 | Python 3.14.4 -Quality: 83/100 | Updated: 2026-04-26 """ import os -import matplotlib.patheffects as pe import matplotlib.pyplot as plt import seaborn as sns from matplotlib.patches import Polygon @@ -22,87 +20,142 @@ # Okabe-Ito palette — first series always #009E73 OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] -# Data — sales funnel from specification +# Sales funnel data stages = ["Awareness", "Interest", "Consideration", "Intent", "Purchase"] values = [1000, 600, 400, 200, 100] max_value = values[0] percentages = [v / max_value * 100 for v in values] +conversions = [values[i + 1] / values[i] * 100 for i in range(len(values) - 1)] +# Stage transition with the largest drop-off (lowest retention) +worst_idx = min(range(len(conversions)), key=conversions.__getitem__) + +sns.set_theme( + style="white", + rc={ + "figure.facecolor": PAGE_BG, + "axes.facecolor": PAGE_BG, + "text.color": INK, + "axes.labelcolor": INK, + "ytick.color": INK, + "xtick.color": INK_SOFT, + }, +) -# Seaborn theme (chrome only — funnel polygons drawn directly) -sns.set_theme(style="white", rc={"figure.facecolor": PAGE_BG, "axes.facecolor": PAGE_BG, "text.color": INK}) - -# Plot fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) -# Funnel geometry -n_stages = len(stages) -funnel_height = 0.78 -stage_gap = 0.015 -stage_height = (funnel_height - (n_stages - 1) * stage_gap) / n_stages -center_x = 0.55 # shift right to leave room for stage names on left -max_width = 0.55 - -# Draw trapezoidal segments -for i in range(n_stages): - top_width = values[i] / max_value * max_width - if i < n_stages - 1: - bottom_width = values[i + 1] / max_value * max_width - else: - bottom_width = top_width * 0.6 - - y_top = 1 - 0.12 - i * (stage_height + stage_gap) - y_bottom = y_top - stage_height - - vertices = [ - (center_x - top_width / 2, y_top), - (center_x + top_width / 2, y_top), - (center_x + bottom_width / 2, y_bottom), - (center_x - bottom_width / 2, y_bottom), - ] - trapezoid = Polygon(vertices, facecolor=OKABE_ITO[i], edgecolor=PAGE_BG, linewidth=3, closed=True) - ax.add_patch(trapezoid) - - center_y = (y_top + y_bottom) / 2 - - # Stage name on the left (fixed x to avoid collision with value text in narrow segments) - ax.text(0.20, center_y, stages[i], ha="right", va="center", fontsize=20, fontweight="medium", color=INK) - - # Value + percentage centered on the segment. Path-effect stroke keeps the white - # text readable when narrow segments force it to overflow onto the page background. - value_text = ax.text( - center_x, - center_y, - f"{values[i]:,} ({percentages[i]:.0f}%)", - ha="center", +# Seaborn draws the rectangular core of each stage; trapezoidal panels added +# below tie the cores into a continuous funnel silhouette in stage colors. +sns.barplot( + x=values, + y=stages, + hue=stages, + order=stages, + palette=OKABE_ITO[: len(stages)], + ax=ax, + legend=False, + width=0.50, + edgecolor="none", +) + +# Center each bar on x=0 so the silhouette narrows symmetrically +bars = list(ax.patches)[: len(stages)] +for patch in bars: + patch.set_x(-patch.get_width() / 2) + +# Trapezoidal panels between stages — each panel inherits the upper stage color +# so visually each stage = rectangle + tapering trapezoid below it. +for i in range(len(bars) - 1): + p_top, p_bot = bars[i], bars[i + 1] + top_y = p_top.get_y() + p_top.get_height() + bot_y = p_bot.get_y() + ax.add_patch( + Polygon( + [ + (p_top.get_x(), top_y), + (p_top.get_x() + p_top.get_width(), top_y), + (p_bot.get_x() + p_bot.get_width(), bot_y), + (p_bot.get_x(), bot_y), + ], + facecolor=OKABE_ITO[i], + edgecolor="none", + zorder=1, + ) + ) + +# Closing tail below the last stage so the funnel ends with a proper taper +last_bar = bars[-1] +last_top_y = last_bar.get_y() + last_bar.get_height() +tail_height = 0.50 +tail_bot_w = last_bar.get_width() * 0.5 +ax.add_patch( + Polygon( + [ + (last_bar.get_x(), last_top_y), + (last_bar.get_x() + last_bar.get_width(), last_top_y), + (tail_bot_w / 2, last_top_y + tail_height), + (-tail_bot_w / 2, last_top_y + tail_height), + ], + facecolor=OKABE_ITO[-1], + edgecolor="none", + zorder=1, + ) +) + +# Emphasise the bar after the worst drop-off with a thicker outline accent +worst_bar = bars[worst_idx + 1] +worst_bar.set_edgecolor(INK) +worst_bar.set_linewidth(2.5) +worst_bar.set_zorder(3) + +# Value + percentage labels are placed OUTSIDE bars (right) so narrow stages +# never overflow onto the page background. +right_offset = max_value * 0.04 +for i, patch in enumerate(bars): + cy = patch.get_y() + patch.get_height() / 2 + x_right = patch.get_x() + patch.get_width() + ax.text( + x_right + right_offset, + cy, + f"{values[i]:,} · {percentages[i]:.0f}%", + ha="left", va="center", fontsize=18, - fontweight="bold", - color="#FFFFFF", + fontweight="medium", + color=INK, ) - value_text.set_path_effects([pe.withStroke(linewidth=2.5, foreground=OKABE_ITO[i])]) - - # Conversion rate between stages — placed inside the gap, right of the funnel - if i < n_stages - 1: - conversion_rate = values[i + 1] / values[i] * 100 - ax.text( - center_x + max_width / 2 + 0.04, - y_bottom - stage_gap / 2, - f"↓ {conversion_rate:.0f}%", - ha="left", - va="center", - fontsize=14, - color=INK_MUTED, - style="italic", - ) -# Axis frame -ax.set_xlim(0, 1) -ax.set_ylim(0, 1) -ax.set_aspect("equal") -ax.axis("off") +# Conversion-rate annotations on the LEFT, with the largest drop-off +# rendered bolder and in full-strength ink for visual emphasis. +left_anchor = -max_value / 2 - max_value * 0.06 +for i in range(len(conversions)): + p_top, p_bot = bars[i], bars[i + 1] + y_mid = (p_top.get_y() + p_top.get_height() + p_bot.get_y()) / 2 + is_worst = i == worst_idx + ax.text( + left_anchor, + y_mid, + f"↓ {conversions[i]:.0f}%", + ha="right", + va="center", + fontsize=16 if is_worst else 13, + fontweight="bold" if is_worst else "normal", + style="italic", + color=INK if is_worst else INK_MUTED, + ) + +# Awareness on top — matplotlib's default places the first category at the bottom +ax.invert_yaxis() +ax.set_ylim(len(stages) - 1 + tail_height + 0.25, -0.45) + +sns.despine(ax=ax, left=True, bottom=True) +ax.set_xticks([]) +ax.set_xlabel("") +ax.set_ylabel("") +ax.tick_params(axis="y", labelsize=20, length=0, pad=10) + +ax.set_xlim(-max_value * 0.95, max_value * 0.85) -# Title ax.set_title("funnel-basic · seaborn · anyplot.ai", fontsize=24, fontweight="medium", color=INK, pad=20) plt.tight_layout() From b3f7c671d495bc13f36fb312543cf16da63d2235 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 05:57:01 +0000 Subject: [PATCH 4/4] chore(seaborn): update quality score 90 and review feedback for funnel-basic --- .../implementations/python/seaborn.py | 3 +- .../funnel-basic/metadata/python/seaborn.yaml | 148 ++++++++++-------- 2 files changed, 86 insertions(+), 65 deletions(-) diff --git a/plots/funnel-basic/implementations/python/seaborn.py b/plots/funnel-basic/implementations/python/seaborn.py index 6fb1667cd7..db673bc9a5 100644 --- a/plots/funnel-basic/implementations/python/seaborn.py +++ b/plots/funnel-basic/implementations/python/seaborn.py @@ -1,6 +1,7 @@ -"""anyplot.ai +""" anyplot.ai funnel-basic: Basic Funnel Chart Library: seaborn 0.13.2 | Python 3.14.4 +Quality: 90/100 | Updated: 2026-04-26 """ import os diff --git a/plots/funnel-basic/metadata/python/seaborn.yaml b/plots/funnel-basic/metadata/python/seaborn.yaml index f01c911793..ca04d01ca7 100644 --- a/plots/funnel-basic/metadata/python/seaborn.yaml +++ b/plots/funnel-basic/metadata/python/seaborn.yaml @@ -2,7 +2,7 @@ library: seaborn language: python specification_id: funnel-basic created: '2025-12-23T13:04:46Z' -updated: '2026-04-26T05:32:28Z' +updated: '2026-04-26T05:57:01Z' generated_by: claude-opus workflow_run: 24949059229 issue: 789 @@ -12,31 +12,36 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/funnel-ba preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/funnel-basic/python/seaborn/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 83 +quality_score: 90 review: strengths: - - Perfect spec compliance with all required features present - - Strong theme-adaptive implementation with both renders fully readable - - Elegant path-effects for text legibility on narrow segments - - Clean flat code with no unnecessary abstractions + - Creative use of sns.barplot as scaffold for a custom funnel shape with trapezoidal + Polygon patches + - 'Correct Okabe-Ito palette (first series #009E73), both themes properly themed + with no dark-on-dark failures' + - 'Visual storytelling: worst drop-off highlighted with bold conversion annotation + and edge outline on the next stage bar' + - Perfect spec compliance — ordered stages, distinct per-stage colors, value+percentage + labels, proportional widths + - Clean, deterministic flat code with correct seaborn 0.14+ hue/palette/legend=False + API weaknesses: - - Seaborn usage is minimal — only sns.set_theme() provides seaborn-specific contribution; - no seaborn plot functions called - - Narrowest segments (Intent, Purchase) have value text overflowing outside trapezoid - onto background, relying on path-effect stroke workaround - - Slight empty canvas at the bottom below the Purchase segment + - Non-emphasized conversion-rate annotations at 13pt fall below the 16pt guideline + for secondary labels — raise all to 16pt or remove distinction + - 16:9 landscape canvas leaves noticeable horizontal whitespace; a square canvas + (3600×3600) or taller aspect would suit the funnel shape better image_description: |- Light render (plot-light.png): - Background: Warm off-white #FAF8F1 — correct, not pure white - Chrome: Title "funnel-basic · seaborn · anyplot.ai" in dark ink (fontsize 24) — clearly readable. Stage names in dark INK (fontsize 20) on left side — all readable. Conversion-rate annotations in muted INK_MUTED on right — readable. - Data: Five trapezoidal segments in Okabe-Ito order — #009E73 (Awareness), #D55E00 (Interest), #0072B2 (Consideration), #CC79A7 (Intent), #E69F00 (Purchase). White bold value+percentage text centered in each segment, protected by colored path-effect strokes. - Legibility verdict: PASS — minor issue: for narrowest segments (Intent, Purchase), text overflows outside trapezoid and relies on colored stroke for legibility on cream background; functional but not polished. + Background: Warm off-white #FAF8F1 — correct, not pure white. + Chrome: Title "funnel-basic · seaborn · anyplot.ai" in dark ink at 24pt — clearly readable. Stage names (Awareness, Interest, Consideration, Intent, Purchase) in dark INK on left at 20pt — all readable. Conversion-rate annotations (↓60%, ↓67%, ↓50%, ↓50%) in INK_MUTED on the right — visible. Worst drop-off annotation (↓50% between Consideration and Intent) rendered bold and in full INK for emphasis. + Data: Five trapezoidal segments in Okabe-Ito order — #009E73 (Awareness), #D55E00 (Interest), #0072B2 (Consideration), #CC79A7 (Intent), #E69F00 (Purchase). Value+percentage labels ("1,000 · 100%", "600 · 60%", etc.) positioned to the right of each bar in INK color. The Intent bar carries a bold dark edge outline highlighting the worst conversion drop-off. Closing tail gives the funnel a clean pointed tip. + Legibility verdict: PASS — all text readable; minor concern: non-emphasized conversion labels at 13pt are slightly below the 16pt guideline but still legible at full resolution. Dark render (plot-dark.png): - Background: Warm near-black #1A1A17 — correct, not pure black - Chrome: Title in light near-white text — clearly readable. Stage names in light INK token — readable. Conversion rates in muted gray — readable. No dark-on-dark failures detected. - Data: Identical Okabe-Ito colors to light render — #009E73, #D55E00, #0072B2, #CC79A7, #E69F00. White text on dark background reads more cleanly than in light mode. - Legibility verdict: PASS — all text elements readable in dark theme; white value labels on dark background provide excellent contrast. + Background: Warm near-black #1A1A17 — correct, not pure black. + Chrome: Title in near-white #F0EFE8 — clearly readable against dark background. Stage names in light INK token — readable. Conversion-rate annotations in muted #A8A79F — visible. No dark-on-dark failures detected anywhere. + Data: Identical Okabe-Ito colors to light render (#009E73, #D55E00, #0072B2, #CC79A7, #E69F00) — data identity preserved across themes. Value labels in near-white INK, clean contrast on dark background. Intent bar edge outline switches to INK (#F0EFE8) — visible as a light edge on the dark surface. + Legibility verdict: PASS — all text elements readable in dark theme; value labels on dark background are highly legible. criteria_checklist: visual_quality: score: 28 @@ -47,74 +52,79 @@ review: score: 7 max: 8 passed: true - comment: 'Title 24pt, stage names 20pt, value labels 18pt, conversion rates - 14pt — appropriate sizing. Minor: path-effect text overflow on narrowest - segments in light mode functional but imperfect' + comment: Title 24pt, stage names 20pt, value labels 18pt, worst conversion + 16pt — all explicitly set. Non-emphasized conversion labels at 13pt fall + below 16pt guideline for secondary text. - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true comment: Stage names, value labels, and conversion annotations cleanly separated + across all stages including the narrowest ones - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: All five segments visible including narrowest Intent and Purchase - stages + comment: All five funnel segments visible including narrow Intent and Purchase + stages; closing tail provides a clean bottom tip - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Okabe-Ito palette, CVD-safe, no red-green sole signal + comment: Okabe-Ito palette is CVD-safe; five distinct hues, no red-green sole + signal - id: VQ-05 name: Layout & Canvas score: 3 max: 4 passed: true - comment: Funnel well-proportioned; slight empty space at bottom below Purchase - segment + comment: Funnel well-centered and proportioned; 16:9 canvas leaves noticeable + horizontal whitespace since the funnel is taller than wide - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Title matches required format; no traditional axis labels needed - for funnel chart + comment: Title matches required format; stage names serve as labels; no traditional + axis labels needed for funnel chart - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First stage #009E73, Okabe-Ito order, FAF8F1/1A1A17 backgrounds - correct in both themes' + comment: 'First stage #009E73, Okabe-Ito order through position 5, FAF8F1/1A1A17 + backgrounds correct in both themes, chrome flips correctly' design_excellence: - score: 11 + score: 15 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 5 + score: 6 max: 8 passed: true - comment: 'Clear design intent: custom palette, path effects, conversion-rate - annotations, typography hierarchy' + comment: 'Strong design clearly above defaults: custom funnel geometry, Okabe-Ito + per-stage colors, intentional typography hierarchy, and storytelling emphasis + on worst drop-off' - id: DE-02 name: Visual Refinement - score: 3 + score: 5 max: 6 passed: true - comment: Axes hidden, segment gaps add polish; conversion-rate annotations - slightly cramped at right edge + comment: 'All spines removed, no grid, x-axis ticks hidden, y-axis tick marks + hidden, clean closing tail — near-perfect refinement. Minor: horizontal + whitespace in 16:9 canvas for tall funnel.' - id: DE-03 name: Data Storytelling - score: 3 + score: 4 max: 6 passed: true - comment: Funnel shape narrates conversion; stage-to-stage percentages add - analytical insight; no emphasis on largest drop-off + comment: Funnel shape narrates conversion; worst drop-off highlighted with + bold conversion annotation (↓50%, bold+italic+INK) and edge outline on Intent + bar; visual hierarchy guides reader to key insight spec_compliance: score: 15 max: 15 @@ -124,26 +134,29 @@ review: score: 5 max: 5 passed: true - comment: Correct funnel chart with trapezoidal segments narrowing top to bottom + comment: Correct funnel chart with trapezoidal segments narrowing proportionally + top to bottom - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Ordered stages largest to smallest, distinct colors, value+percentage + comment: Ordered stages largest to smallest, distinct per-stage colors, value+percentage labels, proportional widths — all present - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Sales funnel data [1000,600,400,200,100] exactly as specified + comment: Sales funnel stages as y-axis, values as bar width, properly decreasing + top to bottom - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title funnel-basic · seaborn · anyplot.ai correct; no legend needed + comment: Title 'funnel-basic · seaborn · anyplot.ai' correct; no legend needed + as stage names serve as labels data_quality: score: 15 max: 15 @@ -153,20 +166,22 @@ review: score: 6 max: 6 passed: true - comment: 'All funnel aspects shown: widths, names, values, percentages, conversion - rates' + comment: 'All funnel aspects shown: widths, stage names, absolute values, + percentage of total, stage-to-stage conversion rates with varying drop-offs' - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Sales funnel scenario is real-world, neutral, plausible + comment: Classic marketing funnel (Awareness→Purchase) is a real-world, neutral, + widely-recognized business scenario - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: 1000 to 100 with 50-67% conversion rates reflects typical sales pipeline + comment: 1000 to 100 with 50-67% conversion rates reflects typical sales pipeline; + realistic and appropriate code_quality: score: 10 max: 10 @@ -176,60 +191,65 @@ review: score: 3 max: 3 passed: true - comment: No functions or classes; flat script + comment: 'Flat script: imports → tokens → data → theme → plot → labels → save; + no functions or classes' - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Fully deterministic hardcoded data + comment: Fully deterministic hardcoded data; no random generation needed - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: 'All imports used: os, patheffects, pyplot, seaborn, Polygon' + comment: 'All imports used: os, matplotlib.pyplot, seaborn, matplotlib.patches.Polygon' - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Appropriate complexity for custom chart type; clean loop; no fake - UI + comment: Appropriate complexity for custom chart type; clean loops; comments + explain non-obvious geometry decisions; no fake UI - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot-{THEME}.png with correct theme token + comment: Saves as plot-{THEME}.png with dpi=300, bbox_inches=tight, facecolor=PAGE_BG library_mastery: - score: 4 + score: 7 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 3 + score: 4 max: 5 passed: true - comment: Uses sns.set_theme() for theme/chrome setup idiomatically; actual - polygon drawing is raw matplotlib — unavoidable given no seaborn funnel - primitive + comment: 'Uses seaborn 0.14+ idiomatic API: hue=stages+palette+legend=False + for per-bar coloring, sns.set_theme() for comprehensive chrome configuration, + sns.despine() for spine management. Core funnel geometry unavoidably uses + matplotlib patches (no seaborn funnel primitive).' - id: LM-02 name: Distinctive Features - score: 1 + score: 3 max: 5 - passed: false - comment: Seaborn usage limited to set_theme() for color tokens; no seaborn - plot functions called; chart is effectively pure matplotlib + passed: true + comment: hue-based per-bar coloring with legend=False is a seaborn-distinctive + pattern not easily replicated in plain matplotlib; sns.set_theme() and sns.despine() + are seaborn-specific. Core polygon geometry is matplotlib. verdict: APPROVED impl_tags: dependencies: [] techniques: - patches - annotations + - manual-ticks patterns: - explicit-figure - iteration-over-groups dataprep: [] styling: - minimal-chrome + - edge-highlighting