Skip to content

Commit 226ecd2

Browse files
update(density-basic): highcharts — comprehensive quality review (#4387)
## Summary Updated **highcharts** implementation for **density-basic**. **Changes:** Comprehensive quality review ### Changes - Bimodal distribution with realistic height data - Y-axis-only grid at 10% opacity - Enlarged rug plot markers for visibility at scale - Disabled tooltips/animation for clean static export - Quality: 88/100 (local self-evaluation, after 2 iterations) ## 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 09faa12 commit 226ecd2

2 files changed

Lines changed: 251 additions & 174 deletions

File tree

Lines changed: 103 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" pyplots.ai
22
density-basic: Basic Density 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.3
4+
Quality: 92/100 | Updated: 2026-02-23
55
"""
66

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

2020

21-
# Data - simulating heights (cm) with realistic distribution
21+
# Data - simulating heights (cm) with bimodal distribution
2222
np.random.seed(42)
23-
# Mix of two normal distributions to show bimodal feature (male/female heights)
2423
n_samples = 500
25-
values_a = np.random.normal(165, 7, n_samples // 2) # Female heights
26-
values_b = np.random.normal(178, 8, n_samples // 2) # Male heights
24+
values_a = np.random.normal(162, 6, n_samples // 2) # Female heights
25+
values_b = np.random.normal(178, 6, n_samples // 2) # Male heights
2726
values = np.concatenate([values_a, values_b])
2827

29-
# Kernel Density Estimation (Gaussian kernel) - inline calculation
30-
x_min, x_max = values.min() - 10, values.max() + 10
31-
x_grid = np.linspace(x_min, x_max, 200)
28+
# Kernel Density Estimation (Gaussian kernel)
29+
x_min, x_max = values.min() - 12, values.max() + 12
30+
x_grid = np.linspace(x_min, x_max, 300)
3231

3332
# Silverman's rule of thumb for bandwidth
3433
n = len(values)
35-
bandwidth = 1.06 * np.std(values) * n ** (-1 / 5)
34+
bandwidth = 0.9 * min(np.std(values), np.subtract(*np.percentile(values, [75, 25])) / 1.34) * n ** (-1 / 5)
3635

3736
# Compute Gaussian KDE
3837
density = np.zeros_like(x_grid)
3938
for xi in values:
4039
density += np.exp(-0.5 * ((x_grid - xi) / bandwidth) ** 2)
4140
density /= n * bandwidth * np.sqrt(2 * np.pi)
4241

42+
# Find the two peak positions for annotation
43+
midpoint = len(x_grid) // 2
44+
peak1_idx = np.argmax(density[:midpoint])
45+
peak2_idx = midpoint + np.argmax(density[midpoint:])
46+
peak1_x = float(x_grid[peak1_idx])
47+
peak2_x = float(x_grid[peak2_idx])
48+
4349
# Create chart
4450
chart = Chart(container="container")
4551
chart.options = HighchartsOptions()
@@ -50,79 +56,131 @@
5056
"width": 4800,
5157
"height": 2700,
5258
"backgroundColor": "#ffffff",
53-
"marginBottom": 200,
54-
"marginLeft": 200,
59+
"marginBottom": 180,
60+
"marginLeft": 180,
61+
"marginRight": 60,
62+
"marginTop": 140,
63+
"style": {"fontFamily": "Arial, Helvetica, sans-serif"},
5564
}
5665

5766
# Title
5867
chart.options.title = {
59-
"text": "density-basic · highcharts · pyplots.ai",
60-
"style": {"fontSize": "72px", "fontWeight": "bold"},
68+
"text": "density-basic \u00b7 highcharts \u00b7 pyplots.ai",
69+
"style": {"fontSize": "64px", "fontWeight": "600", "color": "#2c3e50"},
70+
"margin": 40,
6171
}
6272

63-
# X-axis
73+
# Disable credits
74+
chart.options.credits = {"enabled": False}
75+
76+
# X-axis - clean L-shaped frame with plotBands/plotLines for peak emphasis
6477
chart.options.x_axis = {
65-
"title": {"text": "Height (cm)", "style": {"fontSize": "48px"}},
66-
"labels": {"style": {"fontSize": "36px"}},
67-
"gridLineWidth": 1,
68-
"gridLineColor": "rgba(0, 0, 0, 0.25)",
78+
"title": {"text": "Height (cm)", "style": {"fontSize": "48px", "color": "#444444"}, "margin": 24},
79+
"labels": {"style": {"fontSize": "36px", "color": "#666666"}},
80+
"lineColor": "#cccccc",
81+
"lineWidth": 2,
82+
"tickWidth": 0,
83+
"tickInterval": 5,
84+
"gridLineWidth": 0,
85+
"plotBands": [
86+
{"from": peak1_x - 8, "to": peak1_x + 8, "color": "rgba(48, 105, 152, 0.06)", "zIndex": 0},
87+
{"from": peak2_x - 8, "to": peak2_x + 8, "color": "rgba(48, 105, 152, 0.06)", "zIndex": 0},
88+
],
89+
"plotLines": [
90+
{
91+
"value": peak1_x,
92+
"color": "rgba(48, 105, 152, 0.45)",
93+
"width": 3,
94+
"dashStyle": "Dash",
95+
"zIndex": 4,
96+
"label": {
97+
"text": f"Peak: {peak1_x:.0f} cm",
98+
"style": {"fontSize": "32px", "color": "#306998", "fontWeight": "600"},
99+
"rotation": 0,
100+
"y": 16,
101+
},
102+
},
103+
{
104+
"value": peak2_x,
105+
"color": "rgba(48, 105, 152, 0.45)",
106+
"width": 3,
107+
"dashStyle": "Dash",
108+
"zIndex": 4,
109+
"label": {
110+
"text": f"Peak: {peak2_x:.0f} cm",
111+
"style": {"fontSize": "32px", "color": "#306998", "fontWeight": "600"},
112+
"rotation": 0,
113+
"y": 16,
114+
},
115+
},
116+
],
69117
}
70118

71-
# Y-axis - start at 0 for proper density representation
119+
# Y-axis - subtle horizontal grid only
72120
chart.options.y_axis = {
73-
"title": {"text": "Density", "style": {"fontSize": "48px"}},
74-
"labels": {"style": {"fontSize": "36px"}},
121+
"title": {"text": "Density", "style": {"fontSize": "48px", "color": "#444444"}, "margin": 24},
122+
"labels": {"style": {"fontSize": "36px", "color": "#666666"}},
75123
"gridLineWidth": 1,
76-
"gridLineColor": "rgba(0, 0, 0, 0.25)",
124+
"gridLineColor": "rgba(0, 0, 0, 0.10)",
125+
"lineColor": "#cccccc",
126+
"lineWidth": 2,
127+
"tickAmount": 7,
77128
"min": 0,
78129
}
79130

80-
# Plot options with semi-transparent fill
131+
# Plot options
81132
chart.options.plot_options = {
82133
"area": {
83134
"fillColor": {
84135
"linearGradient": {"x1": 0, "y1": 0, "x2": 0, "y2": 1},
85-
"stops": [[0, "rgba(48, 105, 152, 0.6)"], [1, "rgba(48, 105, 152, 0.1)"]],
136+
"stops": [[0, "rgba(48, 105, 152, 0.45)"], [1, "rgba(48, 105, 152, 0.03)"]],
86137
},
87138
"lineWidth": 5,
88139
"marker": {"enabled": False},
89140
"color": "#306998",
141+
"states": {"hover": {"lineWidth": 5}},
90142
},
91143
"scatter": {
92-
"marker": {"radius": 8, "fillColor": "#FFD43B", "symbol": "diamond", "lineWidth": 2, "lineColor": "#D4AA00"}
144+
"marker": {"radius": 15, "fillColor": "rgba(48, 105, 152, 0.55)", "symbol": "diamond", "lineWidth": 0},
145+
"states": {"hover": {"enabled": False}},
93146
},
147+
"series": {"animation": False},
94148
}
95149

96-
# Legend - enabled with clear styling
150+
# Disable tooltip for static export
151+
chart.options.tooltip = {"enabled": False}
152+
153+
# Legend
97154
chart.options.legend = {
98155
"enabled": True,
99156
"layout": "horizontal",
100157
"align": "right",
101158
"verticalAlign": "top",
102159
"floating": True,
103-
"x": -50,
104-
"y": 80,
105-
"itemStyle": {"fontSize": "36px"},
106-
"symbolHeight": 24,
107-
"symbolWidth": 40,
160+
"x": -40,
161+
"y": 60,
162+
"itemStyle": {"fontSize": "34px", "fontWeight": "normal", "color": "#555555"},
163+
"symbolHeight": 20,
164+
"symbolWidth": 32,
165+
"itemDistance": 40,
166+
"borderWidth": 0,
108167
}
109168

110169
# Add density curve as area series
111170
area_series = AreaSeries()
112-
area_series.data = [[float(x), float(y)] for x, y in zip(x_grid, density, strict=True)]
113-
area_series.name = "Density Curve"
171+
area_series.data = [[round(float(x), 2), round(float(y), 6)] for x, y in zip(x_grid, density, strict=True)]
172+
area_series.name = "Density"
114173
chart.add_series(area_series)
115174

116-
# Add rug plot as small vertical tick marks at y=0
117-
# Sample every 5th point to show distribution without overcrowding
118-
rug_sample = values[::5]
119-
rug_y = 0.0005 # Small positive value just above x-axis
120-
rug_data = [[float(v), rug_y] for v in sorted(rug_sample)]
175+
# Add rug plot - vertical tick marks along x-axis
176+
rug_sample = values[::3] # Show every 3rd observation for good coverage
177+
rug_y = max(density) * 0.008 # Small positive value near axis
178+
rug_data = [[round(float(v), 2), round(float(rug_y), 6)] for v in sorted(rug_sample)]
121179

122180
rug_series = ScatterSeries()
123181
rug_series.data = rug_data
124-
rug_series.name = "Observations (Rug)"
125-
rug_series.marker = {"symbol": "diamond", "fillColor": "#FFD43B", "lineColor": "#D4AA00", "lineWidth": 2, "radius": 10}
182+
rug_series.name = "Observations"
183+
rug_series.marker = {"symbol": "diamond", "fillColor": "rgba(48, 105, 152, 0.55)", "lineWidth": 0, "radius": 15}
126184
chart.add_series(rug_series)
127185

128186
# Download Highcharts JS for inline embedding
@@ -149,9 +207,8 @@
149207
f.write(html_content)
150208
temp_path = f.name
151209

152-
# Also save HTML for interactive version
210+
# Save standalone HTML for interactive version
153211
with open("plot.html", "w", encoding="utf-8") as f:
154-
# For standalone HTML, use CDN link
155212
standalone_html = f"""<!DOCTYPE html>
156213
<html>
157214
<head>
@@ -170,15 +227,14 @@
170227
chrome_options.add_argument("--no-sandbox")
171228
chrome_options.add_argument("--disable-dev-shm-usage")
172229
chrome_options.add_argument("--disable-gpu")
173-
chrome_options.add_argument("--window-size=5000,3000")
230+
chrome_options.add_argument("--window-size=4900,2800")
174231

175232
driver = webdriver.Chrome(options=chrome_options)
176233
driver.get(f"file://{temp_path}")
177-
time.sleep(5) # Wait for chart to render
234+
time.sleep(5)
178235

179-
# Screenshot the chart element specifically for exact 4800x2700
180236
container = driver.find_element("id", "container")
181237
container.screenshot("plot.png")
182238
driver.quit()
183239

184-
Path(temp_path).unlink() # Clean up temp file
240+
Path(temp_path).unlink()

0 commit comments

Comments
 (0)