Skip to content

Commit 9413de8

Browse files
feat(highcharts): implement histogram-stacked (#2639)
## Implementation: `histogram-stacked` - highcharts Implements the **highcharts** version of `histogram-stacked`. **File:** `plots/histogram-stacked/implementations/highcharts.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20594562099)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 04d6a26 commit 9413de8

2 files changed

Lines changed: 183 additions & 0 deletions

File tree

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
""" pyplots.ai
2+
histogram-stacked: Stacked Histogram
3+
Library: highcharts unknown | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-30
5+
"""
6+
7+
import tempfile
8+
import time
9+
import urllib.request
10+
from pathlib import Path
11+
12+
import numpy as np
13+
from highcharts_core.chart import Chart
14+
from highcharts_core.options import HighchartsOptions
15+
from highcharts_core.options.series.bar import ColumnSeries
16+
from selenium import webdriver
17+
from selenium.webdriver.chrome.options import Options
18+
19+
20+
# Data: Generate three groups with different distributions
21+
np.random.seed(42)
22+
group_a = np.random.normal(loc=45, scale=12, size=150) # Centered around 45
23+
group_b = np.random.normal(loc=55, scale=10, size=120) # Centered around 55
24+
group_c = np.random.normal(loc=65, scale=15, size=100) # Centered around 65
25+
26+
# Combine all data to determine common bin edges
27+
all_data = np.concatenate([group_a, group_b, group_c])
28+
bin_edges = np.histogram_bin_edges(all_data, bins=15)
29+
bin_centers = [(bin_edges[i] + bin_edges[i + 1]) / 2 for i in range(len(bin_edges) - 1)]
30+
bin_labels = [f"{bin_edges[i]:.0f}-{bin_edges[i + 1]:.0f}" for i in range(len(bin_edges) - 1)]
31+
32+
# Calculate histogram counts for each group
33+
counts_a, _ = np.histogram(group_a, bins=bin_edges)
34+
counts_b, _ = np.histogram(group_b, bins=bin_edges)
35+
counts_c, _ = np.histogram(group_c, bins=bin_edges)
36+
37+
# Create chart
38+
chart = Chart(container="container")
39+
chart.options = HighchartsOptions()
40+
41+
# Chart options
42+
chart.options.chart = {
43+
"type": "column",
44+
"width": 4800,
45+
"height": 2700,
46+
"backgroundColor": "#ffffff",
47+
"marginBottom": 300,
48+
}
49+
50+
# Title
51+
chart.options.title = {
52+
"text": "histogram-stacked · highcharts · pyplots.ai",
53+
"style": {"fontSize": "72px", "fontWeight": "bold"},
54+
}
55+
56+
# Subtitle
57+
chart.options.subtitle = {"text": "Measurement Distribution by Sensor Type", "style": {"fontSize": "42px"}}
58+
59+
# X-axis (categories for bins)
60+
chart.options.x_axis = {
61+
"categories": bin_labels,
62+
"title": {"text": "Measurement Range", "style": {"fontSize": "48px"}, "margin": 40},
63+
"labels": {"style": {"fontSize": "28px"}},
64+
}
65+
66+
# Y-axis
67+
chart.options.y_axis = {
68+
"title": {"text": "Frequency", "style": {"fontSize": "48px"}},
69+
"labels": {"style": {"fontSize": "36px"}},
70+
"gridLineWidth": 1,
71+
"gridLineColor": "rgba(0,0,0,0.1)",
72+
"stackLabels": {"enabled": False},
73+
}
74+
75+
# Plot options for stacking
76+
chart.options.plot_options = {
77+
"column": {"stacking": "normal", "borderWidth": 1, "borderColor": "#ffffff", "dataLabels": {"enabled": False}}
78+
}
79+
80+
# Legend
81+
chart.options.legend = {
82+
"enabled": True,
83+
"itemStyle": {"fontSize": "36px"},
84+
"align": "right",
85+
"verticalAlign": "top",
86+
"layout": "vertical",
87+
"x": -50,
88+
"y": 100,
89+
}
90+
91+
# Colors - colorblind-safe palette
92+
colors = ["#306998", "#FFD43B", "#9467BD"]
93+
94+
# Add series for each group
95+
series_a = ColumnSeries()
96+
series_a.data = [int(c) for c in counts_a]
97+
series_a.name = "Sensor A"
98+
series_a.color = colors[0]
99+
100+
series_b = ColumnSeries()
101+
series_b.data = [int(c) for c in counts_b]
102+
series_b.name = "Sensor B"
103+
series_b.color = colors[1]
104+
105+
series_c = ColumnSeries()
106+
series_c.data = [int(c) for c in counts_c]
107+
series_c.name = "Sensor C"
108+
series_c.color = colors[2]
109+
110+
chart.add_series(series_a)
111+
chart.add_series(series_b)
112+
chart.add_series(series_c)
113+
114+
# Export to PNG via Selenium
115+
highcharts_url = "https://code.highcharts.com/highcharts.js"
116+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
117+
highcharts_js = response.read().decode("utf-8")
118+
119+
# Generate HTML with inline scripts
120+
html_str = chart.to_js_literal()
121+
html_content = f"""<!DOCTYPE html>
122+
<html>
123+
<head>
124+
<meta charset="utf-8">
125+
<script>{highcharts_js}</script>
126+
</head>
127+
<body style="margin:0;">
128+
<div id="container" style="width: 4800px; height: 2700px;"></div>
129+
<script>{html_str}</script>
130+
</body>
131+
</html>"""
132+
133+
# Write temp HTML and take screenshot
134+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
135+
f.write(html_content)
136+
temp_path = f.name
137+
138+
chrome_options = Options()
139+
chrome_options.add_argument("--headless")
140+
chrome_options.add_argument("--no-sandbox")
141+
chrome_options.add_argument("--disable-dev-shm-usage")
142+
chrome_options.add_argument("--disable-gpu")
143+
chrome_options.add_argument("--window-size=4800,2700")
144+
145+
driver = webdriver.Chrome(options=chrome_options)
146+
driver.get(f"file://{temp_path}")
147+
time.sleep(5)
148+
driver.save_screenshot("plot.png")
149+
driver.quit()
150+
151+
Path(temp_path).unlink()
152+
153+
# Also save HTML for interactive version
154+
with open("plot.html", "w", encoding="utf-8") as f:
155+
f.write(html_content)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: highcharts
2+
specification_id: histogram-stacked
3+
created: '2025-12-30T10:42:46Z'
4+
updated: '2025-12-30T10:50:48Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20594562099
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: unknown
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/histogram-stacked/highcharts/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/histogram-stacked/highcharts/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/histogram-stacked/highcharts/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent colorblind-safe palette with clear visual distinction between all three
17+
groups
18+
- Proper implementation of stacked histogram using common bin edges calculated from
19+
combined data
20+
- Clean, professional appearance with well-sized text elements for high-resolution
21+
output
22+
- Correct title format and meaningful subtitle adding context
23+
- Good use of Highcharts-specific features like plot_options stacking configuration
24+
weaknesses:
25+
- Output image height is 2561px instead of the expected 2700px (minor dimension
26+
mismatch)
27+
- Axis labels lack units (e.g., could be "Measurement Range (units)" and "Frequency
28+
(count)")

0 commit comments

Comments
 (0)