From 5e3c5eff6fa4f96e13108c8c1f65532a58d66c02 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 18 Aug 2026 14:51:23 +0000 Subject: [PATCH 1/7] feat(plotnine): implement bar-stacked-percent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regen from quality 86. Addressed: - design excellence (DE-01/DE-03): "Others" now uses the theme-adaptive muted semantic anchor instead of a fourth categorical color, since it's the aggregate rest-of-market bucket rather than a distinct company - y-axis now shows percent-formatted labels (scale_y_continuous with percent_format), dropping the redundant "(%)" from the axis title - title now includes the language segment per the mandated "{spec-id} · {lang} · {lib} · anyplot.ai" format - canvas resized to the canonical 3200x1800 (figure_size=(8, 4.5), dpi=400) per the current library prompt; font sizes aligned to the library prompt's sizing table (title 12pt, axis title 10pt, axis/legend text 8pt) Kept unchanged: Okabe-Ito-derived Imprint palette ordering, KISS structure, categorical ordering, realistic market-share data, theme- adaptive chrome. --- .../implementations/python/plotnine.py | 83 ++++++++----------- 1 file changed, 33 insertions(+), 50 deletions(-) diff --git a/plots/bar-stacked-percent/implementations/python/plotnine.py b/plots/bar-stacked-percent/implementations/python/plotnine.py index 471d455f81..21eeed779d 100644 --- a/plots/bar-stacked-percent/implementations/python/plotnine.py +++ b/plots/bar-stacked-percent/implementations/python/plotnine.py @@ -1,15 +1,17 @@ -""" anyplot.ai +"""anyplot.ai bar-stacked-percent: 100% Stacked Bar Chart Library: plotnine 0.15.4 | Python 3.13.13 -Quality: 86/100 | Updated: 2026-05-08 +Quality: pending | Updated: 2026-08-18 """ import os import sys + sys.path = [p for p in sys.path if os.path.abspath(p) != os.getcwd()] import pandas as pd # noqa: E402 +from mizani.formatters import percent_format # noqa: E402 from plotnine import ( # noqa: E402 aes, element_blank, @@ -21,57 +23,36 @@ labs, position_fill, scale_fill_manual, + scale_y_continuous, theme, theme_minimal, ) -# Theme tokens +# Theme tokens (see prompts/default-style-guide.md "Background" + "Theme-adaptive Chrome") 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" +MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Imprint semantic anchor: other/rest -# Okabe-Ito palette -IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] +# Imprint palette — named competitors take positions 1-3 in canonical order; +# "Others" uses the muted semantic anchor since it is literally the aggregate +# rest-of-market bucket, not a distinct company. +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] -# Data - Market share by quarter for tech companies +# Data - smartphone market share by quarter quarters = ["Q1 2023", "Q2 2023", "Q3 2023", "Q4 2023", "Q1 2024", "Q2 2024"] companies_ordered = ["Others", "Xiaomi", "Samsung", "Apple"] +apple_share = [23, 21, 20, 22, 21, 20] +samsung_share = [22, 21, 20, 19, 20, 19] +xiaomi_share = [12, 13, 14, 14, 15, 16] +others_share = [43, 45, 46, 45, 44, 45] data = { "Quarter": quarters * 4, "Company": (["Apple"] * 6 + ["Samsung"] * 6 + ["Xiaomi"] * 6 + ["Others"] * 6), - "Share": [ - # Apple - 23, - 21, - 20, - 22, - 21, - 20, - # Samsung - 22, - 21, - 20, - 19, - 20, - 19, - # Xiaomi - 12, - 13, - 14, - 14, - 15, - 16, - # Others - 43, - 45, - 46, - 45, - 44, - 45, - ], + "Share": apple_share + samsung_share + xiaomi_share + others_share, } df = pd.DataFrame(data) @@ -79,36 +60,38 @@ df["Quarter"] = pd.Categorical(df["Quarter"], categories=quarters, ordered=True) df["Company"] = pd.Categorical(df["Company"], categories=companies_ordered, ordered=True) -# Color mapping with Okabe-Ito palette -color_map = {"Others": IMPRINT[3], "Xiaomi": IMPRINT[2], "Samsung": IMPRINT[1], "Apple": IMPRINT[0]} +# Color mapping: Apple/Samsung/Xiaomi in canonical Imprint order, Others muted +color_map = {"Others": MUTED, "Xiaomi": IMPRINT[2], "Samsung": IMPRINT[1], "Apple": IMPRINT[0]} -# Theme-adaptive colors for legend and text +# Theme-adaptive chrome anyplot_theme = theme( plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), - panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10), + panel_border=element_blank(), + panel_grid_major_x=element_blank(), + panel_grid_major_y=element_line(color=INK, size=0.3, alpha=0.10), panel_grid_minor=element_blank(), - panel_border=element_rect(color=INK_SOFT, fill=None, size=0.5), - axis_title=element_text(color=INK, size=20), - axis_text=element_text(color=INK_SOFT, size=16), + axis_title=element_text(color=INK, size=10), + axis_text=element_text(color=INK_SOFT, size=8), axis_line=element_line(color=INK_SOFT, size=0.5), - plot_title=element_text(color=INK, size=24, weight="bold"), + plot_title=element_text(color=INK, size=12), legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), - legend_title=element_text(color=INK, size=16), - legend_text=element_text(color=INK_SOFT, size=16), + legend_title=element_text(color=INK, size=8), + legend_text=element_text(color=INK_SOFT, size=8), legend_position="right", - figure_size=(16, 9), + figure_size=(8, 4.5), ) # Create 100% stacked bar chart plot = ( ggplot(df, aes(x="Quarter", y="Share", fill="Company")) - + geom_bar(stat="identity", position=position_fill(), width=0.65) + + geom_bar(stat="identity", position=position_fill(), width=0.7) + scale_fill_manual(values=color_map) - + labs(title="bar-stacked-percent · plotnine · anyplot.ai", x="Quarter", y="Market Share (%)", fill="Company") + + scale_y_continuous(labels=percent_format()) + + labs(title="bar-stacked-percent · python · plotnine · anyplot.ai", x="Quarter", y="Market Share", fill="Company") + theme_minimal() + anyplot_theme ) # Save -plot.save(f"plot-{THEME}.png", dpi=300, verbose=False) +plot.save(f"plot-{THEME}.png", dpi=400, width=8, height=4.5, units="in", verbose=False) From df786727c5efa4c6da73deebda71523145f00f02 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 18 Aug 2026 14:51:37 +0000 Subject: [PATCH 2/7] chore(plotnine): add metadata for bar-stacked-percent --- .../metadata/python/plotnine.yaml | 238 +----------------- 1 file changed, 11 insertions(+), 227 deletions(-) diff --git a/plots/bar-stacked-percent/metadata/python/plotnine.yaml b/plots/bar-stacked-percent/metadata/python/plotnine.yaml index ed52441fdd..7d5d6115ef 100644 --- a/plots/bar-stacked-percent/metadata/python/plotnine.yaml +++ b/plots/bar-stacked-percent/metadata/python/plotnine.yaml @@ -1,237 +1,21 @@ +# Per-library metadata for plotnine implementation of bar-stacked-percent +# Auto-generated by impl-generate.yml + library: plotnine language: python specification_id: bar-stacked-percent created: '2025-12-25T22:50:12Z' -updated: '2026-05-08T11:07:23Z' -generated_by: claude-haiku -workflow_run: 25551716625 +updated: '2026-08-18T14:51:37Z' +generated_by: claude-sonnet +workflow_run: 32150273058 issue: 2008 -python_version: 3.13.13 -library_version: 0.15.4 +language_version: 3.13.15 +library_version: 0.15.8 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-stacked-percent/python/plotnine/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-stacked-percent/python/plotnine/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 86 +quality_score: null review: - strengths: - - Perfect theme-adaptive styling in both light and dark renders - - Correct Okabe-Ito palette with proper color ordering (#009E73 first) - - Excellent text legibility with explicitly sized fonts (title 24pt, labels 20pt, - ticks 16pt) - - Clean, KISS-structured code with proper categorical ordering - - Accurate 100% stacked bar implementation using position_fill() - - Realistic and neutral market share data with clear quarterly trends - weaknesses: - - Design Excellence lacks distinctive custom elements beyond style guide requirements - - Visual storytelling is straightforward—displays data clearly but without particular - emphasis or focal points - image_description: |- - Light render (plot-light.png): - Background: Warm off-white (#FAF8F1) as specified - Chrome: Title "bar-stacked-percent · plotnine · anyplot.ai" bold and large (24pt), axis labels "Quarter" and "Market Share (%)" clearly visible in dark text (INK #1A1A17), tick labels for quarters and percentages readable at 16pt - Data: Green (#009E73) Apple segment at bottom, orange (#D55E00) Samsung, blue (#0072B2) Xiaomi, pink (#CC79A7) Others—all segments clearly distinguishable with proper proportions - Legend: Company labels match data; legend positioned right of plot with adequate spacing - Grid: Subtle major grid lines at 10% opacity; no visual competition with data - Legibility verdict: PASS — all elements readable with good contrast against light background - - Dark render (plot-dark.png): - Background: Warm near-black (#1A1A17) matching specification - Chrome: Title visible in light text (INK #F0EFE8), axis labels and tick labels clearly readable in INK_SOFT (#B8B7B0), no dark-on-dark failures - Data: All data colors identical to light render (Apple #009E73, Samsung #D55E00, Xiaomi #0072B2, Others #CC79A7)—categorical series maintain visual identity across themes; proportions match light render - Legend: Company labels equally visible with light text on elevated background (#242420) - Grid: Subtle grid at same opacity; visible but not dominant - Legibility verdict: PASS — excellent readability on dark surface; all text and data elements clearly distinguished - criteria_checklist: - visual_quality: - score: 30 - max: 30 - items: - - id: VQ-01 - name: Text Legibility - score: 8 - max: 8 - passed: true - comment: Title 24pt, labels 20pt, ticks 16pt—all explicitly sized and readable - in both themes - - id: VQ-02 - name: No Overlap - score: 6 - max: 6 - passed: true - comment: No overlapping elements; all quarter labels and legend clear - - id: VQ-03 - name: Element Visibility - score: 6 - max: 6 - passed: true - comment: Bar segments clearly visible and sized appropriately for 100% stacked - format - - id: VQ-04 - name: Color Accessibility - score: 2 - max: 2 - passed: true - comment: Okabe-Ito palette ensures colorblind safety; good contrast between - segments - - id: VQ-05 - name: Layout & Canvas - score: 4 - max: 4 - passed: true - comment: Figure 16×9 (4800×2700px) fills canvas appropriately with balanced - margins - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: 'X: ''Quarter'' (descriptive), Y: ''Market Share (%)'' (with units)' - - id: VQ-07 - name: Palette Compliance - score: 2 - max: 2 - passed: true - comment: 'First series #009E73 ✓; Okabe-Ito order maintained; backgrounds - #FAF8F1/#1A1A17 ✓; theme-correct chrome in both renders' - design_excellence: - score: 8 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 4 - max: 8 - passed: false - comment: Well-configured library defaults following style guide; no exceptional - custom design beyond requirements - - id: DE-02 - name: Visual Refinement - score: 2 - max: 6 - passed: false - comment: Grid and theme customized; mostly library defaults with theme token - application - - id: DE-03 - name: Data Storytelling - score: 2 - max: 6 - passed: false - comment: Data clearly displayed with natural composition order; no visual - emphasis or focal point - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: 100% stacked bar chart with position_fill() normalization ✓ - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: Multiple categories (6 quarters), components (4 companies), proportional - segments, legend all present - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: 'X: Quarter, Y: normalized share, Fill: Company—correct and complete' - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: 'Title: ''bar-stacked-percent · plotnine · anyplot.ai'' ✓; Legend: - Company with correct labels' - data_quality: - score: 15 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 6 - max: 6 - passed: true - comment: 'Shows all aspects: multiple categories, multiple components, composition - changes over time' - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: Tech company market share by quarter—real, neutral, comprehensible - scenario - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: Percentages sum to ~100% per quarter; trends are realistic for smartphone - market - code_quality: - score: 10 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: Imports → Data → Plot → Save; no unnecessary functions - - id: CQ-02 - name: Reproducibility - score: 2 - max: 2 - passed: true - comment: Hardcoded deterministic data - - id: CQ-03 - name: Clean Imports - score: 2 - max: 2 - passed: true - comment: Only used imports; all necessary - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: Clean Python; proper categorical ordering; no fake functionality - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: Saves as plot-{THEME}.png; current API - library_mastery: - score: 8 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 5 - max: 5 - passed: true - comment: 'Expert ggplot2 style: ggplot() + geom_bar() + position_fill() + - scale_fill_manual() + theme()' - - id: LM-02 - name: Distinctive Features - score: 3 - max: 5 - passed: false - comment: Uses position_fill() and comprehensive theme customization; solid - library knowledge but standard patterns - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: [] - patterns: - - data-generation - dataprep: [] - styling: - - grid-styling + strengths: [] + weaknesses: [] From 80988bcdbfacb09676c6d0e1acd4a1652d257f55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 18 Aug 2026 14:56:13 +0000 Subject: [PATCH 3/7] chore(plotnine): update quality score 88 and review feedback for bar-stacked-percent --- .../implementations/python/plotnine.py | 6 +- .../metadata/python/plotnine.yaml | 239 +++++++++++++++++- 2 files changed, 235 insertions(+), 10 deletions(-) diff --git a/plots/bar-stacked-percent/implementations/python/plotnine.py b/plots/bar-stacked-percent/implementations/python/plotnine.py index 21eeed779d..950b4b6694 100644 --- a/plots/bar-stacked-percent/implementations/python/plotnine.py +++ b/plots/bar-stacked-percent/implementations/python/plotnine.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai bar-stacked-percent: 100% Stacked Bar Chart -Library: plotnine 0.15.4 | Python 3.13.13 -Quality: pending | Updated: 2026-08-18 +Library: plotnine 0.15.8 | Python 3.13.15 +Quality: 88/100 | Updated: 2026-08-18 """ import os diff --git a/plots/bar-stacked-percent/metadata/python/plotnine.yaml b/plots/bar-stacked-percent/metadata/python/plotnine.yaml index 7d5d6115ef..0551d7358a 100644 --- a/plots/bar-stacked-percent/metadata/python/plotnine.yaml +++ b/plots/bar-stacked-percent/metadata/python/plotnine.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for plotnine implementation of bar-stacked-percent -# Auto-generated by impl-generate.yml - library: plotnine language: python specification_id: bar-stacked-percent created: '2025-12-25T22:50:12Z' -updated: '2026-08-18T14:51:37Z' +updated: '2026-08-18T14:56:13Z' generated_by: claude-sonnet workflow_run: 32150273058 issue: 2008 @@ -15,7 +12,235 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-stack preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-stacked-percent/python/plotnine/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: null +quality_score: 88 review: - strengths: [] - weaknesses: [] + strengths: + - Correct idiomatic 100% stacked bar via geom_bar(stat='identity', position=position_fill()) + with mizani's percent_format() on the y-axis + - 'Thoughtful semantic color mapping: Apple/Samsung/Xiaomi get canonical Imprint + positions 1-3 (brand green always first), while ''Others'' correctly uses the + theme-adaptive `muted` semantic anchor instead of a 4th arbitrary categorical + color, matching the anchor''s defined ''other/rest'' role' + - Both renders are clean with no clipping, no overlap, and consistent component + ordering across all six quarters for easy visual tracking + - 'Clean, reproducible, minimal code: no randomness, no unused imports, correct + 3200x1800 landscape canvas via figure_size=(8, 4.5) at dpi=400' + weaknesses: + - No visual hierarchy or data storytelling beyond the base composition view (DE-03) + — no annotation or highlighted trend, and the spec's Notes explicitly suggest + percentage labels within segments 'when space permits'; the Apple/Samsung/Xiaomi + bands are each roughly 20% tall, which is enough room for a geom_text label layer + - Beyond the required position_fill mechanism, no additional distinctive plotnine + feature is showcased (LM-02) — e.g. no guide/legend reordering, no coord_flip + consideration, no direct segment labeling + - Aesthetic polish is solid but fairly restrained beyond the palette choice (DE-01/DE-02) + — no additional refinement flourishes (e.g. subtle bar edge stroke, custom legend + key styling) beyond the standard theme_minimal + token overrides + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1), correctly not pure white and not dark. + Chrome: Title "bar-stacked-percent · python · plotnine · anyplot.ai" in dark ink, centered, ~60% of plot width. Axis titles "Quarter" (x) and "Market Share" (y) in dark ink, tick labels ("Q1 2023"..."Q2 2024", "0%"-"100%") in softer dark gray. Subtle horizontal gridlines at low alpha. Legend box top-right with bordered fill, title "Company", 4 entries (Others, Xiaomi, Samsung, Apple). + Data: Six bars, each stacked bottom-to-top as Apple (brand green #009E73) / Samsung (lavender #C475FD) / Xiaomi (blue #4467A3) / Others (dark taupe-gray muted anchor). All bars sum to 100%, consistent ordering across quarters. + Legibility verdict: PASS — all text is clearly readable against the light background, no light-on-light issues. + + Dark render (plot-dark.png): + Background: Warm near-black (#1A1A17), correctly not pure black and not light. + Chrome: Title and axis titles in light off-white ink, tick labels in a softer light gray. Gridlines subtle and visible. Legend box uses the elevated dark fill with a light border, same 4 entries. + Data: Same stacking order and same green/lavender/blue hues as the light render — Apple/Samsung/Xiaomi colors are pixel-identical to the light render, confirming positions 1-3 of the Imprint palette are theme-independent. "Others" appropriately switches to the lighter warm-gray `muted` anchor value (by design — the muted/neutral semantic anchors are explicitly theme-adaptive per the style guide, unlike the 8 categorical positions). + Legibility verdict: PASS — no dark-on-dark text; all title, axis, and tick text is clearly visible against the dark background. Brand green reads well on the dark surface. + criteria_checklist: + visual_quality: + score: 29 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: Explicit font sizes set for all text roles; readable in both themes, + no light-on-light or dark-on-dark issues + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No text or element collisions in either render + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Bar width (0.7) and stacking are clearly visible, appropriate for + 6 categories x 4 components + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Distinguishable Imprint hues plus muted anchor for 'Others'; no red-green-only + encoding + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Title ~60% width, balanced axis labels, nothing cut off, canvas 3200x1800 + confirmed + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: '''Quarter'' and ''Market Share'' are descriptive; y-axis uses percent + formatting' + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series #009E73, canonical order for named companies, correct + theme-adaptive muted anchor for ''Others'', correct theme backgrounds' + design_excellence: + score: 12 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: Thoughtful semantic color mapping for 'Others', but otherwise fairly + standard theme_minimal customization + - id: DE-02 + name: Visual Refinement + score: 5 + max: 6 + passed: true + comment: Panel border removed, grid reduced to faint y-major only, generous + whitespace via figure sizing + - id: DE-03 + name: Data Storytelling + score: 2 + max: 6 + passed: false + comment: No annotation, highlighted trend, or in-segment percentage labels + despite spec suggesting labels when space permits + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct 100% stacked bar via position_fill() + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Distinct colors, clear legend, consistent component ordering across + bars + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: x=Quarter (category), y=Share normalized to %, fill=Company (component); + all data shown + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title matches mandated format exactly; legend labels match component + names + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Full composition-over-time view with 4 components across 6 quarters, + each summing to 100% + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Smartphone market share by quarter is plausible and neutral + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Share values (12-46%) are sensible for the domain and sum correctly + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: No functions/classes, flat script + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fully deterministic hardcoded data, no randomness + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: Only used imports (pandas, mizani.percent_format, plotnine components) + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Appropriate complexity, no fake UI or simulated interactivity + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves plot-{THEME}.png via plot.save() with correct dpi/width/height + for the landscape canvas + library_mastery: + score: 7 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: 'Grammar-of-graphics idioms: aes mapping, position_fill, scale_fill_manual, + scale_y_continuous with percent_format' + - id: LM-02 + name: Distinctive Features + score: 2 + max: 5 + passed: false + comment: position_fill is required for the plot type itself rather than a + distinctive extra; no additional plotnine-specific flourish (e.g. geom_text + labels, guide customization) + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: [] + patterns: + - data-generation + dataprep: [] + styling: + - grid-styling + - publication-ready From 4847c75b47e662cf7a3e9eb65cf2e15603ceab50 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 18 Aug 2026 15:00:03 +0000 Subject: [PATCH 4/7] fix(plotnine): address review feedback for bar-stacked-percent Attempt 1/4 - fixes based on AI review --- .../implementations/python/plotnine.py | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/plots/bar-stacked-percent/implementations/python/plotnine.py b/plots/bar-stacked-percent/implementations/python/plotnine.py index 950b4b6694..f7a536c563 100644 --- a/plots/bar-stacked-percent/implementations/python/plotnine.py +++ b/plots/bar-stacked-percent/implementations/python/plotnine.py @@ -1,4 +1,4 @@ -""" anyplot.ai +"""anyplot.ai bar-stacked-percent: 100% Stacked Bar Chart Library: plotnine 0.15.8 | Python 3.13.15 Quality: 88/100 | Updated: 2026-08-18 @@ -19,9 +19,11 @@ element_rect, element_text, geom_bar, + geom_text, ggplot, labs, position_fill, + scale_color_identity, scale_fill_manual, scale_y_continuous, theme, @@ -63,6 +65,38 @@ # Color mapping: Apple/Samsung/Xiaomi in canonical Imprint order, Others muted color_map = {"Others": MUTED, "Xiaomi": IMPRINT[2], "Samsung": IMPRINT[1], "Apple": IMPRINT[0]} +# In-segment percentage labels (DE-03): pick whichever ink extreme has higher +# WCAG contrast against each segment's own fill color, so labels stay legible +# on both the mid-tone brand hues and the theme-adaptive "Others" gray. +LIGHT_INK = "#F0EFE8" +DARK_INK = "#1A1A17" + + +def _relative_luminance(hex_color): + r, g, b = (int(hex_color[i : i + 2], 16) / 255 for i in (1, 3, 5)) + + def _linearize(c): + return c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4 + + r, g, b = _linearize(r), _linearize(g), _linearize(b) + return 0.2126 * r + 0.7152 * g + 0.0722 * b + + +def _contrast_ratio(l1, l2): + lighter, darker = max(l1, l2), min(l1, l2) + return (lighter + 0.05) / (darker + 0.05) + + +def _label_color(fill_hex): + fill_l = _relative_luminance(fill_hex) + light_contrast = _contrast_ratio(fill_l, _relative_luminance(LIGHT_INK)) + dark_contrast = _contrast_ratio(fill_l, _relative_luminance(DARK_INK)) + return LIGHT_INK if light_contrast >= dark_contrast else DARK_INK + + +df["Label"] = df["Share"].astype(str) + "%" +df["LabelColor"] = df["Company"].map(color_map).map(_label_color) + # Theme-adaptive chrome anyplot_theme = theme( plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), @@ -82,11 +116,13 @@ figure_size=(8, 4.5), ) -# Create 100% stacked bar chart +# Create 100% stacked bar chart with in-segment percentage labels plot = ( ggplot(df, aes(x="Quarter", y="Share", fill="Company")) + geom_bar(stat="identity", position=position_fill(), width=0.7) + + geom_text(aes(label="Label", color="LabelColor"), position=position_fill(vjust=0.5), size=2.8, show_legend=False) + scale_fill_manual(values=color_map) + + scale_color_identity() + scale_y_continuous(labels=percent_format()) + labs(title="bar-stacked-percent · python · plotnine · anyplot.ai", x="Quarter", y="Market Share", fill="Company") + theme_minimal() From c6dedd7c6c85d6daaa1ac5e30bba18a56ac34ab3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 18 Aug 2026 15:07:49 +0000 Subject: [PATCH 5/7] chore(plotnine): update quality score 49 and review feedback for bar-stacked-percent --- .../implementations/python/plotnine.py | 4 +- .../metadata/python/plotnine.yaml | 224 ++++++++++-------- 2 files changed, 129 insertions(+), 99 deletions(-) diff --git a/plots/bar-stacked-percent/implementations/python/plotnine.py b/plots/bar-stacked-percent/implementations/python/plotnine.py index f7a536c563..4fdf02101d 100644 --- a/plots/bar-stacked-percent/implementations/python/plotnine.py +++ b/plots/bar-stacked-percent/implementations/python/plotnine.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai bar-stacked-percent: 100% Stacked Bar Chart Library: plotnine 0.15.8 | Python 3.13.15 -Quality: 88/100 | Updated: 2026-08-18 +Quality: 49/100 | Updated: 2026-08-18 """ import os diff --git a/plots/bar-stacked-percent/metadata/python/plotnine.yaml b/plots/bar-stacked-percent/metadata/python/plotnine.yaml index 0551d7358a..cd17fb84f1 100644 --- a/plots/bar-stacked-percent/metadata/python/plotnine.yaml +++ b/plots/bar-stacked-percent/metadata/python/plotnine.yaml @@ -2,7 +2,7 @@ library: plotnine language: python specification_id: bar-stacked-percent created: '2025-12-25T22:50:12Z' -updated: '2026-08-18T14:56:13Z' +updated: '2026-08-18T15:07:49Z' generated_by: claude-sonnet workflow_run: 32150273058 issue: 2008 @@ -12,97 +12,126 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-stack preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-stacked-percent/python/plotnine/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 88 +quality_score: 49 review: strengths: - - Correct idiomatic 100% stacked bar via geom_bar(stat='identity', position=position_fill()) - with mizani's percent_format() on the y-axis - - 'Thoughtful semantic color mapping: Apple/Samsung/Xiaomi get canonical Imprint - positions 1-3 (brand green always first), while ''Others'' correctly uses the - theme-adaptive `muted` semantic anchor instead of a 4th arbitrary categorical - color, matching the anchor''s defined ''other/rest'' role' - - Both renders are clean with no clipping, no overlap, and consistent component - ordering across all six quarters for easy visual tracking - - 'Clean, reproducible, minimal code: no randomness, no unused imports, correct - 3200x1800 landscape canvas via figure_size=(8, 4.5) at dpi=400' + - Correct 100% stacked bar structure via geom_bar(stat="identity", position=position_fill()); + every bar sums to 100% as required. + - WCAG-contrast-aware in-segment label coloring (_relative_luminance/_contrast_ratio/_label_color) + is a genuinely sophisticated touch, picking whichever ink extreme has higher contrast + against each segment's own fill. + - 'Imprint palette used correctly: Apple (first categorical series) is bound to + #009E73; "Others" uses the muted semantic-anchor gray with a documented rationale + (aggregate rest-of-market bucket, not a distinct company).' + - 'Theme-adaptive chrome correctly threaded through both renders: light background + #FAF8F1 and dark background #1A1A17 both read correctly, axis/tick/legend text + is legible against each background, y-only gridlines are subtle.' + - Canvas size correct (3200x1800 landscape, matches figure_size=(8,4.5) target), + title format matches the mandated `bar-stacked-percent · python · plotnine · anyplot.ai` + pattern, legend order (Others/Xiaomi/Samsung/Apple top-to-bottom) mirrors the + visual stacking order. weaknesses: - - No visual hierarchy or data storytelling beyond the base composition view (DE-03) - — no annotation or highlighted trend, and the spec's Notes explicitly suggest - percentage labels within segments 'when space permits'; the Apple/Samsung/Xiaomi - bands are each roughly 20% tall, which is enough room for a geom_text label layer - - Beyond the required position_fill mechanism, no additional distinctive plotnine - feature is showcased (LM-02) — e.g. no guide/legend reordering, no coord_flip - consideration, no direct segment labeling - - Aesthetic polish is solid but fairly restrained beyond the palette choice (DE-01/DE-02) - — no additional refinement flourishes (e.g. subtle bar edge stroke, custom legend - key styling) beyond the standard theme_minimal + token overrides + - 'CRITICAL: In-segment percentage labels (geom_text + position_fill(vjust=0.5)) + are not reliably aligned with their own bar segment. Verified by cropping and + zooming into individual bars in both renders across Q1/Q3/Q4 2023. In the LIGHT + render, several bars show TWO labels crammed into one colored segment while the + adjacent segment has none at all -- e.g. Q1 2023''s gray "Others" segment shows + both "22%" and "23%" (Samsung''s and Apple''s real values) while the blue "Xiaomi" + segment is completely unlabeled; Q3 2023''s gray segment shows "20%" twice; Q4 + 2023''s gray segment shows "19%" and "22%" together. In the DARK render every + segment gets exactly one label, but the bottom three segments (Apple/Samsung/Xiaomi) + are cyclically shifted by one position relative to their true value -- e.g. Q4 + 2023 dark: green "Apple" segment shows "14%" (Xiaomi''s real value, Apple''s real + value is 22%), purple "Samsung" segment shows "22%" (Apple''s real value), blue + "Xiaomi" segment shows "19%" (Samsung''s real value); only the top "Others" segment + shows its own correct value. This is systemic (every bar, both themes) and actively + misrepresents the data to the viewer.' + - 'Likely root cause: geom_bar''s and geom_text''s independent position_fill() computations + are producing different per-group cumulative offsets because the dataframe rows + are grouped by company (all-Apple rows, then all-Samsung, then all-Xiaomi, then + all-Others) rather than interleaved/sorted to match the stacking order used for + the bars. Fix by forcing both layers onto identical offsets -- e.g. precompute + the cumulative fill midpoint per (Quarter, Company) as an explicit numeric `y` + column (matching the same fill/stack order geom_bar uses) and pass it directly + to geom_text(aes(y=label_y), position=''identity'') instead of relying on geom_text''s + own position_fill. Verify by re-cropping every bar in both themes and confirming + exactly one label lands centered in its own correctly colored segment before resubmitting.' + - Because labels show numbers under the wrong segment, the chart currently gives + an incorrect visual takeaway (e.g. a viewer reading '14%' from the green Apple + segment when Apple's real Q4 2023 share is 22%) -- this undermines the data storytelling + and the 'percentage labels within segments' feature the spec suggests, even though + the feature is coded, it does not function correctly on-canvas. image_description: |- Light render (plot-light.png): - Background: Warm off-white (#FAF8F1), correctly not pure white and not dark. - Chrome: Title "bar-stacked-percent · python · plotnine · anyplot.ai" in dark ink, centered, ~60% of plot width. Axis titles "Quarter" (x) and "Market Share" (y) in dark ink, tick labels ("Q1 2023"..."Q2 2024", "0%"-"100%") in softer dark gray. Subtle horizontal gridlines at low alpha. Legend box top-right with bordered fill, title "Company", 4 entries (Others, Xiaomi, Samsung, Apple). - Data: Six bars, each stacked bottom-to-top as Apple (brand green #009E73) / Samsung (lavender #C475FD) / Xiaomi (blue #4467A3) / Others (dark taupe-gray muted anchor). All bars sum to 100%, consistent ordering across quarters. - Legibility verdict: PASS — all text is clearly readable against the light background, no light-on-light issues. + Background: Warm off-white (~#FAF8F1), correct light theme surface. + Chrome: Title "bar-stacked-percent · python · plotnine · anyplot.ai" is dark, centered, fully legible. Axis titles "Quarter"/"Market Share" and tick labels are dark/soft-dark and clearly legible. Legend box ("Company": Others/Xiaomi/Samsung/Apple) is clearly readable with a bordered background. Y-axis-only gridlines are subtle. + Data: 6 quarterly 100%-stacked bars (Q1 2023 - Q2 2024), 4 components each (Apple green #009E73, Samsung purple, Xiaomi blue, Others muted gray), each bar sums to 100%. BUG: in-segment percentage labels are misplaced -- on multiple bars (Q1, Q3, Q4 2023 confirmed via zoomed crops) two labels land inside a single segment (e.g. Q1's gray "Others" segment shows both "22%" and "23%") while an adjacent segment (blue "Xiaomi") has no label at all. Label text itself is legible/readable (good contrast against each fill), but the values are attached to the wrong segment. + Legibility verdict: PASS for chrome text; FAIL for data-label correctness/placement (see weaknesses). Dark render (plot-dark.png): - Background: Warm near-black (#1A1A17), correctly not pure black and not light. - Chrome: Title and axis titles in light off-white ink, tick labels in a softer light gray. Gridlines subtle and visible. Legend box uses the elevated dark fill with a light border, same 4 entries. - Data: Same stacking order and same green/lavender/blue hues as the light render — Apple/Samsung/Xiaomi colors are pixel-identical to the light render, confirming positions 1-3 of the Imprint palette are theme-independent. "Others" appropriately switches to the lighter warm-gray `muted` anchor value (by design — the muted/neutral semantic anchors are explicitly theme-adaptive per the style guide, unlike the 8 categorical positions). - Legibility verdict: PASS — no dark-on-dark text; all title, axis, and tick text is clearly visible against the dark background. Brand green reads well on the dark surface. + Background: Warm near-black (~#1A1A17), correct dark theme surface. + Chrome: Title, axis titles, and tick labels render in light ink, clearly legible against the dark background -- no dark-on-dark failures observed. Legend box uses an elevated dark background with light text, fully readable. + Data: Same 6 quarters/4 components; data fill colors are identical to the light render (Apple #009E73 green, Samsung purple, Xiaomi blue, Others muted gray) -- confirms the Imprint palette is theme-invariant as required. BUG: every segment gets exactly one label (no doubling/blanks like the light render), but zoomed crops of Q1/Q3/Q4 2023 show the bottom three segments (Apple/Samsung/Xiaomi) are cyclically shifted by one position relative to their true share value -- e.g. Q4 2023: green "Apple" shows "14%" (actually Xiaomi's value; Apple's real share is 22%). Only the top "Others" segment consistently shows its own correct value. + Legibility verdict: PASS for chrome text (no dark-on-dark); FAIL for data-label correctness/placement (see weaknesses). criteria_checklist: visual_quality: - score: 29 + score: 23 max: 30 items: - id: VQ-01 name: Text Legibility - score: 7 + score: 6 max: 8 passed: true - comment: Explicit font sizes set for all text roles; readable in both themes, - no light-on-light or dark-on-dark issues + comment: All glyphs are readable in both themes at good contrast; deducted + because mismatched label values create confusing/misleading reading rather + than a font-rendering issue. - id: VQ-02 name: No Overlap - score: 6 + score: 0 max: 6 - passed: true - comment: No text or element collisions in either render + passed: false + comment: 'Severe: multiple bars have two percentage labels crammed into one + colored segment while an adjacent segment has none (light render), or values + cyclically misassigned across segments (dark render). Confirmed via zoomed + crops of Q1/Q3/Q4 2023 in both themes.' - id: VQ-03 name: Element Visibility - score: 6 + score: 5 max: 6 passed: true - comment: Bar width (0.7) and stacking are clearly visible, appropriate for - 6 categories x 4 components + comment: Bar segments and fills are clearly visible and distinct; only the + text-label placement is broken. - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Distinguishable Imprint hues plus muted anchor for 'Others'; no red-green-only - encoding + comment: Imprint palette provides adequate contrast and is CVD-safe; no red-green + as sole signal. - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Title ~60% width, balanced axis labels, nothing cut off, canvas 3200x1800 - confirmed + comment: Canvas is exactly 3200x1800; title, axes, and legend fit comfortably + with no overflow or clipping. - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: '''Quarter'' and ''Market Share'' are descriptive; y-axis uses percent - formatting' + comment: Descriptive axis titles (Quarter, Market Share) with percent-formatted + y-axis. - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series #009E73, canonical order for named companies, correct - theme-adaptive muted anchor for ''Others'', correct theme backgrounds' + comment: 'First series (Apple) = #009E73; Others uses documented muted semantic + anchor; backgrounds correct in both themes; data colors identical light/dark.' design_excellence: - score: 12 + score: 10 max: 20 items: - id: DE-01 @@ -110,24 +139,24 @@ review: score: 5 max: 8 passed: true - comment: Thoughtful semantic color mapping for 'Others', but otherwise fairly - standard theme_minimal customization + comment: Custom contrast-aware label coloring and deliberate semantic color + mapping show real design thought, above generic defaults. - id: DE-02 name: Visual Refinement - score: 5 + score: 4 max: 6 passed: true - comment: Panel border removed, grid reduced to faint y-major only, generous - whitespace via figure sizing + comment: Spines/panel border removed, y-only subtle gridlines, generous whitespace + around the legend. - id: DE-03 name: Data Storytelling - score: 2 + score: 1 max: 6 passed: false - comment: No annotation, highlighted trend, or in-segment percentage labels - despite spec suggesting labels when space permits + comment: The mislabeled percentages actively mislead the viewer about each + component's true share, undermining the chart's core storytelling purpose. spec_compliance: - score: 15 + score: 10 max: 15 items: - id: SC-01 @@ -135,112 +164,113 @@ review: score: 5 max: 5 passed: true - comment: Correct 100% stacked bar via position_fill() + comment: Correct 100% stacked bar chart via position_fill(). - id: SC-02 name: Required Features - score: 4 + score: 1 max: 4 - passed: true - comment: Distinct colors, clear legend, consistent component ordering across - bars + passed: false + comment: In-segment percentage labels are implemented in code but render with + wrong values/positions in every bar of both themes. - id: SC-03 name: Data Mapping - score: 3 + score: 1 max: 3 - passed: true - comment: x=Quarter (category), y=Share normalized to %, fill=Company (component); - all data shown + passed: false + comment: Bar x/y mapping is correct, but the geom_text layer's y (fill-position) + mapping does not match geom_bar's, producing incorrect data-to-position + mapping for labels. - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title matches mandated format exactly; legend labels match component - names + comment: Title matches mandated format exactly; legend labels match company + names. data_quality: - score: 15 + score: 11 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 6 + score: 2 max: 6 - passed: true - comment: Full composition-over-time view with 4 components across 6 quarters, - each summing to 100% + passed: false + comment: Bars/components/quarters are all present and sum to 100%, but the + on-canvas percentage labels do not correctly represent each segment's value. - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Smartphone market share by quarter is plausible and neutral + comment: Plausible, neutral smartphone market-share-by-quarter scenario. - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Share values (12-46%) are sensible for the domain and sum correctly + comment: Percentages 0-100%, sensible magnitudes for a market-share domain. code_quality: - score: 10 + score: 8 max: 10 items: - id: CQ-01 name: KISS Structure - score: 3 + score: 2 max: 3 passed: true - comment: No functions/classes, flat script + comment: Three small helper functions for WCAG contrast add some structure + beyond a flat script, though reasonably justified. - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Fully deterministic hardcoded data, no randomness + comment: Fully deterministic hardcoded data, no randomness. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only used imports (pandas, mizani.percent_format, plotnine components) + comment: Only imports that are used. - id: CQ-04 name: Code Elegance - score: 2 + score: 1 max: 2 - passed: true - comment: Appropriate complexity, no fake UI or simulated interactivity + passed: false + comment: No fake UI, but the geom_text/position_fill layering has a real functional + defect (see weaknesses). - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves plot-{THEME}.png via plot.save() with correct dpi/width/height - for the landscape canvas + comment: Saves as plot-{THEME}.png via plot.save() with correct dpi/size. library_mastery: - score: 7 + score: 5 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 5 + score: 3 max: 5 passed: true - comment: 'Grammar-of-graphics idioms: aes mapping, position_fill, scale_fill_manual, - scale_y_continuous with percent_format' + comment: Uses idiomatic plotnine grammar (position_fill, scale_fill_manual, + scale_color_identity), but the text-label layer's position handling is misapplied. - id: LM-02 name: Distinctive Features score: 2 max: 5 passed: false - comment: position_fill is required for the plot type itself rather than a - distinctive extra; no additional plotnine-specific flourish (e.g. geom_text - labels, guide customization) - verdict: APPROVED + comment: WCAG-contrast-aware label coloring is a distinctive technique, but + its value is undermined by the broken label placement. + verdict: REJECTED impl_tags: dependencies: [] - techniques: [] + techniques: + - annotations patterns: - data-generation dataprep: [] styling: - - grid-styling - - publication-ready + - alpha-blending From c3f1e7aeedde0cc2dce36cb2f3eb4f154c1e323b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 18 Aug 2026 15:12:01 +0000 Subject: [PATCH 6/7] fix(plotnine): address review feedback for bar-stacked-percent Attempt 2/4 - fixes based on AI review --- .../implementations/python/plotnine.py | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/plots/bar-stacked-percent/implementations/python/plotnine.py b/plots/bar-stacked-percent/implementations/python/plotnine.py index 4fdf02101d..1ae6440c80 100644 --- a/plots/bar-stacked-percent/implementations/python/plotnine.py +++ b/plots/bar-stacked-percent/implementations/python/plotnine.py @@ -1,4 +1,4 @@ -""" anyplot.ai +"""anyplot.ai bar-stacked-percent: 100% Stacked Bar Chart Library: plotnine 0.15.8 | Python 3.13.15 Quality: 49/100 | Updated: 2026-08-18 @@ -97,6 +97,27 @@ def _label_color(fill_hex): df["Label"] = df["Share"].astype(str) + "%" df["LabelColor"] = df["Company"].map(color_map).map(_label_color) +# Precompute label y-positions explicitly (SC-03/VQ-02 fix): geom_bar and +# geom_text each calling their own independent position_fill() can derive +# mismatched per-group cumulative offsets when the source rows are grouped +# by company rather than interleaved per quarter. Instead, compute the exact +# fill-stack midpoint per (Quarter, Company) ourselves -- stacked bottom-to-top +# as Apple/Samsung/Xiaomi/Others, i.e. the reverse of the legend/factor order, +# matching plotnine's default stacking -- and feed it to geom_text via +# position="identity" so both layers are guaranteed to agree. +stack_order_bottom_to_top = ["Apple", "Samsung", "Xiaomi", "Others"] +stack_rank = {company: rank for rank, company in enumerate(stack_order_bottom_to_top)} +# .map() on a Categorical column returns a Categorical result that inherits +# the *original* category order, so sorting by it would sort by category +# position rather than by the mapped rank value -- cast to plain strings +# first so the mapped ranks are ordinary integers. +df["StackRank"] = df["Company"].astype(str).map(stack_rank) +df = df.sort_values(["Quarter", "StackRank"]).reset_index(drop=True) +df["Fraction"] = df["Share"] / 100 +cum_top = df.groupby("Quarter", observed=True)["Fraction"].cumsum() +cum_bottom = cum_top - df["Fraction"] +df["LabelY"] = (cum_top + cum_bottom) / 2 + # Theme-adaptive chrome anyplot_theme = theme( plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), @@ -120,7 +141,7 @@ def _label_color(fill_hex): plot = ( ggplot(df, aes(x="Quarter", y="Share", fill="Company")) + geom_bar(stat="identity", position=position_fill(), width=0.7) - + geom_text(aes(label="Label", color="LabelColor"), position=position_fill(vjust=0.5), size=2.8, show_legend=False) + + geom_text(aes(y="LabelY", label="Label", color="LabelColor"), position="identity", size=2.8, show_legend=False) + scale_fill_manual(values=color_map) + scale_color_identity() + scale_y_continuous(labels=percent_format()) From d37e8a54002b952d3f8062b62e5d9769cd0df01c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 18 Aug 2026 15:16:36 +0000 Subject: [PATCH 7/7] chore(plotnine): update quality score 88 and review feedback for bar-stacked-percent --- .../implementations/python/plotnine.py | 4 +- .../metadata/python/plotnine.yaml | 232 ++++++++---------- 2 files changed, 108 insertions(+), 128 deletions(-) diff --git a/plots/bar-stacked-percent/implementations/python/plotnine.py b/plots/bar-stacked-percent/implementations/python/plotnine.py index 1ae6440c80..e5a6324c96 100644 --- a/plots/bar-stacked-percent/implementations/python/plotnine.py +++ b/plots/bar-stacked-percent/implementations/python/plotnine.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai bar-stacked-percent: 100% Stacked Bar Chart Library: plotnine 0.15.8 | Python 3.13.15 -Quality: 49/100 | Updated: 2026-08-18 +Quality: 88/100 | Updated: 2026-08-18 """ import os diff --git a/plots/bar-stacked-percent/metadata/python/plotnine.yaml b/plots/bar-stacked-percent/metadata/python/plotnine.yaml index cd17fb84f1..eb75e4e101 100644 --- a/plots/bar-stacked-percent/metadata/python/plotnine.yaml +++ b/plots/bar-stacked-percent/metadata/python/plotnine.yaml @@ -2,7 +2,7 @@ library: plotnine language: python specification_id: bar-stacked-percent created: '2025-12-25T22:50:12Z' -updated: '2026-08-18T15:07:49Z' +updated: '2026-08-18T15:16:36Z' generated_by: claude-sonnet workflow_run: 32150273058 issue: 2008 @@ -12,126 +12,103 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-stack preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-stacked-percent/python/plotnine/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 49 +quality_score: 88 review: strengths: - - Correct 100% stacked bar structure via geom_bar(stat="identity", position=position_fill()); - every bar sums to 100% as required. + - In-segment percentage labels now correctly match their own bar segment in every + quarter, both themes -- verified via zoomed crops of Q1 2023 and Q1/Q2 2024 -- + fully resolving the attempt-2 misalignment bug via the precomputed LabelY/StackRank + approach. - WCAG-contrast-aware in-segment label coloring (_relative_luminance/_contrast_ratio/_label_color) - is a genuinely sophisticated touch, picking whichever ink extreme has higher contrast - against each segment's own fill. - - 'Imprint palette used correctly: Apple (first categorical series) is bound to - #009E73; "Others" uses the muted semantic-anchor gray with a documented rationale - (aggregate rest-of-market bucket, not a distinct company).' - - 'Theme-adaptive chrome correctly threaded through both renders: light background - #FAF8F1 and dark background #1A1A17 both read correctly, axis/tick/legend text - is legible against each background, y-only gridlines are subtle.' - - Canvas size correct (3200x1800 landscape, matches figure_size=(8,4.5) target), - title format matches the mandated `bar-stacked-percent · python · plotnine · anyplot.ai` - pattern, legend order (Others/Xiaomi/Samsung/Apple top-to-bottom) mirrors the - visual stacking order. + driven through scale_color_identity() is a genuinely sophisticated, distinctive + plotnine touch. + - 'Imprint palette used correctly: Apple (first categorical series) = #009E73; Samsung/Xiaomi + take canonical positions 2-3; "Others" correctly uses the theme-adaptive muted + semantic anchor rather than a 4th arbitrary categorical color.' + - Theme-adaptive chrome correctly threaded through both renders -- correct backgrounds, + fully legible text, subtle y-only gridlines, no dark-on-dark or light-on-light + failures. + - Clean, realistic, neutral smartphone-market-share data with genuine quarter-over-quarter + variation (Apple gently declining, Xiaomi growing) that gives DQ-01/DQ-03 real + substance; canvas is correct (3200x1800), title format matches the mandated pattern, + legend order mirrors the visual stacking order. weaknesses: - - 'CRITICAL: In-segment percentage labels (geom_text + position_fill(vjust=0.5)) - are not reliably aligned with their own bar segment. Verified by cropping and - zooming into individual bars in both renders across Q1/Q3/Q4 2023. In the LIGHT - render, several bars show TWO labels crammed into one colored segment while the - adjacent segment has none at all -- e.g. Q1 2023''s gray "Others" segment shows - both "22%" and "23%" (Samsung''s and Apple''s real values) while the blue "Xiaomi" - segment is completely unlabeled; Q3 2023''s gray segment shows "20%" twice; Q4 - 2023''s gray segment shows "19%" and "22%" together. In the DARK render every - segment gets exactly one label, but the bottom three segments (Apple/Samsung/Xiaomi) - are cyclically shifted by one position relative to their true value -- e.g. Q4 - 2023 dark: green "Apple" segment shows "14%" (Xiaomi''s real value, Apple''s real - value is 22%), purple "Samsung" segment shows "22%" (Apple''s real value), blue - "Xiaomi" segment shows "19%" (Samsung''s real value); only the top "Others" segment - shows its own correct value. This is systemic (every bar, both themes) and actively - misrepresents the data to the viewer.' - - 'Likely root cause: geom_bar''s and geom_text''s independent position_fill() computations - are producing different per-group cumulative offsets because the dataframe rows - are grouped by company (all-Apple rows, then all-Samsung, then all-Xiaomi, then - all-Others) rather than interleaved/sorted to match the stacking order used for - the bars. Fix by forcing both layers onto identical offsets -- e.g. precompute - the cumulative fill midpoint per (Quarter, Company) as an explicit numeric `y` - column (matching the same fill/stack order geom_bar uses) and pass it directly - to geom_text(aes(y=label_y), position=''identity'') instead of relying on geom_text''s - own position_fill. Verify by re-cropping every bar in both themes and confirming - exactly one label lands centered in its own correctly colored segment before resubmitting.' - - Because labels show numbers under the wrong segment, the chart currently gives - an incorrect visual takeaway (e.g. a viewer reading '14%' from the green Apple - segment when Apple's real Q4 2023 share is 22%) -- this undermines the data storytelling - and the 'percentage labels within segments' feature the spec suggests, even though - the feature is coded, it does not function correctly on-canvas. + - Design Excellence beyond the palette/label choices is fairly restrained -- no + additional refinement flourish (e.g. subtle bar-edge stroke, custom legend key + styling) beyond theme_minimal() + token overrides. + - LM-01 idiomatic usage is solid but not exhaustive -- e.g. no guides()-based legend + reordering or other plotnine-specific flourish beyond the required position_fill() + mechanism and the label-color trick. + - Y-axis title "Market Share" has no explicit unit suffix (e.g. "Market Share (%)") + -- the percent-formatted tick labels make the unit self-evident, but an explicit + unit would be marginally clearer. image_description: |- Light render (plot-light.png): - Background: Warm off-white (~#FAF8F1), correct light theme surface. - Chrome: Title "bar-stacked-percent · python · plotnine · anyplot.ai" is dark, centered, fully legible. Axis titles "Quarter"/"Market Share" and tick labels are dark/soft-dark and clearly legible. Legend box ("Company": Others/Xiaomi/Samsung/Apple) is clearly readable with a bordered background. Y-axis-only gridlines are subtle. - Data: 6 quarterly 100%-stacked bars (Q1 2023 - Q2 2024), 4 components each (Apple green #009E73, Samsung purple, Xiaomi blue, Others muted gray), each bar sums to 100%. BUG: in-segment percentage labels are misplaced -- on multiple bars (Q1, Q3, Q4 2023 confirmed via zoomed crops) two labels land inside a single segment (e.g. Q1's gray "Others" segment shows both "22%" and "23%") while an adjacent segment (blue "Xiaomi") has no label at all. Label text itself is legible/readable (good contrast against each fill), but the values are attached to the wrong segment. - Legibility verdict: PASS for chrome text; FAIL for data-label correctness/placement (see weaknesses). + Background: Warm off-white, matches #FAF8F1 -- not pure white. + Chrome: Title "bar-stacked-percent · python · plotnine · anyplot.ai", axis titles ("Quarter", "Market Share"), tick labels, and the bordered "Company" legend (top-right) all render in dark ink and are clearly legible against the light background. + Data: Six 100%-stacked bars (Q1 2023-Q2 2024), each summing to 100%, stacked bottom-to-top as Apple (#009E73 brand green) / Samsung (lavender) / Xiaomi (blue) / Others (muted dark taupe-gray). In-segment percentage labels are correctly positioned: verified Q1 2023 shows Apple 23%, Samsung 22%, Xiaomi 12%, Others 43% (sums to 100, matches source data exactly), each label centered in its own correctly-colored segment with dark-ink text on the lighter purple/blue segments and light-ink text on the darker gray "Others" segment. + Legibility verdict: PASS Dark render (plot-dark.png): - Background: Warm near-black (~#1A1A17), correct dark theme surface. - Chrome: Title, axis titles, and tick labels render in light ink, clearly legible against the dark background -- no dark-on-dark failures observed. Legend box uses an elevated dark background with light text, fully readable. - Data: Same 6 quarters/4 components; data fill colors are identical to the light render (Apple #009E73 green, Samsung purple, Xiaomi blue, Others muted gray) -- confirms the Imprint palette is theme-invariant as required. BUG: every segment gets exactly one label (no doubling/blanks like the light render), but zoomed crops of Q1/Q3/Q4 2023 show the bottom three segments (Apple/Samsung/Xiaomi) are cyclically shifted by one position relative to their true share value -- e.g. Q4 2023: green "Apple" shows "14%" (actually Xiaomi's value; Apple's real share is 22%). Only the top "Others" segment consistently shows its own correct value. - Legibility verdict: PASS for chrome text (no dark-on-dark); FAIL for data-label correctness/placement (see weaknesses). + Background: Warm near-black, matches #1A1A17 -- not pure black. + Chrome: Title, axis titles, tick labels, and legend flip to light off-white / soft light-gray ink and remain fully legible -- no dark-on-dark failures anywhere. + Data: Apple/Samsung/Xiaomi fill colors are pixel-identical to the light render (Imprint categorical positions held constant across themes, as required). "Others" correctly switches to the lighter warm-gray value of the theme-adaptive muted anchor. Verified Q1 2024 shows Apple 21%, Samsung 20%, Xiaomi 15%, Others 44% (sums to 100, matches source data), and Q2 2024 shows Apple 20%, Samsung 19%, Xiaomi 16%, Others 45% -- one correctly-valued label per segment, zero doubling/blank segments (the exact defect that caused the attempt-2 rejection is gone). + Legibility verdict: PASS criteria_checklist: visual_quality: - score: 23 + score: 29 max: 30 items: - id: VQ-01 name: Text Legibility - score: 6 + score: 7 max: 8 passed: true - comment: All glyphs are readable in both themes at good contrast; deducted - because mismatched label values create confusing/misleading reading rather - than a font-rendering issue. + comment: All font sizes explicitly set via theme(); title fits without clipping; + readable in both themes - id: VQ-02 name: No Overlap - score: 0 + score: 6 max: 6 - passed: false - comment: 'Severe: multiple bars have two percentage labels crammed into one - colored segment while an adjacent segment has none (light render), or values - cyclically misassigned across segments (dark render). Confirmed via zoomed - crops of Q1/Q3/Q4 2023 in both themes.' + passed: true + comment: Attempt-2 label misalignment bug fully fixed -- exactly one correctly-valued + label per segment, verified across multiple quarters in both renders - id: VQ-03 name: Element Visibility - score: 5 + score: 6 max: 6 passed: true - comment: Bar segments and fills are clearly visible and distinct; only the - text-label placement is broken. + comment: Bar segments and labels clearly visible and well-sized for 6 categories + x 4 series - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Imprint palette provides adequate contrast and is CVD-safe; no red-green - as sole signal. + comment: WCAG-contrast-computed label color per segment; CVD-safe Imprint + palette - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Canvas is exactly 3200x1800; title, axes, and legend fit comfortably - with no overflow or clipping. + comment: Correct 3200x1800 canvas, balanced margins, legend near plot - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Descriptive axis titles (Quarter, Market Share) with percent-formatted - y-axis. + comment: Descriptive Quarter/Market Share labels, percent-formatted ticks + make units self-evident - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series (Apple) = #009E73; Others uses documented muted semantic - anchor; backgrounds correct in both themes; data colors identical light/dark.' + comment: 'First series #009E73, canonical Imprint order, correct muted semantic + anchor for Others, theme-correct chrome in both renders' design_excellence: - score: 10 + score: 13 max: 20 items: - id: DE-01 @@ -139,24 +116,25 @@ review: score: 5 max: 8 passed: true - comment: Custom contrast-aware label coloring and deliberate semantic color - mapping show real design thought, above generic defaults. + comment: Thoughtful WCAG-aware label coloring and semantic color mapping, + but otherwise fairly standard theme_minimal customization - id: DE-02 name: Visual Refinement score: 4 max: 6 passed: true - comment: Spines/panel border removed, y-only subtle gridlines, generous whitespace - around the legend. + comment: Spines removed, subtle y-only grid, generous whitespace; no additional + refinement flourishes - id: DE-03 name: Data Storytelling - score: 1 + score: 4 max: 6 - passed: false - comment: The mislabeled percentages actively mislead the viewer about each - component's true share, undermining the chart's core storytelling purpose. + passed: true + comment: Correctly-placed in-segment percentage labels now give the chart + a real data-reading focal point, guiding the viewer to exact share values + per component spec_compliance: - score: 10 + score: 15 max: 15 items: - id: SC-01 @@ -164,54 +142,54 @@ review: score: 5 max: 5 passed: true - comment: Correct 100% stacked bar chart via position_fill(). + comment: Correct 100% stacked bar via geom_bar(position_fill()) - id: SC-02 name: Required Features - score: 1 + score: 4 max: 4 - passed: false - comment: In-segment percentage labels are implemented in code but render with - wrong values/positions in every bar of both themes. + passed: true + comment: Percentage labels now render with correct values/positions in every + bar, both themes - id: SC-03 name: Data Mapping - score: 1 + score: 3 max: 3 - passed: false - comment: Bar x/y mapping is correct, but the geom_text layer's y (fill-position) - mapping does not match geom_bar's, producing incorrect data-to-position - mapping for labels. + passed: true + comment: geom_text's y now matches geom_bar's stacking order exactly, via + shared precomputed LabelY - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title matches mandated format exactly; legend labels match company - names. + comment: Title matches mandated format, legend labels match data, legend order + mirrors visual stack data_quality: - score: 11 + score: 15 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 2 + score: 6 max: 6 - passed: false - comment: Bars/components/quarters are all present and sum to 100%, but the - on-canvas percentage labels do not correctly represent each segment's value. + passed: true + comment: 6 quarters x 4 components with genuine quarter-over-quarter variation + in each series - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Plausible, neutral smartphone market-share-by-quarter scenario. + comment: Neutral, comprehensible smartphone-market-share business scenario - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Percentages 0-100%, sensible magnitudes for a market-share domain. + comment: Values and trends are plausible for real-world smartphone market + share code_quality: - score: 8 + score: 9 max: 10 items: - id: CQ-01 @@ -219,58 +197,60 @@ review: score: 2 max: 3 passed: true - comment: Three small helper functions for WCAG contrast add some structure - beyond a flat script, though reasonably justified. + comment: Mostly linear script, but 3 helper functions for WCAG contrast math + deviate from pure imports->data->plot->save - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Fully deterministic hardcoded data, no randomness. + comment: Fully deterministic, no randomness - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only imports that are used. + comment: Only used imports - id: CQ-04 name: Code Elegance - score: 1 + score: 2 max: 2 - passed: false - comment: No fake UI, but the geom_text/position_fill layering has a real functional - defect (see weaknesses). + passed: true + comment: Functional defect from attempt 2 resolved; appropriate complexity + for the WCAG-aware labeling feature - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot-{THEME}.png via plot.save() with correct dpi/size. + comment: Saves as plot-{THEME}.png via plot.save(), current API library_mastery: - score: 5 + score: 7 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 3 + score: 4 max: 5 passed: true - comment: Uses idiomatic plotnine grammar (position_fill, scale_fill_manual, - scale_color_identity), but the text-label layer's position handling is misapplied. + comment: Idiomatic grammar-of-graphics composition; correct usage of position_fill, + scale_fill_manual, theme_minimal - id: LM-02 name: Distinctive Features - score: 2 + score: 3 max: 5 - passed: false - comment: WCAG-contrast-aware label coloring is a distinctive technique, but - its value is undermined by the broken label placement. - verdict: REJECTED + passed: true + comment: scale_color_identity() mapping a precomputed WCAG-contrast column + to per-row text color is a genuinely distinctive grammar-of-graphics technique + verdict: APPROVED impl_tags: dependencies: [] techniques: - annotations patterns: - data-generation - dataprep: [] + - groupby-aggregation + dataprep: + - cumulative-sum styling: - - alpha-blending + - grid-styling