diff --git a/plots/logistic-regression/implementations/python/highcharts.py b/plots/logistic-regression/implementations/python/highcharts.py
index 630a873010..4b9c397923 100644
--- a/plots/logistic-regression/implementations/python/highcharts.py
+++ b/plots/logistic-regression/implementations/python/highcharts.py
@@ -1,12 +1,12 @@
-""" pyplots.ai
+""" anyplot.ai
logistic-regression: Logistic Regression Curve Plot
-Library: highcharts unknown | Python 3.13.11
-Quality: 91/100 | Created: 2026-01-09
+Library: highcharts unknown | Python 3.13.13
+Quality: 91/100 | Updated: 2026-05-18
"""
+import os
import tempfile
import time
-import urllib.request
from pathlib import Path
import numpy as np
@@ -20,6 +20,17 @@
from sklearn.linear_model import LogisticRegression
+# Theme-adaptive colors
+THEME = os.getenv("ANYPLOT_THEME", "light")
+PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
+ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
+INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
+INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
+GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)"
+
+# Okabe-Ito palette
+OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"]
+
# Data - Generate binary classification data
np.random.seed(42)
n_points = 150
@@ -66,12 +77,12 @@
chart = Chart(container="container")
chart.options = HighchartsOptions()
-# Chart configuration
+# Chart configuration with theme-adaptive colors
chart.options.chart = {
"width": 4800,
"height": 2700,
- "backgroundColor": "#ffffff",
- "style": {"fontFamily": "Arial, sans-serif"},
+ "backgroundColor": PAGE_BG,
+ "style": {"fontFamily": "Arial, sans-serif", "color": INK},
"spacingBottom": 100,
"spacingLeft": 50,
"spacingTop": 50,
@@ -80,40 +91,42 @@
# Title
chart.options.title = {
- "text": "logistic-regression · highcharts · pyplots.ai",
- "style": {"fontSize": "48px", "fontWeight": "bold"},
+ "text": "logistic-regression · python · highcharts · anyplot.ai",
+ "style": {"fontSize": "28px", "fontWeight": "medium", "color": INK},
}
-chart.options.subtitle = {"text": "Exam Pass Probability vs Study Hours", "style": {"fontSize": "32px"}}
-
# X-axis
chart.options.x_axis = {
- "title": {"text": "Study Hours", "style": {"fontSize": "36px"}},
- "labels": {"style": {"fontSize": "28px"}},
+ "title": {"text": "Study Hours", "style": {"fontSize": "22px", "color": INK}},
+ "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}},
"min": 0,
"max": 10,
"gridLineWidth": 1,
- "gridLineColor": "rgba(0, 0, 0, 0.1)",
+ "gridLineColor": GRID,
+ "lineColor": INK_SOFT,
+ "tickColor": INK_SOFT,
}
# Y-axis
chart.options.y_axis = {
- "title": {"text": "Probability", "style": {"fontSize": "36px"}},
- "labels": {"style": {"fontSize": "28px"}},
+ "title": {"text": "Probability", "style": {"fontSize": "22px", "color": INK}},
+ "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}},
"min": -0.05,
"max": 1.05,
"gridLineWidth": 1,
- "gridLineColor": "rgba(0, 0, 0, 0.1)",
+ "gridLineColor": GRID,
+ "lineColor": INK_SOFT,
+ "tickColor": INK_SOFT,
"plotLines": [
{
"value": 0.5,
- "color": "#888888",
+ "color": INK_SOFT,
"width": 3,
"dashStyle": "Dash",
"label": {
"text": "Decision Threshold (0.5)",
"align": "right",
- "style": {"fontSize": "24px", "color": "#888888"},
+ "style": {"fontSize": "18px", "color": INK_SOFT},
"x": -10,
"y": -10,
},
@@ -125,7 +138,10 @@
# Legend
chart.options.legend = {
"enabled": True,
- "itemStyle": {"fontSize": "28px"},
+ "itemStyle": {"fontSize": "18px", "color": INK_SOFT},
+ "backgroundColor": ELEVATED_BG,
+ "borderColor": INK_SOFT,
+ "borderWidth": 1,
"symbolRadius": 6,
"symbolHeight": 20,
"symbolWidth": 20,
@@ -133,42 +149,42 @@
# Plot options
chart.options.plot_options = {
- "scatter": {"marker": {"radius": 14, "symbol": "circle"}},
- "spline": {"lineWidth": 6, "marker": {"enabled": False}},
+ "scatter": {"marker": {"radius": 8}},
+ "spline": {"lineWidth": 3, "marker": {"enabled": False}},
"arearange": {"fillOpacity": 0.25, "lineWidth": 0, "marker": {"enabled": False}},
}
-# Confidence interval (arearange series)
+# Confidence interval (arearange series) - use first Okabe-Ito color with transparency
ci_data = [[float(x_curve[i]), float(ci_lower[i]), float(ci_upper[i])] for i in range(len(x_curve))]
ci_series = AreaRangeSeries()
ci_series.data = ci_data
ci_series.name = "95% CI"
-ci_series.color = "rgba(48, 105, 152, 0.3)"
-ci_series.fill_opacity = 0.3
+ci_series.color = OKABE_ITO[0]
+ci_series.fill_opacity = 0.2
chart.add_series(ci_series)
-# Logistic curve
+# Logistic curve - use third color for distinction from scatter points
curve_data = [[float(x_curve[i]), float(y_prob[i])] for i in range(len(x_curve))]
curve_series = SplineSeries()
curve_series.data = curve_data
curve_series.name = "Logistic Curve"
-curve_series.color = "#306998"
+curve_series.color = OKABE_ITO[2]
chart.add_series(curve_series)
-# Class 0 points (Fail)
+# Class 0 points (Fail) - first Okabe-Ito color
scatter_class0 = ScatterSeries()
scatter_class0.data = [[x_class0[i], y_class0[i]] for i in range(len(x_class0))]
scatter_class0.name = "Fail (0)"
-scatter_class0.color = "rgba(48, 105, 152, 0.6)"
-scatter_class0.marker = {"radius": 14, "symbol": "circle"}
+scatter_class0.color = OKABE_ITO[0]
+scatter_class0.marker = {"radius": 8}
chart.add_series(scatter_class0)
-# Class 1 points (Pass)
+# Class 1 points (Pass) - second Okabe-Ito color
scatter_class1 = ScatterSeries()
scatter_class1.data = [[x_class1[i], y_class1[i]] for i in range(len(x_class1))]
scatter_class1.name = "Pass (1)"
-scatter_class1.color = "rgba(255, 212, 59, 0.8)"
-scatter_class1.marker = {"radius": 14, "symbol": "circle"}
+scatter_class1.color = OKABE_ITO[1]
+scatter_class1.marker = {"radius": 8}
chart.add_series(scatter_class1)
# Add model accuracy annotation
@@ -179,9 +195,9 @@
{
"point": {"x": 8.5, "y": 0.15, "xAxis": 0, "yAxis": 0},
"text": f"Accuracy: {accuracy:.1%}",
- "style": {"fontSize": "28px"},
- "backgroundColor": "rgba(255, 255, 255, 0.8)",
- "borderColor": "#306998",
+ "style": {"fontSize": "18px", "color": INK},
+ "backgroundColor": ELEVATED_BG,
+ "borderColor": INK_SOFT,
"borderWidth": 2,
"padding": 15,
}
@@ -193,20 +209,11 @@
# Credits
chart.options.credits = {"enabled": False}
-# Download Highcharts JS
-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")
-
-# Download highcharts-more for arearange
-highcharts_more_url = "https://code.highcharts.com/highcharts-more.js"
-with urllib.request.urlopen(highcharts_more_url, timeout=30) as response:
- highcharts_more_js = response.read().decode("utf-8")
-
-# Download annotations module
-annotations_url = "https://code.highcharts.com/modules/annotations.js"
-with urllib.request.urlopen(annotations_url, timeout=30) as response:
- annotations_js = response.read().decode("utf-8")
+# Load Highcharts JS from local npm package (CDN blocked in CI)
+HC_NPM = Path("/tmp/hc-tmp/node_modules/highcharts")
+highcharts_js = (HC_NPM / "highcharts.js").read_text(encoding="utf-8")
+highcharts_more_js = (HC_NPM / "highcharts-more.js").read_text(encoding="utf-8")
+annotations_js = (HC_NPM / "modules/annotations.js").read_text(encoding="utf-8")
# Generate HTML with inline scripts
html_str = chart.to_js_literal()
@@ -218,13 +225,17 @@
-
+
"""
-# Write temp HTML and take screenshot
+# Save interactive HTML (both themes)
+with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f:
+ f.write(html_content)
+
+# Write temp HTML and take screenshot for PNG
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
f.write(html_content)
temp_path = f.name
@@ -234,30 +245,12 @@
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=4900,2800")
+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.save_screenshot(f"plot-{THEME}.png")
driver.quit()
Path(temp_path).unlink()
-
-# Save interactive HTML (using CDN scripts for standalone viewing)
-html_export = f"""
-
-
-
- Logistic Regression - Highcharts
-
-
-
-
-
-
-
-
-"""
-with open("plot.html", "w", encoding="utf-8") as f:
- f.write(html_export)
diff --git a/plots/logistic-regression/metadata/python/highcharts.yaml b/plots/logistic-regression/metadata/python/highcharts.yaml
index d3e052bc4c..7add06d888 100644
--- a/plots/logistic-regression/metadata/python/highcharts.yaml
+++ b/plots/logistic-regression/metadata/python/highcharts.yaml
@@ -1,200 +1,226 @@
library: highcharts
+language: python
specification_id: logistic-regression
created: '2026-01-09T21:58:34Z'
-updated: '2026-01-09T22:02:31Z'
-generated_by: claude-opus-4-5-20251101
-workflow_run: 20866601331
+updated: '2026-05-18T08:44:10Z'
+generated_by: claude-haiku
+workflow_run: 26022639034
issue: 3550
-python_version: 3.13.11
+language_version: 3.13.13
library_version: unknown
-preview_url: https://storage.googleapis.com/anyplot-images/plots/logistic-regression/highcharts/plot.png
-preview_html: https://storage.googleapis.com/anyplot-images/plots/logistic-regression/highcharts/plot.html
+preview_url_light: https://storage.googleapis.com/anyplot-images/plots/logistic-regression/python/highcharts/plot-light.png
+preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/logistic-regression/python/highcharts/plot-dark.png
+preview_html_light: https://storage.googleapis.com/anyplot-images/plots/logistic-regression/python/highcharts/plot-light.html
+preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/logistic-regression/python/highcharts/plot-dark.html
quality_score: 91
review:
strengths:
- - Excellent implementation of all specification requirements including jittered
- points, confidence intervals, and decision threshold
- - Good use of Highcharts-specific features like AreaRangeSeries for CI and plotLines
- for threshold
- - Bootstrap method for confidence intervals shows statistical rigor
- - Colorblind-safe blue/yellow palette
- - Clear labeling with model accuracy annotation
+ - Perfect theme-adaptive implementation with correct INK/INK_SOFT tokens throughout
+ both renders
+ - 'All spec requirements met: S-shaped curve, two-class data, 95% CI band, decision
+ threshold line, accuracy annotation'
+ - 'Strong palette compliance: Okabe-Ito colors correct (Fail=#009E73, Pass=#D55E00,
+ Curve=#0072B2) with identical data colors across themes'
+ - 'Excellent code quality: deterministic with np.random.seed(42), proper bootstrap
+ confidence intervals, clean Highcharts API usage'
+ - 'Good use of library-specific features: AreaRangeSeries for CI, plotLines for
+ threshold, annotations module for accuracy display'
+ - Text legible in both themes at correct font sizes (title 28px, labels 22px, ticks
+ 18px)
+ - 'All visual elements properly scaled for 4800x2700 canvas: markers, line widths,
+ spacing all appropriate'
weaknesses:
- - Class 0 points (blue) have same color family as the logistic curve making them
- less distinct - could use a different hue
- - The for loop for bootstrap adds complexity to an otherwise linear structure
- image_description: 'The plot displays a logistic regression visualization with the
- characteristic S-shaped (sigmoid) probability curve in blue. The X-axis shows
- "Study Hours" (ranging 0-10) and Y-axis shows "Probability" (ranging -0.05 to
- 1.05). Blue circular scatter points representing the "Fail (0)" class are clustered
- near y=0 with slight jitter, while yellow/gold circular scatter points for "Pass
- (1)" class are clustered near y=1. A light blue semi-transparent confidence interval
- band surrounds the logistic curve. A gray dashed horizontal line at y=0.5 indicates
- the decision threshold with a label "Decision Threshold (0.5)" on the right side.
- An annotation box in the lower right shows "Accuracy: 92.7%". The legend at the
- bottom displays: 95% CI, Logistic Curve, Fail (0), Pass (1). The title correctly
- follows the format "logistic-regression · highcharts · pyplots.ai" with a subtitle
- "Exam Pass Probability vs Study Hours".'
+ - 'DE-02/DE-03 could be elevated: legend box styling is good but grid could be more
+ distinctive, visual hierarchy adequate but not exceptional'
+ image_description: |-
+ Light render (plot-light.png):
+ Background: Warm off-white (#FAF8F1) with generous margins and spacing
+ Chrome: Title "logistic-regression · python · highcharts · anyplot.ai" in dark text (28px), X-axis label "Study Hours" and Y-axis label "Probability" both clearly visible in INK color (#1A1A17), tick labels in secondary INK_SOFT (#4A4A44) at appropriate sizes (18px), all readable with good contrast
+ Data: Green squares for class 0 (Fail, #009E73), orange triangles for class 1 (Pass, #D55E00), blue logistic curve (#0072B2), light teal confidence interval band with 20% opacity, dashed decision threshold line at 0.5 with label, annotation box showing "Accuracy: 92.7%"
+ Legend: Shows 95% CI, Logistic Curve, Fail (0), Pass (1) with background color and border styling
+ Legibility verdict: PASS - all text is fully readable against the light background with excellent contrast
+
+ Dark render (plot-dark.png):
+ Background: Warm near-black (#1A1A17) matching light theme offset
+ Chrome: Title in light text (F0EFE8), axis labels in light text, tick labels in INK_SOFT light variant (B8B7B0), all clearly visible against dark background - no dark-on-dark failures detected
+ Data: Green squares identical to light render (#009E73), orange triangles identical (#D55E00), blue curve identical (#0072B2), confidence band in darker teal with same 20% opacity, decision threshold line visible in light color
+ Legend: Background adapted to elevated dark color (#242420), border and text in light INK_SOFT
+ Legibility verdict: PASS - all text readable against the dark background, data colors remain constant between renders, only chrome adapts to theme
criteria_checklist:
visual_quality:
- score: 36
- max: 40
+ score: 30
+ max: 30
items:
- id: VQ-01
name: Text Legibility
- score: 9
- max: 10
+ score: 8
+ max: 8
passed: true
- comment: All text readable, title and labels clear, though axis tick labels
- could be slightly larger
+ comment: Font sizes explicit, readable in both themes at correct sizes (28/22/18px)
- id: VQ-02
name: No Overlap
- score: 8
- max: 8
+ score: 6
+ max: 6
passed: true
- comment: No overlapping text elements
+ comment: All text well-spaced, data points with jitter, annotation positioned
+ cleanly
- id: VQ-03
name: Element Visibility
- score: 7
- max: 8
+ score: 6
+ max: 6
passed: true
- comment: Markers are visible with appropriate jitter, though class 0 blue
- points blend slightly with the curve color
+ comment: Markers, curve, CI band, threshold line all clearly visible
- id: VQ-04
name: Color Accessibility
- score: 5
- max: 5
+ score: 2
+ max: 2
passed: true
- comment: Blue and yellow are colorblind-safe, good contrast
+ comment: Okabe-Ito palette, colorblind-safe, good contrast
- id: VQ-05
- name: Layout Balance
- score: 5
- max: 5
+ name: Layout & Canvas
+ score: 4
+ max: 4
passed: true
- comment: Plot fills canvas well with balanced margins
+ comment: 4800x2700 well-utilized with generous margins
- id: VQ-06
- name: Axis Labels
- score: 1
+ name: Axis Labels & Title
+ score: 2
max: 2
passed: true
- comment: Descriptive labels but no units
+ comment: Title correct format, axis labels descriptive
- id: VQ-07
- name: Grid & Legend
- score: 1
+ name: Palette Compliance
+ score: 2
max: 2
passed: true
- comment: Grid is subtle, legend well placed, though legend text is small
+ comment: Okabe-Ito correct, backgrounds match theme, chrome adapts correctly
+ design_excellence:
+ score: 13
+ max: 20
+ items:
+ - id: DE-01
+ name: Aesthetic Sophistication
+ score: 6
+ max: 8
+ passed: true
+ comment: Explicit color management, intentional hierarchy, professional appearance
+ - id: DE-02
+ name: Visual Refinement
+ score: 3
+ max: 6
+ passed: true
+ comment: Grid subtle, legend styled, annotation boxes refined, good spacing
+ - id: DE-03
+ name: Data Storytelling
+ score: 4
+ max: 6
+ passed: true
+ comment: Clear focal point on sigmoid curve, decision threshold guides interpretation,
+ accuracy metric adds context
spec_compliance:
- score: 25
- max: 25
+ score: 15
+ max: 15
items:
- id: SC-01
name: Plot Type
- score: 8
- max: 8
- passed: true
- comment: Correct logistic regression curve with sigmoid shape
- - id: SC-02
- name: Data Mapping
score: 5
max: 5
passed: true
- comment: X is continuous predictor, Y shows binary outcomes and probability
- - id: SC-03
+ comment: Correct logistic regression S-shaped curve
+ - id: SC-02
name: Required Features
- score: 5
- max: 5
+ score: 4
+ max: 4
passed: true
- comment: 'Has all: jittered points, two class colors, logistic curve, 95%
- CI band, decision threshold line, accuracy annotation'
- - id: SC-04
- name: Data Range
+ comment: Points by class, curve, 95% CI, threshold, jitter all present
+ - id: SC-03
+ name: Data Mapping
score: 3
max: 3
passed: true
- comment: Axes show full data range
- - id: SC-05
- name: Legend Accuracy
- score: 2
- max: 2
- passed: true
- comment: Legend correctly identifies all series
- - id: SC-06
- name: Title Format
- score: 2
- max: 2
+ comment: X/Y axes correct, full data range shown, ranges appropriate
+ - id: SC-04
+ name: Title & Legend
+ score: 3
+ max: 3
passed: true
- comment: 'Uses correct format: logistic-regression · highcharts · pyplots.ai'
+ comment: Title format correct, legend labels match series
data_quality:
- score: 17
- max: 20
+ score: 15
+ max: 15
items:
- id: DQ-01
name: Feature Coverage
- score: 7
- max: 8
+ score: 6
+ max: 6
passed: true
- comment: Shows both classes clearly, demonstrates sigmoid transition, CI widens
- appropriately at edges
+ comment: 'All logistic regression aspects shown: classes, curve, CI, threshold,
+ accuracy'
- id: DQ-02
name: Realistic Context
- score: 7
- max: 7
+ score: 5
+ max: 5
passed: true
- comment: Exam pass probability vs study hours is a relatable educational scenario
+ comment: Study hours/pass-fail realistic for education, neutral and plausible
- id: DQ-03
name: Appropriate Scale
- score: 3
- max: 5
+ score: 4
+ max: 4
passed: true
- comment: Study hours 0-10 is reasonable, though transition point at 5 hours
- is somewhat idealized
+ comment: Study hours 0-10 reasonable, probability 0-1 correct, 150 points
+ in spec range
code_quality:
- score: 8
+ score: 10
max: 10
items:
- id: CQ-01
name: KISS Structure
- score: 2
+ score: 3
max: 3
passed: true
- comment: Has a for loop for bootstrap, but structure is mostly linear
+ comment: Linear, no functions/classes, straightforward data flow
- id: CQ-02
name: Reproducibility
- score: 3
- max: 3
+ score: 2
+ max: 2
passed: true
- comment: Uses np.random.seed(42)
+ comment: np.random.seed(42) set, deterministic
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
- comment: All imports are used
+ comment: All imports used, no waste
- id: CQ-04
- name: No Deprecated API
- score: 1
- max: 1
+ name: Code Elegance
+ score: 2
+ max: 2
passed: true
- comment: Current APIs used
+ comment: Bootstrap CI properly implemented, theme tokens clean, no fake UI
- id: CQ-05
- name: Output Correct
- score: 0
+ name: Output & API
+ score: 1
max: 1
- passed: false
- comment: Saves as plot.png correctly but also creates plot.html (expected
- for highcharts)
- library_features:
- score: 5
- max: 5
+ passed: true
+ comment: Correct plot-{THEME}.html/png output
+ library_mastery:
+ score: 8
+ max: 10
items:
- - id: LF-01
+ - id: LM-01
+ name: Idiomatic Usage
+ score: 4
+ max: 5
+ passed: true
+ comment: Proper Chart/HighchartsOptions pattern, correct series types, annotations
+ module used
+ - id: LM-02
name: Distinctive Features
- score: 5
+ score: 4
max: 5
passed: true
- comment: 'Uses Highcharts-specific features: SplineSeries, AreaRangeSeries,
- plotLines, annotations module, selenium export pattern'
+ comment: AreaRangeSeries for CI, plotLines for threshold, annotations for
+ metrics are distinctive Highcharts uses
verdict: APPROVED
impl_tags:
dependencies:
@@ -205,9 +231,7 @@ impl_tags:
- html-export
patterns:
- data-generation
- - iteration-over-groups
dataprep:
- regression
styling:
- alpha-blending
- - grid-styling