Skip to content

feat(letsplot): implement timeseries-forecast-uncertainty#7401

Merged
MarkusNeusinger merged 3 commits into
mainfrom
implementation/timeseries-forecast-uncertainty/letsplot
May 19, 2026
Merged

feat(letsplot): implement timeseries-forecast-uncertainty#7401
MarkusNeusinger merged 3 commits into
mainfrom
implementation/timeseries-forecast-uncertainty/letsplot

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Implementation: timeseries-forecast-uncertainty - python/letsplot

Implements the python/letsplot version of timeseries-forecast-uncertainty.

File: plots/timeseries-forecast-uncertainty/implementations/python/letsplot.py

Parent Issue: #3188


🤖 impl-generate workflow

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 19, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): The plot renders on a warm off-white (#FAF8F1) background. The historical period (2023–2025) is shown as a solid green (#009E73) line with visible seasonal oscillation and an upward trend. The forecast period (2026–2027) is a dashed orange (#E69F00) line with two nested confidence bands: a darker 80% CI band (alpha 0.38) and a lighter outer 95% CI band (alpha 0.18), both in the same orange color family. A dotted vertical line marks the forecast start at 2026-01. The bottom legend labels "Historical" and "Forecast"; a bottom-right caption reads "Bands: 80% CI (darker) · 95% CI (lighter)". X-axis: "Date"; Y-axis: "Energy Demand (MWh)". Title reads "timeseries-forecast-uncertainty · python · letsplot · anyplot.ai" at approximately 75% of plot width. All text is clearly readable against the light background — no light-on-light failures.

Dark render (plot-dark.png): The same plot renders on a near-black (#1A1A17) background. Data colors are identical: green (#009E73) historical line, orange (#E69F00) forecast line and CI bands. All chrome adapts correctly — title, axis labels, tick labels, legend text, and caption are all rendered in light-colored text (INK/INK_SOFT tokens), clearly readable against the dark background. The 80% CI band remains clearly visible; the 95% CI outer band (alpha 0.18) is subtle on the dark surface but its boundary remains distinguishable. The band boundary outlines are visible in orange. No dark-on-dark failures detected.

Both paragraphs are required. A review that only describes one render is invalid.

Score: 90/100

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

Visual Quality (29/30)

  • VQ-01: Text Legibility (8/8) — All font sizes explicitly set (title=18, axis_title=14, axis_text=12, legend_text=12, caption=11). Proportions good in both renders; mandated title fills ~75% of width as expected.
  • VQ-02: No Overlap (6/6) — No text collisions in either render. Legend at bottom is well-separated; caption in corner is unobstructed.
  • VQ-03: Element Visibility (5/6) — Historical and forecast lines clearly visible. 80% CI band well-rendered in both themes. 95% CI (alpha=0.18) is subtly rendered on the dark surface — distinguishable via boundary lines but the fill is quite muted there.
  • VQ-04: Color Accessibility (2/2) — Green/orange Okabe-Ito pair is CVD-safe. Dual encoding (color + linestyle solid/dashed) ensures accessibility beyond hue alone.
  • VQ-05: Layout & Canvas (4/4) — 3200×1800 px canvas well-utilized. Generous margins; legend below plot; caption in corner; no content cut off.
  • VQ-06: Axis Labels & Title (2/2) — Y-axis has units: "Energy Demand (MWh)". Title correct format.
  • VQ-07: Palette Compliance (2/2) — First series (Historical) = #009E73 ✓. Forecast = #E69F00 (Okabe-Ito position 5) ✓. Background #FAF8F1 / #1A1A17 ✓. All chrome theme-adaptive in both renders ✓.

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) — Above a plain library default: warm theme-adaptive backgrounds, Okabe-Ito palette, linetype differentiation, clean L-spine layout. Not quite "strong design" (6) but clearly above a generic default.
  • DE-02: Visual Refinement (4/6) — theme_classic() removes top/right spines. Y-only subtle grid (INK_GRID at 10% opacity). Warm backgrounds and bottom legend placement are polished. Clean visual hierarchy.
  • DE-03: Data Storytelling (4/6) — Clear narrative arc: green solid history → dotted boundary → dashed orange future with widening uncertainty bands. Color contrast and linestyle create immediate visual distinction between past and forecast. Caption adds informative context. Guides the viewer naturally from observation to projection.

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Time series with historical line, forecast with confidence bands, and vertical forecast-start marker. All subtypes present.
  • SC-02: Required Features (4/4) — Solid historical / dashed forecast lines, vertical dotted boundary line, nested 80% + 95% CI bands with semi-transparent fills, legend and caption describing all elements.
  • SC-03: Data Mapping (3/3) — X: datetime axis (2023–2027), Y: energy demand. All data visible within axis bounds.
  • SC-04: Title & Legend (3/3) — Title: "timeseries-forecast-uncertainty · python · letsplot · anyplot.ai" ✓. Legend labels: "Historical" / "Forecast" ✓.

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Seasonal oscillation, upward trend, realistic noise, widening uncertainty over forecast horizon — all key features of the plot type demonstrated.
  • DQ-02: Realistic Context (5/5) — Monthly energy demand forecasting is a real, neutral, everyday application. No controversial subject matter.
  • DQ-03: Appropriate Scale (4/4) — Values 400–620 MWh plausible for district-level energy. Seasonal amplitude, trend slope, and growing uncertainty width all realistic for ARIMA/Prophet outputs.

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Clean linear structure: imports → tokens → data generation → plot → save.
  • CQ-02: Reproducibility (2/2) — np.random.seed(42) set.
  • CQ-03: Clean Imports (2/2) — Only used imports; noqa comments appropriate for lets_plot wildcard import pattern.
  • CQ-04: Code Elegance (2/2) — Clean grammar-of-graphics composition; theme tokens well-organized; no over-engineering.
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png (scale=4) and plot-{THEME}.html correctly.

Library Mastery (8/10)

  • LM-01: Idiomatic Usage (5/5) — Expertly uses lets-plot grammar: ggplot() + geom_ribbon() + geom_line() + geom_vline() + scale_color_manual() + theme_classic() + theme(). Correct ggsize() and ggsave() with scale parameter.
  • LM-02: Distinctive Features (3/5) — Uses geom_ribbon() for CI bands, datetime-as-milliseconds xintercept pattern specific to lets-plot (forecast_start.timestamp() * 1000), and HTML export. Multi-layer composition shows good library depth, though the patterns are shared with other gg-based libraries.

Score Caps Applied

  • None — DE-01=5 > 2 and DE-02=4 > 2 so the 75-cap does not apply.

Strengths

  • Full spec compliance: all required elements (solid/dashed lines, nested CI bands, vline marker, legend, caption) correctly implemented
  • Excellent theme adaptation: all chrome tokens (backgrounds, text, grid) flip correctly between light and dark without any dark-on-dark failures
  • Clean data story: green→orange color contrast plus solid→dashed linetype creates immediate visual clarity of past vs. future
  • Perfect code quality: reproducible, KISS structure, idiomatic lets-plot API usage

Weaknesses

  • 95% CI band (alpha=0.18) is very subtle in dark render — consider increasing to 0.22–0.25 or adding a thin boundary line to the outer ribbon for better dark-mode visibility
  • LM-02 could be higher: no use of lets-plot-exclusive interactive features (e.g., tooltips in the HTML output, geom_errorbar alternatives, or lets-plot's built-in theming system beyond basics)

Issues Found

  1. VQ-03 MINOR: 95% CI outer band is barely distinguishable from background in dark render due to alpha=0.18 being too low on dark surface.
    • Fix: Increase outer ribbon alpha to 0.22–0.25, or add boundary line to geom_ribbon for the 95% CI.

AI Feedback for Next Attempt

Implementation is strong. If repair is needed: increase the 95% CI ribbon alpha slightly (0.22–0.25) for better dark-mode visibility while keeping lighter than the 80% CI. Consider adding a thin boundary line to geom_ribbon for outer CI. To push LM-02 higher, add tooltip aesthetics to the HTML output or use lets-plot's interactive layer features.

Verdict: APPROVED

@github-actions github-actions Bot added quality:90 Quality score 90/100 ai-approved Quality OK, ready for merge labels May 19, 2026
@MarkusNeusinger MarkusNeusinger merged commit 061b09f into main May 19, 2026
@MarkusNeusinger MarkusNeusinger deleted the implementation/timeseries-forecast-uncertainty/letsplot branch May 19, 2026 13:24
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 quality:90 Quality score 90/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant