Skip to content

feat(plotnine): implement bar-stacked-percent - #10442

Merged
MarkusNeusinger merged 8 commits into
mainfrom
implementation/bar-stacked-percent/plotnine
Aug 18, 2026
Merged

feat(plotnine): implement bar-stacked-percent#10442
MarkusNeusinger merged 8 commits into
mainfrom
implementation/bar-stacked-percent/plotnine

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Implementation: bar-stacked-percent - python/plotnine

Implements the python/plotnine version of bar-stacked-percent.

File: plots/bar-stacked-percent/implementations/python/plotnine.py

Parent Issue: #2008


🤖 impl-generate workflow

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.
@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white (#FAF8F1) background. Title "bar-stacked-percent · python · plotnine · anyplot.ai" and axis titles ("Quarter", "Market Share") render in dark ink; tick labels in softer dark gray. Six bars stack Apple (brand green #009E73) / Samsung (lavender #C475FD) / Xiaomi (blue #4467A3) / Others (muted dark taupe-gray) bottom-to-top, each summing to 100%. Bordered legend box top-right lists all four components. All text is clearly readable against the light background — no light-on-light issues.

Dark render (plot-dark.png): Warm near-black (#1A1A17) background. Title, axis titles, and tick labels flip to light off-white / soft light gray and remain fully legible — no dark-on-dark failures. The Apple/Samsung/Xiaomi bar colors are pixel-identical to the light render, confirming the Imprint categorical positions stay constant across themes. "Others" correctly switches to the lighter warm-gray value of the muted semantic anchor, which is theme-adaptive by design (unlike the 8 categorical positions) — this is the right choice for an "other/rest" aggregate bucket.

Both renders pass legibility and theme-adaptation checks.

Score: 88/100

Category Score Max
Visual Quality 29 30
Design Excellence 12 20
Spec Compliance 15 15
Data Quality 15 15
Code Quality 10 10
Library Mastery 7 10
Total 88 100

Visual Quality (29/30)

  • VQ-01: Text Legibility (7/8)
  • VQ-02: No Overlap (6/6)
  • VQ-03: Element Visibility (6/6)
  • VQ-04: Color Accessibility (2/2)
  • VQ-05: Layout & Canvas (4/4)
  • VQ-06: Axis Labels & Title (2/2)
  • VQ-07: Palette Compliance (2/2)

Design Excellence (12/20)

  • DE-01: Aesthetic Sophistication (5/8) - Thoughtful semantic color mapping for "Others", but otherwise fairly standard theme_minimal customization
  • DE-02: Visual Refinement (5/6) - Panel border removed, grid reduced to faint y-major only, generous whitespace
  • DE-03: Data Storytelling (2/6) - No annotation, highlighted trend, or in-segment percentage labels

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5)
  • SC-02: Required Features (4/4)
  • SC-03: Data Mapping (3/3)
  • SC-04: Title & Legend (3/3)

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6)
  • DQ-02: Realistic Context (5/5)
  • DQ-03: Appropriate Scale (4/4)

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3)
  • CQ-02: Reproducibility (2/2)
  • CQ-03: Clean Imports (2/2)
  • CQ-04: Code Elegance (2/2)
  • CQ-05: Output & API (1/1)

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (5/5)
  • LM-02: Distinctive Features (2/5) - position_fill is required for the plot type itself rather than a distinctive extra; no additional plotnine-specific flourish

Score Caps Applied

  • None

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 3200×1800 landscape canvas via figure_size=(8, 4.5) at dpi=400

Weaknesses

  • No visual hierarchy or data storytelling beyond the base composition view — 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 — e.g. no guide/legend reordering, no direct segment labeling
  • Aesthetic polish is solid but fairly restrained beyond the palette choice — no additional refinement flourishes (e.g. subtle bar edge stroke, custom legend key styling) beyond the standard theme_minimal + token overrides

Issues Found

  1. DE-03 LOW: No visual hierarchy or focal point in the composition
    • Fix: Add geom_text percentage labels inside each segment (using position_fill(vjust=0.5)), or highlight one company's trend with a subtitle/annotation
  2. LM-02 LOW: Only the baseline mechanism for the plot type is used
    • Fix: Consider a distinctive touch such as in-segment labels or explicit legend ordering via guides()

AI Feedback for Next Attempt

