diff --git a/plots/contour-decision-boundary/implementations/python/plotly.py b/plots/contour-decision-boundary/implementations/python/plotly.py
index 99f4d5e820..11777d8447 100644
--- a/plots/contour-decision-boundary/implementations/python/plotly.py
+++ b/plots/contour-decision-boundary/implementations/python/plotly.py
@@ -1,9 +1,11 @@
-""" pyplots.ai
+""" anyplot.ai
contour-decision-boundary: Decision Boundary Classifier Visualization
-Library: plotly 6.5.0 | Python 3.13.11
-Quality: 92/100 | Created: 2025-12-31
+Library: plotly 6.7.0 | Python 3.13.13
+Quality: 95/100 | Updated: 2026-05-16
"""
+import os
+
import numpy as np
import plotly.graph_objects as go
from sklearn.datasets import make_moons
@@ -11,6 +13,15 @@
from sklearn.preprocessing import StandardScaler
+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 = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"]
+
# Data - Generate moon-shaped classification data
np.random.seed(42)
X, y = make_moons(n_samples=200, noise=0.25, random_state=42)
@@ -28,11 +39,7 @@
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 150), np.linspace(y_min, y_max, 150))
-# Get predictions for mesh grid
-Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
-Z = Z.reshape(xx.shape)
-
-# Get prediction probabilities for smoother contours
+# Get prediction probabilities for smooth contours
Z_prob = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z_prob = Z_prob.reshape(xx.shape)
@@ -45,14 +52,18 @@
x=np.linspace(x_min, x_max, 150),
y=np.linspace(y_min, y_max, 150),
z=Z_prob,
- colorscale=[[0, "#306998"], [1, "#FFD43B"]],
- opacity=0.6,
+ colorscale=[[0, OKABE_ITO[0]], [1, OKABE_ITO[1]]],
+ opacity=0.4,
showscale=True,
colorbar=dict(
- title=dict(text="Class Probability", font=dict(size=18)), tickfont=dict(size=16), len=0.7, thickness=25
+ title=dict(text="Class Probability", font=dict(size=18)),
+ tickfont=dict(size=16),
+ len=0.7,
+ thickness=25,
+ bordercolor=INK_SOFT,
),
contours=dict(showlines=False),
- hoverinfo="skip",
+ hovertemplate="Feature 1: %{x:.2f}
Feature 2: %{y:.2f}
Probability: %{z:.2f}",
)
)
@@ -64,7 +75,7 @@
z=Z_prob,
showscale=False,
contours=dict(start=0.5, end=0.5, size=0.1, coloring="lines", showlabels=False),
- line=dict(color="white", width=3, dash="dash"),
+ line=dict(color=INK_SOFT, width=3, dash="dash"),
hoverinfo="skip",
)
)
@@ -79,9 +90,9 @@
x=X_class0[:, 0],
y=X_class0[:, 1],
mode="markers",
- marker=dict(size=14, color="#306998", line=dict(color="white", width=2), symbol="circle"),
+ marker=dict(size=14, color=OKABE_ITO[0], line=dict(color=PAGE_BG, width=2), symbol="circle"),
name="Class 0",
- hovertemplate="Feature 1: %{x:.2f}
Feature 2: %{y:.2f}Class 0",
+ hovertemplate="Feature 1: %{x:.2f}
Feature 2: %{y:.2f}
Class: 0",
)
)
@@ -91,47 +102,56 @@
x=X_class1[:, 0],
y=X_class1[:, 1],
mode="markers",
- marker=dict(size=14, color="#FFD43B", line=dict(color="black", width=2), symbol="diamond"),
+ marker=dict(size=14, color=OKABE_ITO[1], line=dict(color=PAGE_BG, width=2), symbol="diamond"),
name="Class 1",
- hovertemplate="Feature 1: %{x:.2f}
Feature 2: %{y:.2f}Class 1",
+ hovertemplate="Feature 1: %{x:.2f}
Feature 2: %{y:.2f}
Class: 1",
)
)
-# Update layout
+# Update layout with theme-adaptive colors
fig.update_layout(
- title=dict(text="contour-decision-boundary · plotly · pyplots.ai", font=dict(size=28), x=0.5, xanchor="center"),
+ title=dict(
+ text="contour-decision-boundary · plotly · anyplot.ai", font=dict(size=28, color=INK), x=0.5, xanchor="center"
+ ),
xaxis=dict(
- title=dict(text="Feature 1 (Standardized)", font=dict(size=22)),
- tickfont=dict(size=18),
+ title=dict(text="Feature 1 (Standardized)", font=dict(size=22, color=INK)),
+ tickfont=dict(size=18, color=INK_SOFT),
showgrid=True,
gridwidth=1,
- gridcolor="rgba(128, 128, 128, 0.3)",
+ gridcolor=GRID,
zeroline=False,
+ linecolor=INK_SOFT,
+ linewidth=2,
),
yaxis=dict(
- title=dict(text="Feature 2 (Standardized)", font=dict(size=22)),
- tickfont=dict(size=18),
+ title=dict(text="Feature 2 (Standardized)", font=dict(size=22, color=INK)),
+ tickfont=dict(size=18, color=INK_SOFT),
showgrid=True,
gridwidth=1,
- gridcolor="rgba(128, 128, 128, 0.3)",
+ gridcolor=GRID,
zeroline=False,
+ linecolor=INK_SOFT,
+ linewidth=2,
scaleanchor="x",
scaleratio=1,
),
- template="plotly_white",
+ paper_bgcolor=PAGE_BG,
+ plot_bgcolor=PAGE_BG,
+ font=dict(color=INK),
legend=dict(
- font=dict(size=18),
- x=0.02,
- y=0.98,
- xanchor="left",
- yanchor="top",
- bgcolor="rgba(255, 255, 255, 0.8)",
- bordercolor="rgba(0, 0, 0, 0.3)",
+ font=dict(size=18, color=INK_SOFT),
+ x=0.98,
+ y=0.02,
+ xanchor="right",
+ yanchor="bottom",
+ bgcolor=ELEVATED_BG,
+ bordercolor=INK_SOFT,
borderwidth=1,
),
margin=dict(l=80, r=100, t=100, b=80),
+ hovermode="closest",
)
# Save as PNG and HTML
-fig.write_image("plot.png", width=1600, height=900, scale=3)
-fig.write_html("plot.html", include_plotlyjs="cdn")
+fig.write_image(f"plot-{THEME}.png", width=1600, height=900, scale=3)
+fig.write_html(f"plot-{THEME}.html", include_plotlyjs="cdn")
diff --git a/plots/contour-decision-boundary/metadata/python/plotly.yaml b/plots/contour-decision-boundary/metadata/python/plotly.yaml
index 64884203e1..137531c01d 100644
--- a/plots/contour-decision-boundary/metadata/python/plotly.yaml
+++ b/plots/contour-decision-boundary/metadata/python/plotly.yaml
@@ -1,167 +1,178 @@
library: plotly
+language: python
specification_id: contour-decision-boundary
created: '2025-12-31T05:44:26Z'
-updated: '2025-12-31T05:57:15Z'
-generated_by: claude-opus-4-5-20251101
-workflow_run: 20612912968
+updated: '2026-05-16T07:08:48Z'
+generated_by: claude-haiku
+workflow_run: 25955652261
issue: 2921
-python_version: 3.13.11
-library_version: 6.5.0
-preview_url: https://storage.googleapis.com/anyplot-images/plots/contour-decision-boundary/plotly/plot.png
-preview_html: https://storage.googleapis.com/anyplot-images/plots/contour-decision-boundary/plotly/plot.html
-quality_score: 92
-impl_tags:
- dependencies:
- - sklearn
- techniques:
- - colorbar
- - html-export
- patterns:
- - data-generation
- - matrix-construction
- dataprep:
- - normalization
- styling: []
+python_version: 3.13.13
+library_version: 6.7.0
+preview_url_light: https://storage.googleapis.com/anyplot-images/plots/contour-decision-boundary/python/plotly/plot-light.png
+preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/contour-decision-boundary/python/plotly/plot-dark.png
+preview_html_light: https://storage.googleapis.com/anyplot-images/plots/contour-decision-boundary/python/plotly/plot-light.html
+preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/contour-decision-boundary/python/plotly/plot-dark.html
+quality_score: 95
review:
strengths:
- - Excellent use of probability-based contour coloring rather than just discrete
- class regions
- - Clear decision boundary line at probability=0.5 with dashed styling
- - Good marker differentiation using both shape (circle vs diamond) and color
- - Professional colorbar showing class probability scale
- - Responsive layout with proper sizing for 4800x2700 output
+ - Perfect theme adaptation with proper color tokens for both light and dark renders
+ - Clean, reproducible code with deterministic random seed
+ - Excellent visual quality in both themes with all text clearly readable
+ - Proper implementation of decision boundary visualization using Contour traces
+ - Okabe-Ito palette correctly applied throughout
+ - Well-designed layout with appropriate margins and whitespace
+ - Comprehensive specification compliance
weaknesses:
- - Legend position in upper-left overlaps slightly with some Class 0 data points
- - Could enhance interactivity by adding hover info to the contour regions showing
- probability values
- image_description: The plot displays a decision boundary visualization for a KNN
- classifier trained on moon-shaped data. The background shows a continuous gradient
- from blue (Class 0 probability) to yellow (Class 1 probability), with a dashed
- yellow/orange decision boundary line at the 0.5 probability threshold. Blue circular
- markers represent Class 0 data points (clustered in upper-left region), while
- yellow diamond markers represent Class 1 data points (clustered in lower-right
- region). The title reads "contour-decision-boundary · plotly · pyplots.ai" at
- the top. Axis labels show "Feature 1 (Standardized)" and "Feature 2 (Standardized)".
- A colorbar on the right indicates "Class Probability" from 0 to 1. The legend
- in the upper-left shows Class 0 (circle) and Class 1 (diamond).
+ - Design Excellence could be elevated with additional visual refinement or distinctive
+ styling choices
+ - Could explore more advanced plotly features for enhanced library mastery
+ image_description: |-
+ Light render (plot-light.png):
+ Background: Warm off-white (#FAF8F1)
+ Chrome: Title "contour-decision-boundary · plotly · anyplot.ai" in dark ink (#1A1A17), axis labels "Feature 1 (Standardized)" and "Feature 2 (Standardized)" in #1A1A17, tick labels in #4A4A44 (INK_SOFT). All clearly readable against light background.
+ Data: Filled contour regions transitioning from green (#009E73, probability 0) to orange (#D55E00, probability 1) with opacity 0.4. Dashed yellow line at probability 0.5 marks the decision boundary. Training points overlaid: green circles (Class 0) and orange diamonds (Class 1) with white edges. Colorbar shows "Class Probability" scale.
+ Legibility verdict: PASS - All text is readable, no light-on-light issues, contours and training points clearly distinguishable.
+
+ Dark render (plot-dark.png):
+ Background: Warm near-black (#1A1A17)
+ Chrome: Title in light ink (#F0EFE8), axis labels in #F0EFE8, tick labels in #B8B7B0 (INK_SOFT). All clearly readable against dark background. Legend has dark background (#242420) with visible border.
+ Data: Contour colors are identical to light render (green to orange), confirming theme-independent data palette. Dashed boundary line visible as yellow. Training point colors match light render exactly (green circles, orange diamonds).
+ Legibility verdict: PASS - All text is readable, no dark-on-dark failures. Data colors identical to light render, proving proper theme adaptation.
criteria_checklist:
visual_quality:
- score: 37
- max: 40
+ score: 30
+ max: 30
items:
- id: VQ-01
name: Text Legibility
- score: 10
- max: 10
+ score: 8
+ max: 8
passed: true
- comment: Title, axis labels, tick labels, and colorbar text all clearly readable
- at proper sizes
+ comment: Font sizes correct (28px title, 22px axis labels, 18px ticks); all
+ text readable in both themes
- id: VQ-02
name: No Overlap
- score: 8
- max: 8
+ score: 6
+ max: 6
passed: true
- comment: No overlapping text elements; legend well-positioned
+ comment: No overlapping text; legend and colorbar properly positioned without
+ collision
- id: VQ-03
name: Element Visibility
- score: 7
- max: 8
+ score: 6
+ max: 6
passed: true
- comment: Markers well-sized with good contrast; white/black borders help visibility
- against background
+ comment: Contour regions, training points, and decision boundary all clearly
+ visible
- id: VQ-04
name: Color Accessibility
- score: 5
- max: 5
+ score: 2
+ max: 2
passed: true
- comment: Blue and yellow palette is colorblind-friendly; good contrast between
- classes
+ comment: Okabe-Ito palette (green/orange) is colorblind-safe; high contrast
- id: VQ-05
- name: Layout Balance
- score: 5
- max: 5
+ name: Layout & Canvas
+ score: 4
+ max: 4
passed: true
- comment: Good use of canvas space; plot fills appropriate area with balanced
- margins
+ comment: Good proportions; 1600×900 with 3× scale; margins (80,100,100,80)
+ provide generosity
- id: VQ-06
- name: Axis Labels
+ name: Axis Labels & Title
score: 2
max: 2
passed: true
- comment: 'Descriptive labels with units: "Feature 1 (Standardized)"'
+ comment: Title format correct; axis labels include units (Standardized)
- id: VQ-07
- name: Grid & Legend
- score: 0
+ name: Palette Compliance
+ score: 2
max: 2
passed: true
- comment: Grid is subtle but legend overlaps slightly with data points in upper-left
+ comment: 'First series #009E73, second #D55E00 (Okabe-Ito order); colorscale
+ uses these endpoints; plot backgrounds #FAF8F1/#1A1A17; both renders theme-correct'
+ design_excellence:
+ score: 17
+ max: 20
+ items:
+ - id: DE-01
+ name: Aesthetic Sophistication
+ score: 7
+ max: 8
+ passed: true
+ comment: Custom colorscale with Okabe-Ito endpoints; thoughtful opacity (0.4);
+ well-composed layers; professional execution
+ - id: DE-02
+ name: Visual Refinement
+ score: 5
+ max: 6
+ passed: true
+ comment: Clean, minimal design; subtle grid; proper legend background and
+ border; colorbar well-styled
+ - id: DE-03
+ name: Data Storytelling
+ score: 5
+ max: 6
+ passed: true
+ comment: 'Clear narrative: contour regions show class partition, dashed line
+ shows decision threshold, training points show ground truth; visual hierarchy
+ evident'
spec_compliance:
- score: 25
- max: 25
+ score: 15
+ max: 15
items:
- id: SC-01
name: Plot Type
- score: 8
- max: 8
+ score: 5
+ max: 5
passed: true
comment: Correct decision boundary contour visualization
- id: SC-02
- name: Data Mapping
- score: 5
- max: 5
+ name: Required Features
+ score: 4
+ max: 4
passed: true
- comment: X1/X2 features correctly mapped to axes, class labels shown via markers
+ comment: 'All spec requirements met: decision boundary, training data overlay,
+ distinct colors, legend'
- id: SC-03
- name: Required Features
- score: 5
- max: 5
+ name: Data Mapping
+ score: 3
+ max: 3
passed: true
- comment: Has colored regions, training points overlay, legend, decision boundary
- line
+ comment: X1/X2 correctly mapped to axes; y shown via marker color and shape;
+ all data visible
- id: SC-04
- name: Data Range
+ name: Title & Legend
score: 3
max: 3
passed: true
- comment: All data points visible with appropriate padding
- - id: SC-05
- name: Legend Accuracy
- score: 2
- max: 2
- passed: true
- comment: Legend correctly identifies Class 0 and Class 1 with matching markers
- - id: SC-06
- name: Title Format
- score: 2
- max: 2
- passed: true
- comment: 'Uses correct format: "{spec-id} · {library} · pyplots.ai"'
+ comment: Title correct; legend labels match data classes
data_quality:
- score: 18
- 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 curved decision boundary, probability gradient, and overlapping
- classes well; could show some misclassified points more prominently
+ comment: 'Comprehensive: probability gradient, decision boundary line, training
+ points, decision threshold at 0.5'
- id: DQ-02
name: Realistic Context
- score: 6
- max: 7
+ score: 5
+ max: 5
passed: true
- comment: Synthetic make_moons data is standard ML example, but context is
- generic
+ comment: make_moons is realistic ML scenario; noise=0.25 shows real-world
+ classification challenge
- id: DQ-03
name: Appropriate Scale
- score: 5
- max: 5
+ score: 4
+ max: 4
passed: true
- comment: Standardized values are sensible for ML features
+ comment: 200 samples, 150×150 mesh grid, standardized features—appropriate
+ for visualization
code_quality:
- score: 9
+ score: 10
max: 10
items:
- id: CQ-01
@@ -169,42 +180,62 @@ review:
score: 3
max: 3
passed: true
- comment: 'Linear flow: imports → data → model → plot → save'
+ comment: Simple procedural code; no unnecessary functions or classes
- id: CQ-02
name: Reproducibility
- score: 3
- max: 3
+ score: 2
+ max: 2
passed: true
- comment: Fixed seed (np.random.seed(42) and random_state=42)
+ comment: Uses np.random.seed(42); KNeighborsClassifier(n_neighbors=15) is
+ deterministic
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
- comment: All imports used (numpy, plotly.graph_objects, sklearn utilities)
+ comment: 'Only necessary imports: os, numpy, plotly, sklearn components'
- id: CQ-04
- name: No Deprecated API
- score: 1
- max: 1
+ name: Code Elegance
+ score: 2
+ max: 2
passed: true
- comment: Uses current Plotly API
+ comment: No fake UI; appropriate complexity; clear intent
- id: CQ-05
- name: Output Correct
- score: 0
+ name: Output & API
+ score: 1
max: 1
- passed: false
- comment: 'Minor: Saves both plot.png and plot.html (fine, but code style could
- be cleaner)'
- library_features:
- score: 3
- max: 5
+ passed: true
+ comment: 'Correct output: plot-{THEME}.png and plot-{THEME}.html'
+ library_mastery:
+ score: 8
+ max: 10
items:
- - id: LF-01
- name: Uses distinctive library features
- score: 3
+ - id: LM-01
+ name: Idiomatic Usage
+ score: 4
max: 5
passed: true
- comment: Good use of go.Contour for probability gradient and decision boundary
- line, but could leverage more Plotly interactivity features like hover customization
- for the contour regions
+ comment: Proper use of go.Figure(), add_trace(), Contour traces; theme adaptation
+ via environment variable follows plotly conventions
+ - id: LM-02
+ name: Distinctive Features
+ score: 4
+ max: 5
+ passed: true
+ comment: Good use of double Contour trace (filled + boundary line), custom
+ colorscale, hover templates, interactive outputs; solid distinctive features
verdict: APPROVED
+impl_tags:
+ dependencies:
+ - sklearn
+ techniques:
+ - colorbar
+ - hover-tooltips
+ - html-export
+ patterns:
+ - data-generation
+ - matrix-construction
+ dataprep:
+ - normalization
+ styling:
+ - custom-colormap