Skip to content

Commit ee0a269

Browse files
update(box-basic): highcharts — comprehensive quality review and improvement
1 parent 150c632 commit ee0a269

2 files changed

Lines changed: 112 additions & 67 deletions

File tree

Lines changed: 107 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" pyplots.ai
22
box-basic: Basic Box Plot
3-
Library: highcharts unknown | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: highcharts 1.10.3 | Python 3.14
4+
Quality: /100 | Updated: 2026-02-14
55
"""
66

77
import tempfile
@@ -18,127 +18,176 @@
1818
from selenium.webdriver.chrome.options import Options
1919

2020

21-
# Data - generate sample data for 5 categories with different distributions
21+
# Data - employee performance scores across 5 departments
2222
np.random.seed(42)
23-
categories = ["Group A", "Group B", "Group C", "Group D", "Group E"]
23+
departments = ["Engineering", "Marketing", "Sales", "Design", "Finance"]
2424
colors = ["#306998", "#FFD43B", "#9467BD", "#17BECF", "#8C564B"]
2525

26-
# Generate raw data (100 points each with different means and spreads)
27-
raw_data = [
28-
np.random.normal(50, 10, 100), # Group A: moderate mean, moderate spread
29-
np.random.normal(65, 15, 100), # Group B: higher mean, larger spread
30-
np.random.normal(45, 8, 100), # Group C: lower mean, tighter spread
31-
np.random.normal(70, 12, 100), # Group D: highest mean
32-
np.random.normal(55, 20, 100), # Group E: moderate mean, widest spread
26+
scores = [
27+
np.random.normal(78, 8, 80), # Engineering: high, tight
28+
np.random.normal(72, 14, 60), # Marketing: moderate, wide spread
29+
np.random.normal(68, 9, 90), # Sales: lower mean, moderate
30+
np.random.normal(82, 7, 50), # Design: highest, tight
31+
np.random.normal(75, 18, 70), # Finance: moderate, widest spread
3332
]
3433

35-
# Calculate box plot statistics (inline, no functions)
36-
box_data = []
34+
# Calculate box plot statistics
35+
box_stats = []
3736
outlier_data = []
3837

39-
for i, data in enumerate(raw_data):
38+
for i, data in enumerate(scores):
39+
data = np.clip(data, 0, 100)
4040
q1 = float(np.percentile(data, 25))
4141
median = float(np.percentile(data, 50))
4242
q3 = float(np.percentile(data, 75))
4343
iqr = q3 - q1
44-
whisker_low = max(float(data.min()), q1 - 1.5 * iqr)
45-
whisker_high = min(float(data.max()), q3 + 1.5 * iqr)
46-
47-
# Box data: [low, q1, median, q3, high]
48-
box_data.append(
49-
{"low": whisker_low, "q1": q1, "median": median, "q3": q3, "high": whisker_high, "color": colors[i]}
44+
whisker_low = float(max(data[data >= q1 - 1.5 * iqr].min(), data.min()))
45+
whisker_high = float(min(data[data <= q3 + 1.5 * iqr].max(), data.max()))
46+
47+
box_stats.append(
48+
{
49+
"low": round(whisker_low, 1),
50+
"q1": round(q1, 1),
51+
"median": round(median, 1),
52+
"q3": round(q3, 1),
53+
"high": round(whisker_high, 1),
54+
}
5055
)
5156

52-
# Find and add outliers
53-
outliers = data[(data < whisker_low) | (data > whisker_high)]
54-
for outlier in outliers:
55-
outlier_data.append([i, float(outlier)])
57+
outliers = data[(data < q1 - 1.5 * iqr) | (data > q3 + 1.5 * iqr)]
58+
for val in outliers:
59+
outlier_data.append({"x": i, "y": round(float(val), 1)})
60+
61+
# Build fill colors (75% opacity)
62+
fill_colors = []
63+
for c in colors:
64+
r, g, b = int(c[1:3], 16), int(c[3:5], 16), int(c[5:7], 16)
65+
fill_colors.append(f"rgba({r}, {g}, {b}, 0.75)")
5666

5767
# Create chart
5868
chart = Chart(container="container")
5969
chart.options = HighchartsOptions()
6070

61-
# Chart configuration
6271
chart.options.chart = {
6372
"type": "boxplot",
6473
"width": 4800,
6574
"height": 2700,
6675
"backgroundColor": "#ffffff",
67-
"marginBottom": 280,
68-
"spacingBottom": 80,
76+
"marginBottom": 260,
77+
"spacingBottom": 60,
78+
"spacingLeft": 40,
79+
"style": {"fontFamily": "Arial, Helvetica, sans-serif"},
6980
}
7081

71-
# Title
7282
chart.options.title = {
73-
"text": "box-basic · highcharts · pyplots.ai",
74-
"style": {"fontSize": "72px", "fontWeight": "bold"},
83+
"text": "box-basic \u00b7 highcharts \u00b7 pyplots.ai",
84+
"style": {"fontSize": "64px", "fontWeight": "600", "color": "#2c3e50"},
85+
"margin": 60,
86+
}
87+
88+
chart.options.subtitle = {
89+
"text": "Annual Performance Review Scores by Department",
90+
"style": {"fontSize": "42px", "color": "#7f8c8d"},
7591
}
7692

77-
# X-axis
7893
chart.options.x_axis = {
79-
"categories": categories,
80-
"title": {"text": "Category", "style": {"fontSize": "48px"}},
81-
"labels": {"style": {"fontSize": "40px"}},
94+
"categories": departments,
95+
"title": {"text": "Department", "style": {"fontSize": "44px", "color": "#34495e"}},
96+
"labels": {"style": {"fontSize": "38px", "color": "#34495e"}},
97+
"lineColor": "#bdc3c7",
98+
"lineWidth": 2,
99+
"tickWidth": 0,
82100
}
83101

84-
# Y-axis
85102
chart.options.y_axis = {
86-
"title": {"text": "Value", "style": {"fontSize": "48px"}},
87-
"labels": {"style": {"fontSize": "36px"}},
103+
"title": {"text": "Score (out of 100)", "style": {"fontSize": "44px", "color": "#34495e"}},
104+
"labels": {"style": {"fontSize": "34px", "color": "#7f8c8d"}},
88105
"gridLineWidth": 1,
89-
"gridLineColor": "rgba(0, 0, 0, 0.1)",
106+
"gridLineColor": "rgba(0, 0, 0, 0.06)",
107+
"tickInterval": 5,
90108
}
91109

92-
# Legend
93-
chart.options.legend = {"enabled": True, "itemStyle": {"fontSize": "36px"}}
110+
chart.options.legend = {"enabled": False}
111+
chart.options.credits = {"enabled": False}
94112

95-
# Plot options for box styling
96113
chart.options.plot_options = {
97114
"boxplot": {
98115
"lineWidth": 4,
99116
"medianWidth": 6,
100117
"medianColor": "#1a1a1a",
101118
"stemWidth": 3,
119+
"stemDashStyle": "Solid",
102120
"whiskerWidth": 4,
103121
"whiskerLength": "50%",
104-
"colorByPoint": True,
122+
"whiskerColor": "#555555",
123+
"pointWidth": 350,
124+
"tooltip": {
125+
"headerFormat": "<b>{point.key}</b><br/>",
126+
"pointFormat": (
127+
"Max: {point.high}<br/>"
128+
"Q3: {point.q3}<br/>"
129+
"Median: {point.median}<br/>"
130+
"Q1: {point.q1}<br/>"
131+
"Min: {point.low}<br/>"
132+
),
133+
},
105134
}
106135
}
107136

108-
# Box plot series with individual colors per box
109-
box_series = BoxPlotSeries()
110-
box_series.name = "Distribution"
111-
box_series.data = box_data
112-
box_series.colors = colors
113-
114-
chart.add_series(box_series)
115-
116-
# Outliers as scatter series
137+
# One series per department for distinct colors
138+
for i, dept in enumerate(departments):
139+
series = BoxPlotSeries()
140+
series.name = dept
141+
series.data = [
142+
{
143+
"x": i,
144+
"low": box_stats[i]["low"],
145+
"q1": box_stats[i]["q1"],
146+
"median": box_stats[i]["median"],
147+
"q3": box_stats[i]["q3"],
148+
"high": box_stats[i]["high"],
149+
}
150+
]
151+
series.color = colors[i]
152+
chart.add_series(series)
153+
154+
# Outlier series
117155
if outlier_data:
118156
outlier_series = ScatterSeries()
119157
outlier_series.name = "Outliers"
120158
outlier_series.data = outlier_data
121159
outlier_series.marker = {
122-
"fillColor": "#E74C3C",
160+
"fillColor": "rgba(231, 76, 60, 0.7)",
123161
"lineWidth": 2,
124-
"lineColor": "#C0392B",
125-
"radius": 12,
162+
"lineColor": "#c0392b",
163+
"radius": 10,
126164
"symbol": "circle",
127165
}
166+
outlier_series.tooltip = {"pointFormat": "Score: {point.y}"}
128167
chart.add_series(outlier_series)
129168

130169
# Download Highcharts JS files (required for headless Chrome)
131170
highcharts_url = "https://code.highcharts.com/highcharts.js"
132171
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
133172
highcharts_js = response.read().decode("utf-8")
134173

135-
# BoxPlot requires highcharts-more.js
136174
highcharts_more_url = "https://code.highcharts.com/highcharts-more.js"
137175
with urllib.request.urlopen(highcharts_more_url, timeout=30) as response:
138176
highcharts_more_js = response.read().decode("utf-8")
139177

140-
# Generate HTML with inline scripts
178+
# Generate JS and inject properties not supported by highcharts-core API
141179
html_str = chart.to_js_literal()
180+
181+
# Inject stemColor into plotOptions (stripped by Python API)
182+
html_str = html_str.replace("stemDashStyle: 'Solid'", "stemColor: '#555555',\n stemDashStyle: 'Solid'")
183+
184+
# Inject fillColor per series
185+
for i in range(len(departments)):
186+
html_str = html_str.replace(
187+
f"color: '{colors[i]}',\n type: 'boxplot'",
188+
f"color: '{colors[i]}',\n fillColor: '{fill_colors[i]}',\n type: 'boxplot'",
189+
)
190+
142191
html_content = f"""<!DOCTYPE html>
143192
<html>
144193
<head>
@@ -157,10 +206,6 @@
157206
f.write(html_content)
158207
temp_path = f.name
159208

160-
# Save HTML file for interactive viewing
161-
with open("plot.html", "w", encoding="utf-8") as f:
162-
f.write(html_content)
163-
164209
# Take screenshot with Selenium
165210
chrome_options = Options()
166211
chrome_options.add_argument("--headless")
@@ -171,9 +216,9 @@
171216

172217
driver = webdriver.Chrome(options=chrome_options)
173218
driver.get(f"file://{temp_path}")
174-
time.sleep(5) # Wait for chart to render
219+
time.sleep(5)
175220
driver.save_screenshot("plot.png")
176221
driver.quit()
177222

178-
# Clean up temp file
223+
# Clean up
179224
Path(temp_path).unlink()

plots/box-basic/metadata/highcharts.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
library: highcharts
22
specification_id: box-basic
33
created: '2025-12-23T00:38:00Z'
4-
updated: '2025-12-23T00:42:02Z'
5-
generated_by: claude-opus-4-5-20251101
4+
updated: 2026-02-14T22:09:08+00:00
5+
generated_by: claude-opus-4-6
66
workflow_run: 20447784243
77
issue: 0
8-
python_version: 3.13.11
9-
library_version: unknown
8+
python_version: "3.14"
9+
library_version: "1.10.3"
1010
preview_url: https://storage.googleapis.com/pyplots-images/plots/box-basic/highcharts/plot.png
1111
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/box-basic/highcharts/plot_thumb.png
1212
preview_html: https://storage.googleapis.com/pyplots-images/plots/box-basic/highcharts/plot.html
13-
quality_score: 91
13+
quality_score: null
1414
impl_tags:
1515
dependencies:
1616
- selenium

0 commit comments

Comments
 (0)