This implementation is already strong (correct spec compliance, clean theme-adaptive palette, no defects). If revisited, focus on Design Excellence and Library Mastery: add percentage labels inside the wider segments (Apple/Samsung/Xiaomi/Others all have room) to give the chart a stronger data-storytelling focal point, and consider a small distinctive plotnine touch beyond the required position_fill() mechanism.

Verdict: APPROVED

@github-actions github-actions Bot added quality:88 Quality score: 88/100 ai-rejected Quality not OK, triggers update labels Aug 18, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels Aug 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 1/4

Applied fixes based on AI review feedback.

Status: Repair completed, re-triggering review...


🤖 impl-repair

@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): Warm off-white (~#FAF8F1) background, correct light theme. Title "bar-stacked-percent · python · plotnine · anyplot.ai", axis titles ("Quarter"/"Market Share"), tick labels, and the "Company" legend are all dark/soft-dark and clearly legible. Six 100%-stacked quarterly bars (Q1 2023–Q2 2024), four components each (Apple #009E73 green, Samsung purple, Xiaomi blue, Others muted gray), each bar correctly sums to 100%. However, zoomed crops of Q1, Q3, and Q4 2023 show the in-segment percentage labels are misplaced: several bars have two labels crammed into one colored segment (e.g. Q1's gray "Others" segment shows both "22%" and "23%") while an adjacent segment has no label at all (e.g. the blue "Xiaomi" segment). All text is readable against the light background — this is a data-label placement bug, not a legibility bug.

Dark render (plot-dark.png): Warm near-black (~#1A1A17) background, correct dark theme. Title, axis titles, tick labels, and legend render in light ink and are clearly legible against the dark background — no dark-on-dark failures. Data fill colors are identical to the light render, confirming the Imprint palette is theme-invariant as required. However, the same underlying bug manifests differently here: every segment gets exactly one label (no doubling/blanks), but the bottom three segments (Apple/Samsung/Xiaomi) are cyclically shifted by one position relative to their true value — e.g. Q4 2023's green "Apple" segment shows "14%" (actually Xiaomi's real share; Apple's real share is 22%). Only the top "Others" segment consistently shows its own correct value.

Both renders are theme-readable, but both misrepresent the underlying data through mislabeled segments.

Score: 49/100

Category Score Max
Visual Quality 23 30
Design Excellence 10 20
Spec Compliance 10 15
Data Quality 11 15
Code Quality 8 10
Library Mastery 5 10
Raw Total 67 100
Capped Total 49 100

Visual Quality (23/30)

  • VQ-01: Text Legibility (6/8)
  • VQ-02: No Overlap (0/6) — severe: multiple bars have two labels crammed into one segment with an adjacent segment left blank (light), or values cyclically misassigned across segments (dark)
  • VQ-03: Element Visibility (5/6)
  • VQ-04: Color Accessibility (2/2)
  • VQ-05: Layout & Canvas (4/4)
  • VQ-06: Axis Labels & Title (2/2)
  • VQ-07: Palette Compliance (2/2)

Design Excellence (10/20)

  • DE-01: Aesthetic Sophistication (5/8) - Custom WCAG contrast-aware label coloring, deliberate semantic color mapping
  • DE-02: Visual Refinement (4/6) - Spines removed, subtle y-only grid, generous whitespace
  • DE-03: Data Storytelling (1/6) - Mislabeled percentages actively mislead the viewer about true component shares

Spec Compliance (10/15)

  • SC-01: Plot Type (5/5)
  • SC-02: Required Features (1/4) - Percentage labels coded but render with wrong values/positions in every bar, both themes
  • SC-03: Data Mapping (1/3) - geom_text's y (fill-position) mapping does not match geom_bar's
  • SC-04: Title & Legend (3/3)

Data Quality (11/15)

  • DQ-01: Feature Coverage (2/6) - On-canvas labels don't correctly represent each segment's value
  • DQ-02: Realistic Context (5/5)
  • DQ-03: Appropriate Scale (4/4)

Code Quality (8/10)

  • CQ-01: KISS Structure (2/3)
  • CQ-02: Reproducibility (2/2)
  • CQ-03: Clean Imports (2/2)
  • CQ-04: Code Elegance (1/2) - geom_text/position_fill layering has a real functional defect
  • CQ-05: Output & API (1/1)

Library Mastery (5/10)

  • LM-01: Idiomatic Usage (3/5) - Idiomatic grammar, but text-label layer position handling is misapplied
  • LM-02: Distinctive Features (2/5) - Contrast-aware label coloring undermined by broken placement

Score Caps Applied

  • VQ-02 = 0 (severe overlap/misplacement) → capped at 49. Raw weighted total was 67/100; capped per the score-cap table.

Strengths

  • Correct 100% stacked bar structure via geom_bar(stat="identity", position=position_fill()); every bar sums to 100%.
  • WCAG-contrast-aware in-segment label coloring (_relative_luminance/_contrast_ratio/_label_color) is a genuinely sophisticated touch.
  • Imprint palette used correctly: Apple (first categorical series) = #009E73; "Others" uses a documented muted semantic anchor.
  • Theme-adaptive chrome correctly threaded through both renders (correct backgrounds, legible text, subtle gridlines).
  • Canvas size correct (3200×1800), title format matches the mandated pattern, legend order mirrors 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. Confirmed via zoomed crops of Q1/Q3/Q4 2023 in both renders. Light render: several bars have two labels in one segment and an adjacent segment with none (e.g. Q1 2023's gray "Others" shows both "22%" and "23%" while blue "Xiaomi" is unlabeled). Dark render: one label per segment, but the bottom three segments are cyclically shifted by one position (e.g. Q4 2023's green "Apple" segment shows "14%", which is actually Xiaomi's real value). This is systemic across every bar in both themes.
  • Likely root cause: geom_bar's and geom_text's independent position_fill() calls compute different per-group cumulative offsets because the dataframe rows are grouped by company (all-Apple, then all-Samsung, then all-Xiaomi, then all-Others) rather than interleaved to match the stacking order. Fix by forcing both layers onto identical offsets — e.g. precompute the cumulative fill midpoint per (Quarter, Company) as an explicit y column matching the bar's stack order, and pass it to geom_text(aes(y=label_y), position='identity') instead of relying on geom_text's own position_fill. Re-verify by zooming into every bar in both themes and confirming exactly one correctly-valued label lands in its own correctly colored segment.
  • Because labels show numbers under the wrong segment, the chart currently gives an incorrect visual takeaway to viewers, undermining the "percentage labels within segments" feature suggested by the spec even though it is coded.

Issues Found

  1. VQ-02 / SC-02 / SC-03 CRITICAL: geom_text percentage labels are misaligned with their own segments in every bar, both themes (doubled/blank segments in light; cyclic value shift in dark).
    • Fix: Precompute per-(Quarter, Company) cumulative fill midpoints as an explicit y column matching geom_bar's stacking order, and drive geom_text(aes(y=label_y), position='identity') from it instead of a second independent position_fill() call.
  2. DE-03 LOW: Because of the label bug, the chart currently misleads rather than informs.
    • Fix: Resolves automatically once the label positions are corrected.

AI Feedback for Next Attempt

Priority fix: the in-segment percentage labels do not match their own bar segment in either theme (verified via zoomed crops of Q1/Q3/Q4 2023). geom_bar and geom_text each independently call position_fill(), producing inconsistent per-group cumulative offsets since the dataframe rows are grouped by company rather than interleaved to match the stack order. Precompute the cumulative fill midpoint per (Quarter, Company) explicitly (matching the bar's own stacking order) and feed it to geom_text via aes(y=label_y) with position='identity' rather than a second position_fill(). After fixing, re-render both themes and visually confirm — by zooming into each bar if needed — that exactly one correctly-valued label sits centered in its own correctly colored segment for all 6 quarters.

Verdict: REJECTED

@github-actions github-actions Bot added quality:49 Quality score 49/100 ai-rejected Quality not OK, triggers update quality-poor Score < 50, needs fundamental fixes and removed quality:88 Quality score: 88/100 labels Aug 18, 2026
@github-actions github-actions Bot added ai-attempt-2 Second repair attempt and removed ai-rejected Quality not OK, triggers update labels Aug 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 2/4

Applied fixes based on AI review feedback.

Status: Repair completed, re-triggering review...


🤖 impl-repair

@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 3/3

Image Description

Light render (plot-light.png): Warm off-white (#FAF8F1) background. Title "bar-stacked-percent · python · plotnine · anyplot.ai", axis titles ("Quarter"/"Market Share"), tick labels, and the bordered "Company" legend all render in dark ink and are clearly legible. Six 100%-stacked bars (Q1 2023-Q2 2024) stack Apple (#009E73 green) / Samsung (lavender) / Xiaomi (blue) / Others (muted gray) bottom-to-top, each summing to 100%. The attempt-2 label bug is fixed: zoomed crops confirm Q1 2023 shows Apple 23% / Samsung 22% / Xiaomi 12% / Others 43% (sums to 100, matches the source data exactly), each label centered in its own correctly-colored segment. All text is readable against the light background.

Dark render (plot-dark.png): Warm near-black (#1A1A17) background. Title, axis titles, tick labels, and legend flip to light ink and remain fully legible — no dark-on-dark failures. Apple/Samsung/Xiaomi fill colors are pixel-identical to the light render, confirming the Imprint categorical positions hold across themes; "Others" correctly switches to the lighter warm-gray value of the theme-adaptive muted anchor. Zoomed crops confirm Q1 2024 (Apple 21% / Samsung 20% / Xiaomi 15% / Others 44%) and Q2 2024 (Apple 20% / Samsung 19% / Xiaomi 16% / Others 45%) each sum to 100 with exactly one correctly-valued label per segment — zero doubling/blank segments, the exact defect that caused the attempt-2 rejection.

Both renders pass legibility, theme-adaptation, and label-correctness checks.

Score: 88/100

Category Score Max
Visual Quality 29 30
Design Excellence 13 20
Spec Compliance 15 15
Data Quality 15 15
Code Quality 9 10
Library Mastery 7 10
Total 88 100

Visual Quality (29/30)

  • VQ-01: Text Legibility (7/8)
  • VQ-02: No Overlap (6/6)
  • VQ-03: Element Visibility (6/6)
  • VQ-04: Color Accessibility (2/2)
  • VQ-05: Layout & Canvas (4/4)
  • VQ-06: Axis Labels & Title (2/2)
  • VQ-07: Palette Compliance (2/2)

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) - Thoughtful WCAG-aware label coloring and semantic color mapping, but otherwise fairly standard theme_minimal customization
  • DE-02: Visual Refinement (4/6) - Spines removed, subtle y-only grid, generous whitespace
  • DE-03: Data Storytelling (4/6) - Correctly-placed in-segment percentage labels now give the chart a real data-reading focal point

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5)
  • SC-02: Required Features (4/4)
  • SC-03: Data Mapping (3/3)
  • SC-04: Title & Legend (3/3)

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6)
  • DQ-02: Realistic Context (5/5)
  • DQ-03: Appropriate Scale (4/4)

Code Quality (9/10)

  • CQ-01: KISS Structure (2/3)
  • CQ-02: Reproducibility (2/2)
  • CQ-03: Clean Imports (2/2)
  • CQ-04: Code Elegance (2/2)
  • CQ-05: Output & API (1/1)

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5)
  • LM-02: Distinctive Features (3/5) - scale_color_identity() driving per-segment WCAG-contrast text color is a genuinely distinctive grammar-of-graphics technique

Score Caps Applied

  • None

Strengths

  • 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) 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; canvas is correct (3200×1800), title format matches the mandated pattern, legend order mirrors the visual stacking order.

Weaknesses

  • 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.

Issues Found

None blocking. Minor polish opportunities noted above (DE-01/DE-02/LM-01) do not require another repair cycle.

AI Feedback for Next Attempt

N/A — approved. If ever revisited, a small refinement flourish (bar-edge stroke, explicit legend ordering via guides()) would push Design Excellence and Library Mastery higher.

Verdict: APPROVED

@github-actions github-actions Bot added quality:88 Quality score: 88/100 ai-approved Quality OK, ready for merge and removed quality:49 Quality score 49/100 labels Aug 18, 2026
@MarkusNeusinger
MarkusNeusinger merged commit 7d87938 into main Aug 18, 2026
@MarkusNeusinger
MarkusNeusinger deleted the implementation/bar-stacked-percent/plotnine branch August 18, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-approved Quality OK, ready for merge ai-attempt-1 First repair attempt ai-attempt-2 Second repair attempt quality:88 Quality score: 88/100 quality-poor Score < 50, needs fundamental fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant