Skip to content

Commit 69f2f75

Browse files
update(box-basic): pygal — comprehensive quality review and improvement (#4238)
## Summary Updated **pygal** implementation for **box-basic**. **Changes:** Comprehensive quality review — improved data quality, visual design, code style, and library feature usage. ## Test Plan - [x] Preview images uploaded to GCS staging - [x] Implementation file passes ruff format/check - [x] Metadata YAML updated with current versions - [ ] Automated review triggered --- Generated with [Claude Code](https://claude.com/claude-code) `/update` command --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 060d224 commit 69f2f75

2 files changed

Lines changed: 201 additions & 127 deletions

File tree

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
""" pyplots.ai
22
box-basic: Basic Box Plot
3-
Library: pygal 3.1.0 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: pygal 3.1.0 | Python 3.14
4+
Quality: 90/100 | Created: 2025-12-23
55
"""
66

7+
import re
8+
9+
import cairosvg
710
import numpy as np
811
import pygal
912
from pygal.style import Style
@@ -20,26 +23,33 @@
2023
"HR": np.random.normal(58000, 8000, 100),
2124
}
2225

23-
# Add some outliers to demonstrate box plot features
26+
# Add outliers to demonstrate box plot features
2427
data["Engineering"] = np.append(data["Engineering"], [130000, 135000, 40000])
2528
data["Sales"] = np.append(data["Sales"], [120000, 25000])
2629

27-
# Custom style using PyPlots colors
30+
# Compute medians for annotation storytelling
31+
medians = {cat: float(np.median(data[cat])) for cat in categories}
32+
highest_dept = max(medians, key=medians.get)
33+
lowest_dept = min(medians, key=medians.get)
34+
35+
# Custom style for 4800x2700 canvas — strong, vivid colors
2836
custom_style = Style(
2937
background="white",
30-
plot_background="white",
38+
plot_background="#FAFAFA",
3139
foreground="#333333",
32-
foreground_strong="#333333",
33-
foreground_subtle="#666666",
34-
colors=("#306998", "#FFD43B", "#4CAF50", "#FF5722", "#9C27B0"),
35-
title_font_size=60,
36-
label_font_size=40,
37-
major_label_font_size=36,
38-
legend_font_size=36,
39-
value_font_size=32,
40+
foreground_strong="#222222",
41+
foreground_subtle="#E0E0E0",
42+
colors=("#306998", "#E69F00", "#009E73", "#D55E00", "#7B68EE"),
43+
title_font_size=72,
44+
label_font_size=48,
45+
major_label_font_size=44,
46+
legend_font_size=44,
47+
value_font_size=36,
48+
opacity=1.0,
49+
opacity_hover=1.0,
4050
)
4151

42-
# Create box chart
52+
# Create box chart — set y range to focus on actual data, not starting at 0
4353
chart = pygal.Box(
4454
width=4800,
4555
height=2700,
@@ -49,17 +59,45 @@
4959
y_title="Salary ($)",
5060
show_legend=True,
5161
legend_at_bottom=True,
52-
legend_box_size=24,
62+
legend_at_bottom_columns=5,
63+
legend_box_size=36,
64+
truncate_legend=-1,
65+
truncate_label=-1,
5366
show_y_guides=True,
5467
show_x_guides=False,
55-
margin=50,
68+
margin=80,
69+
spacing=40,
5670
box_mode="tukey",
71+
range=(5000, 145000),
72+
y_labels=[20000, 40000, 60000, 80000, 100000, 120000, 140000],
5773
)
5874

5975
# Add data for each category
6076
for category in categories:
6177
chart.add(category, data[category].tolist())
6278

79+
# Render SVG, then post-process for visual improvements
80+
svg_string = chart.render().decode("utf-8")
81+
82+
# Fix 1: Increase box fill opacity from 0.2 to 0.7 (pygal hardcodes subtle-fill)
83+
svg_string = svg_string.replace(".subtle-fill{fill-opacity:.2}", ".subtle-fill{fill-opacity:.7}")
84+
85+
# Fix 2: Enlarge outlier dots (pygal hardcodes r=3 for box outliers)
86+
svg_string = re.sub(r'(<circle[^>]*) r="3" (class="subtle-fill)', r'\1 r="10" \2', svg_string)
87+
88+
# Fix 3: Add storytelling annotation as subtitle
89+
annotation_svg = (
90+
f'<text x="2400" y="200" text-anchor="middle" '
91+
f'font-size="38" fill="#555555" font-family="sans-serif" font-style="italic">'
92+
f"Highest median: {highest_dept} (${medians[highest_dept]:,.0f})"
93+
f" \u00b7 Lowest median: {lowest_dept} (${medians[lowest_dept]:,.0f})"
94+
f" \u00b7 Gap: ${medians[highest_dept] - medians[lowest_dept]:,.0f}"
95+
f"</text>"
96+
)
97+
svg_string = svg_string.replace("</svg>", f"{annotation_svg}</svg>")
98+
6399
# Save outputs
64-
chart.render_to_file("plot.html")
65-
chart.render_to_png("plot.png")
100+
with open("plot.html", "w") as f:
101+
f.write(svg_string)
102+
103+
cairosvg.svg2png(bytestring=svg_string.encode("utf-8"), write_to="plot.png")

0 commit comments

Comments
 (0)