Skip to content

Commit 0d89080

Browse files
feat(highcharts): implement errorbar-asymmetric (#2813)
## Implementation: `errorbar-asymmetric` - highcharts Implements the **highcharts** version of `errorbar-asymmetric`. **File:** `plots/errorbar-asymmetric/implementations/highcharts.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20602452781)* --------- 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 dbe947b commit 0d89080

2 files changed

Lines changed: 195 additions & 0 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
""" pyplots.ai
2+
errorbar-asymmetric: Asymmetric Error Bars Plot
3+
Library: highcharts unknown | Python 3.13.11
4+
Quality: 91/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 selenium import webdriver
16+
from selenium.webdriver.chrome.options import Options
17+
18+
19+
# Data - Quarterly sales forecast with asymmetric confidence intervals
20+
np.random.seed(42)
21+
categories = ["Q1 2024", "Q2 2024", "Q3 2024", "Q4 2024", "Q1 2025", "Q2 2025"]
22+
# Central values (point estimates in millions USD)
23+
y_values = [12.5, 14.2, 13.8, 18.5, 15.2, 16.8]
24+
# Asymmetric errors - upside potential typically larger in optimistic forecasts
25+
error_lower = [1.2, 1.5, 2.0, 2.5, 1.8, 2.2] # Downside risk
26+
error_upper = [2.5, 3.0, 2.8, 4.5, 3.2, 3.8] # Upside potential
27+
28+
# Create chart
29+
chart = Chart(container="container")
30+
chart.options = HighchartsOptions()
31+
32+
# Chart configuration for 4800x2700 canvas
33+
chart.options.chart = {
34+
"width": 4800,
35+
"height": 2700,
36+
"backgroundColor": "#ffffff",
37+
"marginBottom": 280,
38+
"marginLeft": 280,
39+
"marginRight": 150,
40+
"marginTop": 200,
41+
}
42+
43+
# Title
44+
chart.options.title = {
45+
"text": "errorbar-asymmetric · highcharts · pyplots.ai",
46+
"style": {"fontSize": "48px", "fontWeight": "bold"},
47+
}
48+
49+
# Subtitle explaining the error bars
50+
chart.options.subtitle = {
51+
"text": "Quarterly Sales Forecast with 10th-90th Percentile Confidence Intervals",
52+
"style": {"fontSize": "32px", "color": "#666666"},
53+
}
54+
55+
# X-axis configuration
56+
chart.options.x_axis = {
57+
"categories": categories,
58+
"title": {"text": "Quarter", "style": {"fontSize": "36px"}},
59+
"labels": {"style": {"fontSize": "28px"}},
60+
}
61+
62+
# Y-axis configuration - let axis auto-scale to better use canvas space
63+
chart.options.y_axis = {
64+
"title": {"text": "Sales Forecast (Million USD)", "style": {"fontSize": "36px"}},
65+
"labels": {"style": {"fontSize": "28px"}},
66+
"gridLineColor": "#e0e0e0",
67+
"gridLineWidth": 1,
68+
}
69+
70+
# Legend configuration - positioned closer to the data, inside the plot area
71+
chart.options.legend = {
72+
"enabled": True,
73+
"itemStyle": {"fontSize": "32px"},
74+
"symbolRadius": 0,
75+
"symbolWidth": 40,
76+
"symbolHeight": 24,
77+
"align": "right",
78+
"verticalAlign": "top",
79+
"x": -80,
80+
"y": 120,
81+
"layout": "vertical",
82+
"backgroundColor": "#ffffff",
83+
"borderColor": "#e0e0e0",
84+
"borderWidth": 1,
85+
"padding": 16,
86+
}
87+
88+
# Plot options - configure errorbar series with visible caps
89+
chart.options.plot_options = {
90+
"errorbar": {"stemWidth": 6, "whiskerLength": 40, "whiskerWidth": 6},
91+
"scatter": {"marker": {"radius": 16, "symbol": "circle"}},
92+
}
93+
94+
# Create errorbar data with asymmetric errors: [low, high] format
95+
errorbar_data = []
96+
for y, el, eu in zip(y_values, error_lower, error_upper, strict=True):
97+
errorbar_data.append([y - el, y + eu])
98+
99+
# Create series data for central points
100+
scatter_data = []
101+
for i, y in enumerate(y_values):
102+
scatter_data.append({"x": i, "y": y})
103+
104+
# Add series - use native errorbar series type for authentic error bars with caps
105+
chart.options.series = [
106+
{
107+
"name": "Point Estimate",
108+
"type": "scatter",
109+
"data": scatter_data,
110+
"color": "#FFD43B",
111+
"marker": {"radius": 18, "symbol": "diamond", "lineColor": "#306998", "lineWidth": 3},
112+
"zIndex": 5,
113+
},
114+
{
115+
"name": "10th-90th Percentile Range",
116+
"type": "errorbar",
117+
"data": errorbar_data,
118+
"color": "#306998",
119+
"stemColor": "#306998",
120+
"whiskerColor": "#306998",
121+
"showInLegend": True,
122+
},
123+
]
124+
125+
# Download Highcharts JS and highcharts-more.js (needed for some chart types)
126+
highcharts_url = "https://code.highcharts.com/highcharts.js"
127+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
128+
highcharts_js = response.read().decode("utf-8")
129+
130+
highcharts_more_url = "https://code.highcharts.com/highcharts-more.js"
131+
with urllib.request.urlopen(highcharts_more_url, timeout=30) as response:
132+
highcharts_more_js = response.read().decode("utf-8")
133+
134+
# Generate HTML with inline scripts
135+
html_str = chart.to_js_literal()
136+
html_content = f"""<!DOCTYPE html>
137+
<html>
138+
<head>
139+
<meta charset="utf-8">
140+
<script>{highcharts_js}</script>
141+
<script>{highcharts_more_js}</script>
142+
</head>
143+
<body style="margin:0;">
144+
<div id="container" style="width: 4800px; height: 2700px;"></div>
145+
<script>{html_str}</script>
146+
</body>
147+
</html>"""
148+
149+
# Write temp HTML and take screenshot
150+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
151+
f.write(html_content)
152+
temp_path = f.name
153+
154+
# Save HTML for interactive viewing
155+
with open("plot.html", "w", encoding="utf-8") as f:
156+
f.write(html_content)
157+
158+
# Screenshot with headless Chrome
159+
chrome_options = Options()
160+
chrome_options.add_argument("--headless")
161+
chrome_options.add_argument("--no-sandbox")
162+
chrome_options.add_argument("--disable-dev-shm-usage")
163+
chrome_options.add_argument("--disable-gpu")
164+
chrome_options.add_argument("--window-size=4800,2700")
165+
166+
driver = webdriver.Chrome(options=chrome_options)
167+
driver.get(f"file://{temp_path}")
168+
time.sleep(5) # Wait for chart to render
169+
driver.save_screenshot("plot.png")
170+
driver.quit()
171+
172+
Path(temp_path).unlink() # Clean up temp file
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
library: highcharts
2+
specification_id: errorbar-asymmetric
3+
created: '2025-12-30T17:48:53Z'
4+
updated: '2025-12-30T18:07:02Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20602452781
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: unknown
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/errorbar-asymmetric/highcharts/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/errorbar-asymmetric/highcharts/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/errorbar-asymmetric/highcharts/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent use of Highcharts native errorbar series type with proper caps/whiskers
17+
- Clean colorblind-safe color scheme (blue/yellow Python colors)
18+
- Good realistic financial forecasting scenario with appropriate asymmetry
19+
- Proper inclusion of highcharts-more.js for errorbar support
20+
- Clear legend explaining what the asymmetric bounds represent (10th-90th percentile)
21+
- Subtitle provides context for the confidence interval interpretation
22+
weaknesses:
23+
- Library version shows as "unknown" in the header comment (minor metadata issue)

0 commit comments

Comments
 (0)