From 9c4c670541dd4c5d25a0c9ceb723764473bd0d95 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 26 Dec 2025 17:40:44 +0000 Subject: [PATCH 1/3] feat(highcharts): implement roc-curve --- plots/roc-curve/implementations/highcharts.py | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 plots/roc-curve/implementations/highcharts.py diff --git a/plots/roc-curve/implementations/highcharts.py b/plots/roc-curve/implementations/highcharts.py new file mode 100644 index 0000000000..b46de5c8cb --- /dev/null +++ b/plots/roc-curve/implementations/highcharts.py @@ -0,0 +1,197 @@ +"""pyplots.ai +roc-curve: ROC Curve with AUC +Library: highcharts | Python 3.13 +Quality: pending | Created: 2025-12-26 +""" + +import tempfile +import time +import urllib.request +from pathlib import Path + +import numpy as np +from highcharts_core.chart import Chart +from highcharts_core.options import HighchartsOptions +from highcharts_core.options.series.area import AreaSeries +from highcharts_core.options.series.spline import SplineSeries +from selenium import webdriver +from selenium.webdriver.chrome.options import Options + + +# Data - simulate ROC curve from a binary classifier +np.random.seed(42) + +# Generate synthetic prediction scores and true labels +n_samples = 500 +y_true = np.concatenate([np.zeros(250), np.ones(250)]) +# Good classifier: positive class has higher scores +y_scores = np.concatenate( + [ + np.random.beta(2, 5, 250), # Negative class - lower scores + np.random.beta(5, 2, 250), # Positive class - higher scores + ] +) + +# Compute ROC curve manually +thresholds = np.linspace(0, 1, 200) +fpr_list = [] +tpr_list = [] +for thresh in thresholds: + predictions = (y_scores >= thresh).astype(int) + tp = np.sum((predictions == 1) & (y_true == 1)) + fp = np.sum((predictions == 1) & (y_true == 0)) + tn = np.sum((predictions == 0) & (y_true == 0)) + fn = np.sum((predictions == 0) & (y_true == 1)) + tpr = tp / (tp + fn) if (tp + fn) > 0 else 0 + fpr = fp / (fp + tn) if (fp + tn) > 0 else 0 + fpr_list.append(fpr) + tpr_list.append(tpr) + +fpr = np.array(fpr_list) +tpr = np.array(tpr_list) + +# Sort by FPR for proper curve plotting +sorted_indices = np.argsort(fpr) +fpr = fpr[sorted_indices] +tpr = tpr[sorted_indices] + +# Calculate AUC using trapezoidal rule +auc = np.trapezoid(tpr, fpr) + +# Create chart with container +chart = Chart(container="container") +chart.options = HighchartsOptions() + +# Chart configuration for 4800x2700 canvas +chart.options.chart = { + "type": "area", + "width": 4800, + "height": 2700, + "backgroundColor": "#ffffff", + "marginBottom": 250, + "marginLeft": 250, + "marginTop": 200, + "marginRight": 150, +} + +# Title +chart.options.title = { + "text": "roc-curve · highcharts · pyplots.ai", + "style": {"fontSize": "48px", "fontWeight": "bold"}, +} + +# Subtitle with AUC +chart.options.subtitle = {"text": "Binary Classifier Performance", "style": {"fontSize": "32px"}} + +# X-axis configuration +chart.options.x_axis = { + "title": {"text": "False Positive Rate", "style": {"fontSize": "36px"}}, + "labels": {"style": {"fontSize": "28px"}}, + "min": 0, + "max": 1, + "tickInterval": 0.2, + "gridLineWidth": 1, + "gridLineColor": "rgba(0, 0, 0, 0.1)", +} + +# Y-axis configuration +chart.options.y_axis = { + "title": {"text": "True Positive Rate", "style": {"fontSize": "36px"}}, + "labels": {"style": {"fontSize": "28px"}}, + "min": 0, + "max": 1, + "tickInterval": 0.2, + "gridLineWidth": 1, + "gridLineColor": "rgba(0, 0, 0, 0.1)", +} + +# Legend configuration - position in bottom-right of plot area +chart.options.legend = { + "enabled": True, + "align": "right", + "verticalAlign": "bottom", + "layout": "vertical", + "floating": True, + "x": -150, + "y": -150, + "backgroundColor": "rgba(255, 255, 255, 0.95)", + "borderWidth": 2, + "borderColor": "#cccccc", + "borderRadius": 5, + "padding": 20, + "itemStyle": {"fontSize": "28px"}, + "symbolRadius": 0, + "symbolWidth": 50, + "symbolHeight": 5, +} + +# Plot options +chart.options.plot_options = { + "area": {"fillOpacity": 0.3, "lineWidth": 6, "marker": {"enabled": False}}, + "spline": {"lineWidth": 5, "dashStyle": "Dash", "marker": {"enabled": False}}, +} + +# ROC Curve series (area under curve) +roc_data = [[float(x), float(y)] for x, y in zip(fpr, tpr, strict=True)] +roc_series = AreaSeries() +roc_series.data = roc_data +roc_series.name = f"ROC Curve (AUC = {auc:.3f})" +roc_series.color = "#306998" # Python Blue +roc_series.fill_color = { + "linearGradient": {"x1": 0, "y1": 0, "x2": 0, "y2": 1}, + "stops": [[0, "rgba(48, 105, 152, 0.5)"], [1, "rgba(48, 105, 152, 0.1)"]], +} +chart.add_series(roc_series) + +# Random classifier reference line (diagonal) +diagonal_data = [[0, 0], [1, 1]] +diagonal_series = SplineSeries() +diagonal_series.data = diagonal_data +diagonal_series.name = "Random Classifier (AUC = 0.5)" +diagonal_series.color = "#8B8000" # Dark yellow/olive for visibility on white +diagonal_series.dash_style = "ShortDash" +diagonal_series.line_width = 5 +chart.add_series(diagonal_series) + +# Download Highcharts JS for inline embedding +highcharts_url = "https://code.highcharts.com/highcharts.js" +with urllib.request.urlopen(highcharts_url, timeout=30) as response: + highcharts_js = response.read().decode("utf-8") + +# Generate HTML with inline scripts +html_str = chart.to_js_literal() +html_content = f""" + + + + + + +
+ + +""" + +# Save HTML file for interactive viewing +with open("plot.html", "w", encoding="utf-8") as f: + f.write(html_content) + +# Take screenshot with headless Chrome +with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: + f.write(html_content) + temp_path = f.name + +chrome_options = Options() +chrome_options.add_argument("--headless") +chrome_options.add_argument("--no-sandbox") +chrome_options.add_argument("--disable-dev-shm-usage") +chrome_options.add_argument("--disable-gpu") +chrome_options.add_argument("--window-size=4800,2700") + +driver = webdriver.Chrome(options=chrome_options) +driver.get(f"file://{temp_path}") +time.sleep(5) +driver.save_screenshot("plot.png") +driver.quit() + +Path(temp_path).unlink() From 037a901c2e87f79c80a462baee270511babb7d5f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 26 Dec 2025 17:41:06 +0000 Subject: [PATCH 2/3] chore(highcharts): add metadata for roc-curve --- plots/roc-curve/metadata/highcharts.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 plots/roc-curve/metadata/highcharts.yaml diff --git a/plots/roc-curve/metadata/highcharts.yaml b/plots/roc-curve/metadata/highcharts.yaml new file mode 100644 index 0000000000..a03aeaa6de --- /dev/null +++ b/plots/roc-curve/metadata/highcharts.yaml @@ -0,0 +1,19 @@ +# Per-library metadata for highcharts implementation of roc-curve +# Auto-generated by impl-generate.yml + +library: highcharts +specification_id: roc-curve +created: '2025-12-26T17:41:05Z' +updated: '2025-12-26T17:41:05Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 20526595882 +issue: 0 +python_version: 3.13.11 +library_version: unknown +preview_url: https://storage.googleapis.com/pyplots-images/plots/roc-curve/highcharts/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/roc-curve/highcharts/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/roc-curve/highcharts/plot.html +quality_score: null +review: + strengths: [] + weaknesses: [] From 84cee4e5c9ce0f522bcb1db4d779d0c776c8c38c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 26 Dec 2025 17:52:23 +0000 Subject: [PATCH 3/3] chore(highcharts): update quality score 92 and review feedback for roc-curve --- plots/roc-curve/implementations/highcharts.py | 6 ++--- plots/roc-curve/metadata/highcharts.yaml | 23 +++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/plots/roc-curve/implementations/highcharts.py b/plots/roc-curve/implementations/highcharts.py index b46de5c8cb..3639e684f6 100644 --- a/plots/roc-curve/implementations/highcharts.py +++ b/plots/roc-curve/implementations/highcharts.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai roc-curve: ROC Curve with AUC -Library: highcharts | Python 3.13 -Quality: pending | Created: 2025-12-26 +Library: highcharts unknown | Python 3.13.11 +Quality: 92/100 | Created: 2025-12-26 """ import tempfile diff --git a/plots/roc-curve/metadata/highcharts.yaml b/plots/roc-curve/metadata/highcharts.yaml index a03aeaa6de..f7ddc3e947 100644 --- a/plots/roc-curve/metadata/highcharts.yaml +++ b/plots/roc-curve/metadata/highcharts.yaml @@ -1,10 +1,7 @@ -# Per-library metadata for highcharts implementation of roc-curve -# Auto-generated by impl-generate.yml - library: highcharts specification_id: roc-curve created: '2025-12-26T17:41:05Z' -updated: '2025-12-26T17:41:05Z' +updated: '2025-12-26T17:52:23Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20526595882 issue: 0 @@ -13,7 +10,19 @@ library_version: unknown preview_url: https://storage.googleapis.com/pyplots-images/plots/roc-curve/highcharts/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/roc-curve/highcharts/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/roc-curve/highcharts/plot.html -quality_score: null +quality_score: 92 review: - strengths: [] - weaknesses: [] + strengths: + - Excellent use of AreaSeries with gradient fill to visually represent the AUC area + under the ROC curve + - Clean implementation following the library rules for Highcharts with inline JS + embedding + - Proper manual ROC curve calculation demonstrating the algorithm + - Good colorblind-safe color choices (Python Blue and olive yellow) + - Correctly formatted title following pyplots.ai convention + weaknesses: + - Legend position causes slight overlap with the x-axis 1 tick label + - Could leverage Highcharts tooltip feature to show threshold values at each point + on the curve + - Example data shows only one high-performing classifier; could include comparison + with a second model to better demonstrate ROC curve usage