Skip to content

Commit ccefc59

Browse files
feat(altair): implement contour-decision-boundary (#6888)
## Implementation: `contour-decision-boundary` - python/altair Implements the **python/altair** version of `contour-decision-boundary`. **File:** `plots/contour-decision-boundary/implementations/python/altair.py` **Parent Issue:** #2921 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/25955735150)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent 0430626 commit ccefc59

2 files changed

Lines changed: 191 additions & 151 deletions

File tree

plots/contour-decision-boundary/implementations/python/altair.py

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
contour-decision-boundary: Decision Boundary Classifier Visualization
3-
Library: altair 6.0.0 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-31
3+
Library: altair 6.1.0 | Python 3.13.13
4+
Quality: 92/100 | Updated: 2026-05-16
55
"""
66

7+
import os
8+
79
import altair as alt
810
import numpy as np
911
import pandas as pd
1012
from sklearn.datasets import make_moons
1113
from sklearn.neighbors import KNeighborsClassifier
1214

1315

16+
# Theme tokens
17+
THEME = os.getenv("ANYPLOT_THEME", "light")
18+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
19+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
20+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
21+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
22+
23+
# Okabe-Ito palette
24+
OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"]
25+
1426
# Data - generate two-moon classification dataset
1527
np.random.seed(42)
1628
X, y = make_moons(n_samples=150, noise=0.25, random_state=42)
@@ -51,8 +63,8 @@
5163
y=alt.Y("X2:Q", bin=alt.Bin(maxbins=150), title="Feature X2"),
5264
color=alt.Color(
5365
"Class:N",
54-
scale=alt.Scale(domain=["Class A", "Class B"], range=["#306998", "#FFD43B"]),
55-
legend=alt.Legend(title="Class", titleFontSize=18, labelFontSize=16, orient="right"),
66+
scale=alt.Scale(domain=["Class A", "Class B"], range=[OKABE_ITO[0], OKABE_ITO[1]]),
67+
legend=alt.Legend(title="Decision Region", titleFontSize=18, labelFontSize=16, orient="right"),
5668
),
5769
)
5870
)
@@ -65,24 +77,24 @@
6577
x=alt.X("X1:Q"),
6678
y=alt.Y("X2:Q"),
6779
fill=alt.Color(
68-
"Class:N", scale=alt.Scale(domain=["Class A", "Class B"], range=["#306998", "#FFD43B"]), legend=None
80+
"Class:N", scale=alt.Scale(domain=["Class A", "Class B"], range=[OKABE_ITO[0], OKABE_ITO[1]]), legend=None
6981
),
70-
stroke=alt.value("#333333"),
82+
stroke=alt.value(INK_SOFT),
7183
tooltip=["X1:Q", "X2:Q", "Class:N", "Classification:N"],
7284
)
7385
)
7486

75-
# Incorrectly classified points (triangles with red stroke)
87+
# Incorrectly classified points (triangles with orange stroke)
7688
incorrect_points = (
7789
alt.Chart(train_df[train_df["Classification"] == "Incorrect"])
7890
.mark_point(shape="triangle", size=350, strokeWidth=3, filled=True)
7991
.encode(
8092
x=alt.X("X1:Q"),
8193
y=alt.Y("X2:Q"),
8294
fill=alt.Color(
83-
"Class:N", scale=alt.Scale(domain=["Class A", "Class B"], range=["#306998", "#FFD43B"]), legend=None
95+
"Class:N", scale=alt.Scale(domain=["Class A", "Class B"], range=[OKABE_ITO[0], OKABE_ITO[1]]), legend=None
8496
),
85-
stroke=alt.value("#E63946"),
97+
stroke=alt.value(OKABE_ITO[1]),
8698
tooltip=["X1:Q", "X2:Q", "Class:N", "Classification:N"],
8799
)
88100
)
@@ -109,12 +121,30 @@
109121
.properties(
110122
width=1600,
111123
height=900,
112-
title=alt.Title("contour-decision-boundary \u00b7 altair \u00b7 pyplots.ai", fontSize=28, anchor="middle"),
124+
background=PAGE_BG,
125+
title=alt.Title("contour-decision-boundary · altair · anyplot.ai", fontSize=28, anchor="middle", color=INK),
126+
)
127+
.configure_view(fill=PAGE_BG, stroke=INK_SOFT, strokeWidth=0)
128+
.configure_axis(
129+
domainColor=INK_SOFT,
130+
tickColor=INK_SOFT,
131+
gridColor=INK,
132+
gridOpacity=0.10,
133+
labelColor=INK_SOFT,
134+
titleColor=INK,
135+
labelFontSize=18,
136+
titleFontSize=22,
137+
)
138+
.configure_legend(
139+
fillColor=ELEVATED_BG,
140+
strokeColor=INK_SOFT,
141+
labelColor=INK_SOFT,
142+
titleColor=INK,
143+
labelFontSize=16,
144+
titleFontSize=18,
113145
)
114-
.configure_axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.3)
115-
.configure_view(strokeWidth=0)
116146
)
117147

118148
# Save outputs
119-
chart.save("plot.png", scale_factor=3.0)
120-
chart.save("plot.html")
149+
chart.save(f"plot-{THEME}.png", scale_factor=3.0)
150+
chart.save(f"plot-{THEME}.html")

0 commit comments

Comments
 (0)