Skip to content

Commit 2ee0543

Browse files
feat(plotly): implement contour-decision-boundary (#2978)
## Implementation: `contour-decision-boundary` - plotly Implements the **plotly** version of `contour-decision-boundary`. **File:** `plots/contour-decision-boundary/implementations/plotly.py` **Parent Issue:** #2921 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20612912968)* --------- 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 6c4960f commit 2ee0543

2 files changed

Lines changed: 162 additions & 0 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
""" pyplots.ai
2+
contour-decision-boundary: Decision Boundary Classifier Visualization
3+
Library: plotly 6.5.0 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-31
5+
"""
6+
7+
import numpy as np
8+
import plotly.graph_objects as go
9+
from sklearn.datasets import make_moons
10+
from sklearn.neighbors import KNeighborsClassifier
11+
from sklearn.preprocessing import StandardScaler
12+
13+
14+
# Data - Generate moon-shaped classification data
15+
np.random.seed(42)
16+
X, y = make_moons(n_samples=200, noise=0.25, random_state=42)
17+
18+
# Scale features for better visualization
19+
scaler = StandardScaler()
20+
X = scaler.fit_transform(X)
21+
22+
# Train a KNN classifier
23+
clf = KNeighborsClassifier(n_neighbors=15)
24+
clf.fit(X, y)
25+
26+
# Create mesh grid for decision boundary
27+
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
28+
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
29+
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 150), np.linspace(y_min, y_max, 150))
30+
31+
# Get predictions for mesh grid
32+
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
33+
Z = Z.reshape(xx.shape)
34+
35+
# Get prediction probabilities for smoother contours
36+
Z_prob = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
37+
Z_prob = Z_prob.reshape(xx.shape)
38+
39+
# Create figure
40+
fig = go.Figure()
41+
42+
# Add decision boundary contour using probability
43+
fig.add_trace(
44+
go.Contour(
45+
x=np.linspace(x_min, x_max, 150),
46+
y=np.linspace(y_min, y_max, 150),
47+
z=Z_prob,
48+
colorscale=[[0, "#306998"], [1, "#FFD43B"]],
49+
opacity=0.6,
50+
showscale=True,
51+
colorbar=dict(
52+
title=dict(text="Class Probability", font=dict(size=18)), tickfont=dict(size=16), len=0.7, thickness=25
53+
),
54+
contours=dict(showlines=False),
55+
hoverinfo="skip",
56+
)
57+
)
58+
59+
# Add decision boundary line (where probability = 0.5)
60+
fig.add_trace(
61+
go.Contour(
62+
x=np.linspace(x_min, x_max, 150),
63+
y=np.linspace(y_min, y_max, 150),
64+
z=Z_prob,
65+
showscale=False,
66+
contours=dict(start=0.5, end=0.5, size=0.1, coloring="lines", showlabels=False),
67+
line=dict(color="white", width=3, dash="dash"),
68+
hoverinfo="skip",
69+
)
70+
)
71+
72+
# Separate training points by class
73+
X_class0 = X[y == 0]
74+
X_class1 = X[y == 1]
75+
76+
# Add training points - Class 0
77+
fig.add_trace(
78+
go.Scatter(
79+
x=X_class0[:, 0],
80+
y=X_class0[:, 1],
81+
mode="markers",
82+
marker=dict(size=14, color="#306998", line=dict(color="white", width=2), symbol="circle"),
83+
name="Class 0",
84+
hovertemplate="Feature 1: %{x:.2f}<br>Feature 2: %{y:.2f}<extra>Class 0</extra>",
85+
)
86+
)
87+
88+
# Add training points - Class 1
89+
fig.add_trace(
90+
go.Scatter(
91+
x=X_class1[:, 0],
92+
y=X_class1[:, 1],
93+
mode="markers",
94+
marker=dict(size=14, color="#FFD43B", line=dict(color="black", width=2), symbol="diamond"),
95+
name="Class 1",
96+
hovertemplate="Feature 1: %{x:.2f}<br>Feature 2: %{y:.2f}<extra>Class 1</extra>",
97+
)
98+
)
99+
100+
# Update layout
101+
fig.update_layout(
102+
title=dict(text="contour-decision-boundary · plotly · pyplots.ai", font=dict(size=28), x=0.5, xanchor="center"),
103+
xaxis=dict(
104+
title=dict(text="Feature 1 (Standardized)", font=dict(size=22)),
105+
tickfont=dict(size=18),
106+
showgrid=True,
107+
gridwidth=1,
108+
gridcolor="rgba(128, 128, 128, 0.3)",
109+
zeroline=False,
110+
),
111+
yaxis=dict(
112+
title=dict(text="Feature 2 (Standardized)", font=dict(size=22)),
113+
tickfont=dict(size=18),
114+
showgrid=True,
115+
gridwidth=1,
116+
gridcolor="rgba(128, 128, 128, 0.3)",
117+
zeroline=False,
118+
scaleanchor="x",
119+
scaleratio=1,
120+
),
121+
template="plotly_white",
122+
legend=dict(
123+
font=dict(size=18),
124+
x=0.02,
125+
y=0.98,
126+
xanchor="left",
127+
yanchor="top",
128+
bgcolor="rgba(255, 255, 255, 0.8)",
129+
bordercolor="rgba(0, 0, 0, 0.3)",
130+
borderwidth=1,
131+
),
132+
margin=dict(l=80, r=100, t=100, b=80),
133+
)
134+
135+
# Save as PNG and HTML
136+
fig.write_image("plot.png", width=1600, height=900, scale=3)
137+
fig.write_html("plot.html", include_plotlyjs="cdn")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library: plotly
2+
specification_id: contour-decision-boundary
3+
created: '2025-12-31T05:44:26Z'
4+
updated: '2025-12-31T05:57:15Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20612912968
7+
issue: 2921
8+
python_version: 3.13.11
9+
library_version: 6.5.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/contour-decision-boundary/plotly/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/contour-decision-boundary/plotly/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/contour-decision-boundary/plotly/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent use of probability-based contour coloring rather than just discrete
17+
class regions
18+
- Clear decision boundary line at probability=0.5 with dashed styling
19+
- Good marker differentiation using both shape (circle vs diamond) and color
20+
- Professional colorbar showing class probability scale
21+
- Responsive layout with proper sizing for 4800x2700 output
22+
weaknesses:
23+
- Legend position in upper-left overlaps slightly with some Class 0 data points
24+
- Could enhance interactivity by adding hover info to the contour regions showing
25+
probability values

0 commit comments

Comments
 (0)