diff --git a/plots/band-basic/implementations/pygal.py b/plots/band-basic/implementations/pygal.py index 2db84ede7c..a99bce8e4c 100644 --- a/plots/band-basic/implementations/pygal.py +++ b/plots/band-basic/implementations/pygal.py @@ -1,7 +1,7 @@ """ pyplots.ai band-basic: Basic Band Plot -Library: pygal 3.1.0 | Python 3.13.11 -Quality: 88/100 | Created: 2025-12-23 +Library: pygal 3.1.0 | Python 3.14 +Quality: 89/100 | Updated: 2026-02-23 """ import numpy as np @@ -9,70 +9,132 @@ from pygal.style import Style -# Data - Time series with 95% confidence interval +# Data - Soil moisture sensor readings with 95% confidence interval np.random.seed(42) -x = np.linspace(0, 10, 50) -# Central trend line (quadratic curve) -y_center = 2 + 0.5 * x + 0.1 * x**2 + np.random.randn(50) * 0.3 -# Smooth the center line -y_center = np.convolve(y_center, np.ones(3) / 3, mode="same") -# Confidence interval widens with x (increasing uncertainty) -uncertainty = 0.5 + 0.15 * x +n_points = 80 +hours = np.linspace(0, 48, n_points) + +# Realistic soil moisture pattern: starts high after rain, drops during day, +# recovers slightly at night, then dips to a low before a second rain event +base_trend = 38 - 0.3 * hours + 4.0 * np.sin(2 * np.pi * hours / 24) + 8.0 * np.exp(-((hours - 40) ** 2) / 8) +noise = np.random.randn(n_points) * 0.6 +y_raw = base_trend + noise + +# Smooth with convolution, padding edges to preserve length +kernel = np.ones(7) / 7 +y_smooth = np.convolve(y_raw, kernel, mode="valid") +pad_left = (n_points - len(y_smooth)) // 2 +pad_right = n_points - len(y_smooth) - pad_left +y_center = np.concatenate([np.full(pad_left, y_smooth[0]), y_smooth, np.full(pad_right, y_smooth[-1])]) + +# Confidence interval: wider during dry spell, narrower after rain +uncertainty = 1.2 + 0.8 * np.sin(2 * np.pi * hours / 24) ** 2 + 0.04 * hours y_lower = y_center - uncertainty y_upper = y_center + uncertainty -# Custom style for 4800x2700 canvas +# Explicit y-axis labels at clean intervals for precise grid control +y_lo = 4 * (int(min(y_lower)) // 4) +y_hi = 4 * (int(max(y_upper)) // 4 + 1) +y_label_values = list(range(y_lo, y_hi + 1, 4)) + +# Custom style — sans-serif typography, polished palette, refined visual hierarchy custom_style = Style( background="white", plot_background="white", - foreground="#333333", - foreground_strong="#333333", - foreground_subtle="#666666", - guide_stroke_color="#888888", # Darker grid lines for better visibility - colors=("#306998", "#FFD43B"), # Blue for band, Yellow for center line - opacity=".65", # Higher opacity for clearly visible band - opacity_hover=".75", - stroke_width=5, # Thicker lines for better visibility + foreground="#2C3E50", + foreground_strong="#1A252F", + foreground_subtle="#BDC3C7", + guide_stroke_color="#F0F0F0", + guide_stroke_dasharray="2,8", + major_guide_stroke_color="#E8E8E8", + major_guide_stroke_dasharray="4,6", + colors=("#306998", "#B8860B", "#7F8C8D"), + opacity=".20", + opacity_hover=".35", + stroke_opacity="1", + stroke_opacity_hover="1", + stroke_width=4, title_font_size=60, label_font_size=42, major_label_font_size=42, - legend_font_size=42, + legend_font_size=40, value_font_size=36, + value_colors=("transparent",), + tooltip_font_size=32, + font_family='Helvetica, Arial, "DejaVu Sans", sans-serif', ) -# Create XY chart for precise coordinate control +# Create XY chart with fine-tuned layout and pygal-specific configuration chart = pygal.XY( style=custom_style, width=4800, height=2700, - title="95% Confidence Interval · band-basic · pygal · pyplots.ai", - x_title="Time (s)", - y_title="Measurement Value", + explicit_size=True, + title="band-basic \u00b7 pygal \u00b7 pyplots.ai", + x_title="Time (hours)", + y_title="Soil Moisture (%)", show_dots=False, - show_x_guides=True, + show_x_guides=False, show_y_guides=True, fill=True, stroke=True, legend_at_bottom=True, + legend_at_bottom_columns=3, + legend_box_size=28, truncate_legend=-1, + x_label_rotation=0, + range=(y_lo - 1, y_hi + 1), + y_labels=y_label_values, + x_labels=[0, 6, 12, 18, 24, 30, 36, 42, 48], + x_labels_major=[0, 12, 24, 36, 48], + show_minor_x_labels=True, + show_minor_y_labels=False, + print_values=False, + x_value_formatter=lambda x: f"{x:.0f}h", + value_formatter=lambda x: f"{x:.1f}%", + tooltip_border_radius=8, + margin_top=30, + margin_bottom=50, + margin_left=30, + margin_right=50, + spacing=18, + js=[], ) -# Create band as a closed polygon: upper boundary forward, then lower backward -# Using fill only (no stroke) to avoid visual artifacts at polygon edges -band_polygon = [] -# Upper boundary (forward) -for xi, yi in zip(x, y_upper, strict=True): - band_polygon.append((float(xi), float(yi))) -# Lower boundary (backward to close the polygon smoothly) -for xi, yi in zip(reversed(x), reversed(y_lower), strict=True): - band_polygon.append((float(xi), float(yi))) +# Band as closed polygon: upper boundary forward, then lower boundary reversed +band_polygon = [(float(h), float(y)) for h, y in zip(hours, y_upper, strict=True)] +for h, y in zip(reversed(hours), reversed(y_lower), strict=True): + band_polygon.append((float(h), float(y))) -chart.add("Confidence Band", band_polygon, stroke=False) +chart.add( + "95% Confidence Band", + band_polygon, + stroke_style={"width": 0.5, "color": "#306998", "opacity": 0.15}, + show_dots=False, +) -# Add center line (no fill, just stroke) - using a contrasting color -center_data = [(float(xi), float(yi)) for xi, yi in zip(x, y_center, strict=True)] -chart.add("Central Trend", center_data, fill=False, stroke=True, dots_size=0, stroke_style={"width": 6}) +# Central trend line — bold stroke with rounded SVG caps for smooth rendering +center_data = [(float(h), float(y)) for h, y in zip(hours, y_center, strict=True)] +chart.add( + "Sensor Mean", + center_data, + fill=False, + stroke=True, + dots_size=0, + stroke_style={"width": 48, "linecap": "round", "linejoin": "round"}, +) + +# Wilting point reference — threshold below which plants cannot extract moisture +chart.add( + "Wilting Point (25%)", + [(0.0, 25.0), (48.0, 25.0)], + fill=False, + stroke=True, + dots_size=0, + formatter=lambda x: f"{x:.0f}%", + stroke_style={"width": 5, "dasharray": "16,10", "linecap": "round"}, +) -# Save outputs +# Save chart.render_to_png("plot.png") chart.render_to_file("plot.html") diff --git a/plots/band-basic/metadata/pygal.yaml b/plots/band-basic/metadata/pygal.yaml index 3da64dc580..61a5afac96 100644 --- a/plots/band-basic/metadata/pygal.yaml +++ b/plots/band-basic/metadata/pygal.yaml @@ -1,38 +1,229 @@ library: pygal specification_id: band-basic created: '2025-12-23T09:10:07Z' -updated: '2025-12-23T09:33:20Z' -generated_by: claude-opus-4-5-20251101 +updated: '2026-02-23T14:52:49Z' +generated_by: claude-opus-4-6 workflow_run: 20456388584 issue: 0 -python_version: 3.13.11 +python_version: '3.14' library_version: 3.1.0 preview_url: https://storage.googleapis.com/pyplots-images/plots/band-basic/pygal/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/band-basic/pygal/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/band-basic/pygal/plot.html -quality_score: 88 +quality_score: 89 impl_tags: dependencies: [] techniques: - html-export patterns: - data-generation - - iteration-over-groups - dataprep: [] + dataprep: + - rolling-window styling: - alpha-blending + - grid-styling review: strengths: - - Excellent color contrast between blue band and yellow center line, colorblind-safe - palette - - Creative polygon approach to create band effect in pygal which lacks native fill_between - - Widening confidence interval clearly demonstrates increasing uncertainty over - time - - Clean, readable code following KISS principles with proper seed for reproducibility - - Proper axis labels with units and descriptive legend + - Excellent data storytelling with soil moisture scenario, wilting point reference, + and varying confidence band width conveying uncertainty changes + - Custom color palette with intentional Python Blue / Dark Goldenrod harmony and + accessible colorblind-safe choices + - Perfect spec compliance with all required features implemented + - Clean, well-structured code with appropriate complexity for the visualization + - Subtle grid styling with custom dasharray and refined guide colors weaknesses: - - Visual artifact at right edge where the polygon closes creates a sharp triangular - taper - - 'Title format should start with spec-id pattern: band-basic · 95% Confidence Interval - · pygal · pyplots.ai' - - Grid lines are quite light/subtle, could be more visible for reference + - Vertical fill artifact at x=0 where the polygon fill extends down to the chart + baseline, creating a visible blue line on the left edge + - Y-axis shows only two labels (20.0% and 40.0%) — too sparse for the data range + of approximately 18-48% + image_description: 'The plot displays a band chart of soil moisture (%) over a 48-hour + period. A semi-transparent light blue filled region represents the 95% confidence + band, with a dark goldenrod/yellow center line showing the sensor mean. A gray + dashed horizontal line at ~25% marks the wilting point threshold. The x-axis is + labeled "Time (hours)" with ticks at 0h, 6h, 12h, 18h, 24h, 30h, 36h, 42h, 48h. + The y-axis is labeled "Soil Moisture (%)" with labels at 20.0% and 40.0%. The + title reads "band-basic · pygal · pyplots.ai". A legend at the bottom shows three + entries: "95% Confidence Band", "Sensor Mean", and "Wilting Point (25%)". The + moisture starts high (~38-42%), drops with a diurnal pattern, reaches a low around + 18h, recovers with a secondary rain event around hour 40, then drops again. The + confidence band widens over time. A visible vertical fill artifact appears at + x=0 on the left edge where the polygon fill extends to the chart baseline.' + criteria_checklist: + visual_quality: + score: 27 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 8 + max: 8 + passed: true + comment: All font sizes explicitly set (title=60, labels=42, legend=40, value=36). + All text clearly readable. + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No overlapping text elements. Legend at bottom well-separated. + - id: VQ-03 + name: Element Visibility + score: 4 + max: 6 + passed: false + comment: Band and center line visible but vertical fill artifact at x=0 drops + to chart bottom. + - id: VQ-04 + name: Color Accessibility + score: 4 + max: 4 + passed: true + comment: Blue band, dark goldenrod center, gray reference — colorblind-safe + with good contrast. + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: false + comment: Good canvas utilization but y-axis shows only 2 labels (20.0%, 40.0%) + — too sparse. + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Time (hours) and Soil Moisture (%) with units. Title in correct format. + design_excellence: + score: 15 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Custom palette with Python Blue and Dark Goldenrod. White background, + sans-serif typography, thoughtful stroke config. + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Subtle grid with custom dasharray, refined guide colors. Sparse y-labels + and artifact are polish issues. + - id: DE-03 + name: Data Storytelling + score: 5 + max: 6 + passed: true + comment: Soil moisture story with wilting point creates clear narrative. Varying + band width communicates uncertainty changes. + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct band plot with filled region and central trend line. + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Semi-transparent fill, contrasting center line, smooth data, CI context + in legend. + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=time, Y=moisture with correct upper/lower/center mapping. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title format correct. Legend labels descriptive and matching data + series. + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Shows varying band width, diurnal patterns, central trend, reference + threshold. + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Soil moisture with 95% CI — real, neutral agricultural/environmental + science scenario. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Soil moisture 20-45% over 48 hours is realistic for agricultural + sensors. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: 'Flat structure: imports, data, style, chart, series, save. No functions + or classes.' + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: np.random.seed(42) set at beginning. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: Only numpy, pygal, pygal.style.Style imported; all used. + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean, Pythonic. Polygon approach appropriate for pygal band plots. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves as plot.png and plot.html. Current pygal API. + library_mastery: + score: 7 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Good pygal usage with XY chart, custom Style, stroke_style dicts, + formatters. Polygon approach is correct pygal pattern. + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: SVG stroke styling, formatters, js=[] for static render, tooltip + config, legend_at_bottom_columns, HTML export. + verdict: REJECTED