Skip to content

Commit 137f23c

Browse files
feat(highcharts): implement facet-grid (#2777)
## Implementation: `facet-grid` - highcharts Implements the **highcharts** version of `facet-grid`. **File:** `plots/facet-grid/implementations/highcharts.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20601061463)* --------- 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 8b38540 commit 137f23c

2 files changed

Lines changed: 319 additions & 0 deletions

File tree

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
""" pyplots.ai
2+
facet-grid: Faceted Grid Plot
3+
Library: highcharts unknown | Python 3.13.11
4+
Quality: 90/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
20+
np.random.seed(42)
21+
22+
# Create faceted data: Plant growth by soil type (rows) and light condition (columns)
23+
soil_types = ["Sandy", "Loamy", "Clay"]
24+
light_conditions = ["Low", "Medium", "High"]
25+
n_per_group = 25
26+
27+
data = []
28+
for soil in soil_types:
29+
for light in light_conditions:
30+
# Base growth varies by light condition
31+
base_growth = {"Low": 8, "Medium": 15, "High": 22}[light]
32+
# Soil type affects growth rate
33+
soil_factor = {"Sandy": 0.9, "Loamy": 1.2, "Clay": 1.0}[soil]
34+
35+
for _ in range(n_per_group):
36+
water = np.random.uniform(20, 100)
37+
# Growth depends on water, with more noise for wider spread
38+
growth = base_growth * soil_factor + water * 0.18 * soil_factor + np.random.normal(0, 3.5)
39+
data.append({"soil": soil, "light": light, "water": water, "growth": max(0, growth)})
40+
41+
# Build subplot configurations
42+
n_rows = len(soil_types)
43+
n_cols = len(light_conditions)
44+
45+
# Calculate subplot dimensions with margins
46+
chart_width = 4800
47+
chart_height = 2700
48+
margin_top = 200
49+
margin_bottom = 140
50+
margin_left = 200
51+
margin_right = 120
52+
spacing = 80
53+
54+
plot_area_width = chart_width - margin_left - margin_right
55+
plot_area_height = chart_height - margin_top - margin_bottom
56+
subplot_width = (plot_area_width - spacing * (n_cols - 1)) / n_cols
57+
subplot_height = (plot_area_height - spacing * (n_rows - 1)) / n_rows
58+
59+
# Colors for facets - colorblind-safe palette
60+
colors = ["#306998", "#FFD43B", "#9467BD"]
61+
62+
# Build series data for each facet
63+
series_list = []
64+
x_axes = []
65+
y_axes = []
66+
67+
for row_idx, soil in enumerate(soil_types):
68+
for col_idx, light in enumerate(light_conditions):
69+
# Filter data for this facet
70+
facet_data = [d for d in data if d["soil"] == soil and d["light"] == light]
71+
points = [[d["water"], d["growth"]] for d in facet_data]
72+
73+
# Calculate position
74+
left = margin_left + col_idx * (subplot_width + spacing)
75+
top = margin_top + row_idx * (subplot_height + spacing)
76+
77+
# Create x-axis for this subplot
78+
x_axis_id = f"x{row_idx * n_cols + col_idx}"
79+
x_axes.append(
80+
{
81+
"id": x_axis_id,
82+
"left": left,
83+
"top": top + subplot_height,
84+
"width": subplot_width,
85+
"height": 0,
86+
"min": 15,
87+
"max": 105,
88+
"lineWidth": 2,
89+
"lineColor": "#333333",
90+
"tickWidth": 2,
91+
"tickLength": 10,
92+
"labels": {"style": {"fontSize": "20px", "color": "#333333"}, "y": 30},
93+
"title": {
94+
"text": "Water (mm)" if row_idx == n_rows - 1 else None,
95+
"style": {"fontSize": "24px", "color": "#333333"},
96+
"y": 55,
97+
},
98+
"gridLineWidth": 1,
99+
"gridLineColor": "rgba(0,0,0,0.15)",
100+
"gridLineDashStyle": "Dash",
101+
"offset": 0,
102+
}
103+
)
104+
105+
# Create y-axis for this subplot
106+
y_axis_id = f"y{row_idx * n_cols + col_idx}"
107+
y_axes.append(
108+
{
109+
"id": y_axis_id,
110+
"left": left,
111+
"top": top,
112+
"width": 0,
113+
"height": subplot_height,
114+
"min": 0,
115+
"max": 50,
116+
"lineWidth": 2,
117+
"lineColor": "#333333",
118+
"tickWidth": 2,
119+
"tickLength": 10,
120+
"labels": {"style": {"fontSize": "20px", "color": "#333333"}, "x": -15},
121+
"title": {
122+
"text": "Growth (cm)" if col_idx == 0 else None,
123+
"style": {"fontSize": "24px", "color": "#333333"},
124+
"rotation": 270,
125+
"x": -50,
126+
},
127+
"gridLineWidth": 1,
128+
"gridLineColor": "rgba(0,0,0,0.15)",
129+
"gridLineDashStyle": "Dash",
130+
"offset": 0,
131+
}
132+
)
133+
134+
# Create series for this facet
135+
series_list.append(
136+
{
137+
"type": "scatter",
138+
"name": f"{soil} / {light}",
139+
"data": points,
140+
"xAxis": row_idx * n_cols + col_idx,
141+
"yAxis": row_idx * n_cols + col_idx,
142+
"marker": {
143+
"radius": 10,
144+
"symbol": "circle",
145+
"fillColor": colors[row_idx % len(colors)],
146+
"lineWidth": 2,
147+
"lineColor": "#333333",
148+
},
149+
"showInLegend": False,
150+
}
151+
)
152+
153+
# Build annotations for facet labels
154+
annotations = []
155+
156+
# Column headers (light conditions)
157+
for col_idx, light in enumerate(light_conditions):
158+
left = margin_left + col_idx * (subplot_width + spacing) + subplot_width / 2
159+
annotations.append(
160+
{
161+
"labels": [
162+
{
163+
"point": {"x": left, "y": margin_top - 60, "xAxis": None, "yAxis": None},
164+
"text": f"Light: {light}",
165+
"backgroundColor": "transparent",
166+
"borderWidth": 0,
167+
"style": {"fontSize": "28px", "fontWeight": "bold", "color": "#333333"},
168+
}
169+
],
170+
"labelOptions": {"useHTML": True},
171+
}
172+
)
173+
174+
# Row labels (soil types)
175+
for row_idx, soil in enumerate(soil_types):
176+
top = margin_top + row_idx * (subplot_height + spacing) + subplot_height / 2
177+
annotations.append(
178+
{
179+
"labels": [
180+
{
181+
"point": {"x": chart_width - margin_right + 50, "y": top, "xAxis": None, "yAxis": None},
182+
"text": f"Soil: {soil}",
183+
"backgroundColor": "transparent",
184+
"borderWidth": 0,
185+
"style": {"fontSize": "26px", "fontWeight": "bold", "color": "#333333"},
186+
"rotation": 90,
187+
}
188+
],
189+
"labelOptions": {"useHTML": True},
190+
}
191+
)
192+
193+
# Create chart using highcharts-core
194+
chart = Chart(container="container")
195+
chart.options = HighchartsOptions()
196+
197+
# Set chart options
198+
chart.options.chart = {
199+
"type": "scatter",
200+
"width": chart_width,
201+
"height": chart_height,
202+
"backgroundColor": "#ffffff",
203+
"style": {"fontFamily": "Arial, sans-serif"},
204+
"marginTop": margin_top,
205+
"marginBottom": margin_bottom,
206+
"marginLeft": margin_left,
207+
"marginRight": margin_right,
208+
}
209+
210+
chart.options.title = {
211+
"text": "Plant Growth Study · facet-grid · highcharts · pyplots.ai",
212+
"style": {"fontSize": "36px", "fontWeight": "bold", "color": "#333333"},
213+
"y": 55,
214+
}
215+
216+
chart.options.subtitle = {
217+
"text": "Growth by Soil Type (rows) and Light Condition (columns)",
218+
"style": {"fontSize": "26px", "color": "#666666"},
219+
"y": 110,
220+
}
221+
222+
chart.options.credits = {"enabled": False}
223+
chart.options.legend = {"enabled": False}
224+
chart.options.x_axis = x_axes
225+
chart.options.y_axis = y_axes
226+
chart.options.series = series_list
227+
chart.options.annotations = annotations
228+
229+
chart.options.plot_options = {
230+
"scatter": {
231+
"marker": {"radius": 10, "states": {"hover": {"enabled": True, "lineColor": "#333333"}}},
232+
"states": {"inactive": {"opacity": 1}},
233+
}
234+
}
235+
236+
chart.options.tooltip = {
237+
"headerFormat": "<b>{series.name}</b><br>",
238+
"pointFormat": "Water: {point.x:.1f} mm<br>Growth: {point.y:.1f} cm",
239+
"style": {"fontSize": "18px"},
240+
}
241+
242+
# Generate JavaScript
243+
html_str = chart.to_js_literal()
244+
245+
# Download Highcharts JS
246+
highcharts_url = "https://code.highcharts.com/highcharts.js"
247+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
248+
highcharts_js = response.read().decode("utf-8")
249+
250+
# Download annotations module
251+
annotations_url = "https://code.highcharts.com/modules/annotations.js"
252+
with urllib.request.urlopen(annotations_url, timeout=30) as response:
253+
annotations_js = response.read().decode("utf-8")
254+
255+
# Generate HTML with inline scripts
256+
html_content = f"""<!DOCTYPE html>
257+
<html>
258+
<head>
259+
<meta charset="utf-8">
260+
<script>{highcharts_js}</script>
261+
<script>{annotations_js}</script>
262+
</head>
263+
<body style="margin:0; padding:0;">
264+
<div id="container" style="width: {chart_width}px; height: {chart_height}px;"></div>
265+
<script>{html_str}</script>
266+
</body>
267+
</html>"""
268+
269+
# Save HTML
270+
with open("plot.html", "w", encoding="utf-8") as f:
271+
f.write(html_content)
272+
273+
# Create screenshot with Selenium
274+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
275+
f.write(html_content)
276+
temp_path = f.name
277+
278+
chrome_options = Options()
279+
chrome_options.add_argument("--headless")
280+
chrome_options.add_argument("--no-sandbox")
281+
chrome_options.add_argument("--disable-dev-shm-usage")
282+
chrome_options.add_argument("--disable-gpu")
283+
chrome_options.add_argument("--window-size=4900,2800")
284+
285+
driver = webdriver.Chrome(options=chrome_options)
286+
driver.set_window_size(4900, 2800)
287+
driver.get(f"file://{temp_path}")
288+
time.sleep(5)
289+
driver.save_screenshot("plot.png")
290+
driver.quit()
291+
292+
Path(temp_path).unlink()
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library: highcharts
2+
specification_id: facet-grid
3+
created: '2025-12-30T16:40:06Z'
4+
updated: '2025-12-30T16:54:18Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20601061463
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: unknown
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/facet-grid/highcharts/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/facet-grid/highcharts/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/facet-grid/highcharts/plot.html
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Excellent implementation of a complex 3×3 faceted grid using Highcharts multi-axis
17+
capability
18+
- Colorblind-safe palette with clear visual distinction between soil types (blue/yellow/purple)
19+
- Realistic plant growth study with meaningful data patterns showing light and soil
20+
effects
21+
- Proper use of annotations module for column and row facet labels
22+
- Correct title format and subtitle explaining the faceting structure
23+
- Shared axis scales across all facets enabling direct comparison
24+
weaknesses:
25+
- Y-axis label shows 'Values' instead of 'Growth (cm)' in middle and right columns
26+
(inconsistent labeling)
27+
- Text sizes could be larger for optimal readability at 4800×2700 resolution

0 commit comments

Comments
 (0)