{"spec_id":"contour-decision-boundary","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\ncontour-decision-boundary: Decision Boundary Classifier Visualization\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 95/100 | Updated: 2026-05-16\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\nfrom sklearn.datasets import make_moons\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.preprocessing import StandardScaler\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nGRID = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\n# Data - Generate moon-shaped classification data\nnp.random.seed(42)\nX, y = make_moons(n_samples=200, noise=0.25, random_state=42)\n\n# Scale features for better visualization\nscaler = StandardScaler()\nX = scaler.fit_transform(X)\n\n# Train a KNN classifier\nclf = KNeighborsClassifier(n_neighbors=15)\nclf.fit(X, y)\n\n# Create mesh grid for decision boundary\nx_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5\ny_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5\nxx, yy = np.meshgrid(np.linspace(x_min, x_max, 150), np.linspace(y_min, y_max, 150))\n\n# Get prediction probabilities for smooth contours\nZ_prob = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]\nZ_prob = Z_prob.reshape(xx.shape)\n\n# Create figure\nfig = go.Figure()\n\n# Add decision boundary contour using probability\nfig.add_trace(\n    go.Contour(\n        x=np.linspace(x_min, x_max, 150),\n        y=np.linspace(y_min, y_max, 150),\n        z=Z_prob,\n        colorscale=[[0, IMPRINT[0]], [1, IMPRINT[1]]],\n        opacity=0.4,\n        showscale=True,\n        colorbar=dict(\n            title=dict(text=\"Class Probability\", font=dict(size=18)),\n            tickfont=dict(size=16),\n            len=0.7,\n            thickness=25,\n            bordercolor=INK_SOFT,\n        ),\n        contours=dict(showlines=False),\n        hovertemplate=\"Feature 1: %{x:.2f}<br>Feature 2: %{y:.2f}<br>Probability: %{z:.2f}<extra></extra>\",\n    )\n)\n\n# Add decision boundary line (where probability = 0.5)\nfig.add_trace(\n    go.Contour(\n        x=np.linspace(x_min, x_max, 150),\n        y=np.linspace(y_min, y_max, 150),\n        z=Z_prob,\n        showscale=False,\n        contours=dict(start=0.5, end=0.5, size=0.1, coloring=\"lines\", showlabels=False),\n        line=dict(color=INK_SOFT, width=3, dash=\"dash\"),\n        hoverinfo=\"skip\",\n    )\n)\n\n# Separate training points by class\nX_class0 = X[y == 0]\nX_class1 = X[y == 1]\n\n# Add training points - Class 0\nfig.add_trace(\n    go.Scatter(\n        x=X_class0[:, 0],\n        y=X_class0[:, 1],\n        mode=\"markers\",\n        marker=dict(size=14, color=IMPRINT[0], line=dict(color=PAGE_BG, width=2), symbol=\"circle\"),\n        name=\"Class 0\",\n        hovertemplate=\"Feature 1: %{x:.2f}<br>Feature 2: %{y:.2f}<br>Class: 0<extra></extra>\",\n    )\n)\n\n# Add training points - Class 1\nfig.add_trace(\n    go.Scatter(\n        x=X_class1[:, 0],\n        y=X_class1[:, 1],\n        mode=\"markers\",\n        marker=dict(size=14, color=IMPRINT[1], line=dict(color=PAGE_BG, width=2), symbol=\"diamond\"),\n        name=\"Class 1\",\n        hovertemplate=\"Feature 1: %{x:.2f}<br>Feature 2: %{y:.2f}<br>Class: 1<extra></extra>\",\n    )\n)\n\n# Update layout with theme-adaptive colors\nfig.update_layout(\n    title=dict(\n        text=\"contour-decision-boundary · plotly · anyplot.ai\", font=dict(size=28, color=INK), x=0.5, xanchor=\"center\"\n    ),\n    xaxis=dict(\n        title=dict(text=\"Feature 1 (Standardized)\", font=dict(size=22, color=INK)),\n        tickfont=dict(size=18, color=INK_SOFT),\n        showgrid=True,\n        gridwidth=1,\n        gridcolor=GRID,\n        zeroline=False,\n        linecolor=INK_SOFT,\n        linewidth=2,\n    ),\n    yaxis=dict(\n        title=dict(text=\"Feature 2 (Standardized)\", font=dict(size=22, color=INK)),\n        tickfont=dict(size=18, color=INK_SOFT),\n        showgrid=True,\n        gridwidth=1,\n        gridcolor=GRID,\n        zeroline=False,\n        linecolor=INK_SOFT,\n        linewidth=2,\n        scaleanchor=\"x\",\n        scaleratio=1,\n    ),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font=dict(color=INK),\n    legend=dict(\n        font=dict(size=18, color=INK_SOFT),\n        x=0.98,\n        y=0.02,\n        xanchor=\"right\",\n        yanchor=\"bottom\",\n        bgcolor=ELEVATED_BG,\n        bordercolor=INK_SOFT,\n        borderwidth=1,\n    ),\n    margin=dict(l=80, r=100, t=100, b=80),\n    hovermode=\"closest\",\n)\n\n# Save as PNG and HTML\nfig.write_image(f\"plot-{THEME}.png\", width=1600, height=900, scale=3)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}