Skip to content

Commit 73d5f4d

Browse files
feat(highcharts): implement pdp-basic (#2961)
## Implementation: `pdp-basic` - highcharts Implements the **highcharts** version of `pdp-basic`. **File:** `plots/pdp-basic/implementations/highcharts.py` **Parent Issue:** #2922 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20612801369)* --------- 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 7f08546 commit 73d5f4d

2 files changed

Lines changed: 204 additions & 0 deletions

File tree

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
""" pyplots.ai
2+
pdp-basic: Partial Dependence Plot
3+
Library: highcharts unknown | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
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.area import AreaRangeSeries, LineSeries
16+
from selenium import webdriver
17+
from selenium.webdriver.chrome.options import Options
18+
from sklearn.datasets import make_regression
19+
from sklearn.ensemble import GradientBoostingRegressor
20+
from sklearn.inspection import partial_dependence
21+
22+
23+
# Data - Train a GradientBoostingRegressor and compute partial dependence
24+
np.random.seed(42)
25+
X, y = make_regression(n_samples=500, n_features=5, n_informative=3, noise=20, random_state=42)
26+
27+
# Train model
28+
model = GradientBoostingRegressor(n_estimators=100, max_depth=4, random_state=42)
29+
model.fit(X, y)
30+
31+
# Compute partial dependence for feature 0 (most informative)
32+
feature_idx = 0
33+
pd_results = partial_dependence(model, X, features=[feature_idx], kind="average", grid_resolution=80)
34+
feature_values = pd_results["grid_values"][0]
35+
avg_predictions = pd_results["average"][0]
36+
37+
# Compute individual conditional expectations for confidence band
38+
individual_pd = partial_dependence(model, X, features=[feature_idx], kind="individual", grid_resolution=80)
39+
individual_preds = individual_pd["individual"][0]
40+
41+
# Center at zero for easier interpretation
42+
avg_predictions = avg_predictions - np.mean(avg_predictions)
43+
individual_preds = individual_preds - np.mean(individual_preds, axis=1, keepdims=True)
44+
45+
# Compute confidence intervals (5th and 95th percentile across samples)
46+
lower_bound = np.percentile(individual_preds, 5, axis=0)
47+
upper_bound = np.percentile(individual_preds, 95, axis=0)
48+
49+
# Subsample training data for rug plot
50+
rug_sample_idx = np.random.choice(len(X), size=50, replace=False)
51+
rug_values = X[rug_sample_idx, feature_idx]
52+
53+
# Create Highcharts chart
54+
chart = Chart(container="container")
55+
chart.options = HighchartsOptions()
56+
57+
# Chart configuration
58+
chart.options.chart = {"type": "line", "width": 4800, "height": 2700, "backgroundColor": "#ffffff", "marginBottom": 200}
59+
60+
# Title
61+
chart.options.title = {
62+
"text": "pdp-basic · highcharts · pyplots.ai",
63+
"style": {"fontSize": "48px", "fontWeight": "bold"},
64+
}
65+
66+
chart.options.subtitle = {"text": "Partial Dependence of Feature 0 on Model Predictions", "style": {"fontSize": "32px"}}
67+
68+
# Axes
69+
chart.options.x_axis = {
70+
"title": {"text": "Feature 0 Value", "style": {"fontSize": "36px"}},
71+
"labels": {"style": {"fontSize": "28px"}},
72+
"tickInterval": 0.5,
73+
"gridLineWidth": 1,
74+
"gridLineColor": "rgba(0,0,0,0.1)",
75+
"plotLines": [
76+
{"value": float(rug_values[i]), "width": 3, "color": "rgba(48,105,152,0.4)", "zIndex": 1}
77+
for i in range(len(rug_values))
78+
],
79+
}
80+
81+
chart.options.y_axis = {
82+
"title": {"text": "Partial Dependence (centered)", "style": {"fontSize": "36px"}},
83+
"labels": {"style": {"fontSize": "28px"}},
84+
"gridLineWidth": 1,
85+
"gridLineColor": "rgba(0,0,0,0.15)",
86+
"plotLines": [{"value": 0, "width": 2, "color": "#888888", "dashStyle": "Dash", "zIndex": 2}],
87+
}
88+
89+
# Legend
90+
chart.options.legend = {
91+
"enabled": True,
92+
"align": "right",
93+
"verticalAlign": "top",
94+
"layout": "vertical",
95+
"x": -50,
96+
"y": 100,
97+
"itemStyle": {"fontSize": "28px"},
98+
}
99+
100+
# Tooltip
101+
chart.options.tooltip = {"shared": True, "valueDecimals": 2, "style": {"fontSize": "24px"}}
102+
103+
# Plot options
104+
chart.options.plot_options = {
105+
"series": {"animation": False},
106+
"arearange": {"fillOpacity": 0.3, "lineWidth": 0},
107+
"line": {"lineWidth": 5},
108+
}
109+
110+
# Add confidence band as arearange series
111+
confidence_data = [
112+
[float(feature_values[i]), float(lower_bound[i]), float(upper_bound[i])] for i in range(len(feature_values))
113+
]
114+
115+
confidence_series = AreaRangeSeries()
116+
confidence_series.data = confidence_data
117+
confidence_series.name = "90% Confidence Interval"
118+
confidence_series.color = "rgba(48,105,152,0.25)"
119+
confidence_series.fill_opacity = 0.25
120+
chart.add_series(confidence_series)
121+
122+
# Add main PDP line
123+
pdp_data = [[float(feature_values[i]), float(avg_predictions[i])] for i in range(len(feature_values))]
124+
125+
pdp_series = LineSeries()
126+
pdp_series.data = pdp_data
127+
pdp_series.name = "Partial Dependence"
128+
pdp_series.color = "#306998" # Python Blue
129+
pdp_series.marker = {"enabled": False}
130+
chart.add_series(pdp_series)
131+
132+
# Export to PNG via Selenium
133+
highcharts_url = "https://code.highcharts.com/highcharts.js"
134+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
135+
highcharts_js = response.read().decode("utf-8")
136+
137+
highcharts_more_url = "https://code.highcharts.com/highcharts-more.js"
138+
with urllib.request.urlopen(highcharts_more_url, timeout=30) as response:
139+
highcharts_more_js = response.read().decode("utf-8")
140+
141+
html_str = chart.to_js_literal()
142+
html_content = f"""<!DOCTYPE html>
143+
<html>
144+
<head>
145+
<meta charset="utf-8">
146+
<script>{highcharts_js}</script>
147+
<script>{highcharts_more_js}</script>
148+
</head>
149+
<body style="margin:0;">
150+
<div id="container" style="width: 4800px; height: 2700px;"></div>
151+
<script>{html_str}</script>
152+
</body>
153+
</html>"""
154+
155+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
156+
f.write(html_content)
157+
temp_path = f.name
158+
159+
# Also save the HTML for interactive viewing
160+
with open("plot.html", "w", encoding="utf-8") as f:
161+
f.write(html_content)
162+
163+
chrome_options = Options()
164+
chrome_options.add_argument("--headless")
165+
chrome_options.add_argument("--no-sandbox")
166+
chrome_options.add_argument("--disable-dev-shm-usage")
167+
chrome_options.add_argument("--disable-gpu")
168+
chrome_options.add_argument("--window-size=4800,2700")
169+
170+
driver = webdriver.Chrome(options=chrome_options)
171+
driver.get(f"file://{temp_path}")
172+
time.sleep(5)
173+
driver.save_screenshot("plot.png")
174+
driver.quit()
175+
176+
Path(temp_path).unlink()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: highcharts
2+
specification_id: pdp-basic
3+
created: '2025-12-31T05:35:01Z'
4+
updated: '2025-12-31T05:47:18Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20612801369
7+
issue: 2922
8+
python_version: 3.13.11
9+
library_version: unknown
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/pdp-basic/highcharts/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/pdp-basic/highcharts/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/pdp-basic/highcharts/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Correct implementation of PDP with confidence intervals computed from individual
17+
conditional expectations
18+
- Includes rug plot using plotLines to show training data distribution
19+
- Centers partial dependence at zero for easier interpretation as recommended in
20+
spec
21+
- Clean colorblind-safe color scheme using Python Blue (#306998)
22+
- Proper use of AreaRangeSeries for the confidence band
23+
weaknesses:
24+
- Legend is positioned in top-right corner with significant empty space between
25+
it and the data area
26+
- Rug plot lines could be slightly thicker or more prominent at the bottom of the
27+
plot rather than spanning full height
28+
- Could add more descriptive tooltip content showing exact values

0 commit comments

Comments
 (0)