Skip to content

Commit 0dc1bb4

Browse files
feat(altair): implement confusion-matrix (#2286)
## Implementation: `confusion-matrix` - altair Implements the **altair** version of `confusion-matrix`. **File:** `plots/confusion-matrix/implementations/altair.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20526592033)* --------- 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 e5da956 commit 0dc1bb4

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
""" pyplots.ai
2+
confusion-matrix: Confusion Matrix Heatmap
3+
Library: altair 6.0.0 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-26
5+
"""
6+
7+
import altair as alt
8+
import numpy as np
9+
import pandas as pd
10+
11+
12+
# Data - Multi-class classification results
13+
np.random.seed(42)
14+
class_names = ["Dog", "Cat", "Bird", "Fish"]
15+
n_classes = len(class_names)
16+
17+
# Create a realistic confusion matrix with clear patterns
18+
# Most predictions on diagonal (correct), some off-diagonal (errors)
19+
confusion = np.array(
20+
[
21+
[85, 10, 3, 2], # Dogs: mostly correct, some confused with cats
22+
[12, 78, 6, 4], # Cats: mostly correct, some confused with dogs
23+
[5, 8, 82, 5], # Birds: mostly correct, some confusion
24+
[2, 4, 3, 91], # Fish: very distinct, high accuracy
25+
]
26+
)
27+
28+
# Create long-form DataFrame for Altair
29+
rows = []
30+
for i, true_class in enumerate(class_names):
31+
for j, pred_class in enumerate(class_names):
32+
rows.append(
33+
{
34+
"True Label": true_class,
35+
"Predicted Label": pred_class,
36+
"Count": confusion[i, j],
37+
}
38+
)
39+
40+
df = pd.DataFrame(rows)
41+
42+
# Base heatmap with rectangles
43+
base = alt.Chart(df).encode(
44+
x=alt.X("Predicted Label:N", sort=class_names, axis=alt.Axis(labelAngle=0, labelFontSize=20, titleFontSize=24)),
45+
y=alt.Y("True Label:N", sort=class_names, axis=alt.Axis(labelFontSize=20, titleFontSize=24)),
46+
)
47+
48+
# Heatmap cells
49+
heatmap = base.mark_rect(stroke="white", strokeWidth=2).encode(
50+
color=alt.Color(
51+
"Count:Q",
52+
scale=alt.Scale(scheme="blues"),
53+
legend=alt.Legend(title="Count", titleFontSize=18, labelFontSize=16, gradientLength=300, gradientThickness=25),
54+
)
55+
)
56+
57+
# Text annotations - white on dark cells, dark on light cells
58+
text = base.mark_text(fontSize=28, fontWeight="bold").encode(
59+
text="Count:Q", color=alt.condition(alt.datum.Count > 50, alt.value("white"), alt.value("#306998"))
60+
)
61+
62+
# Combine heatmap and text
63+
chart = (
64+
(heatmap + text)
65+
.properties(
66+
width=1000,
67+
height=1000,
68+
title=alt.Title("confusion-matrix · altair · pyplots.ai", fontSize=32, anchor="middle", offset=20),
69+
)
70+
.configure_view(strokeWidth=0)
71+
.configure_axis(domainWidth=0)
72+
)
73+
74+
# Save as PNG (1000 * 3.6 = 3600 for square format)
75+
chart.save("plot.png", scale_factor=3.6)
76+
77+
# Save interactive HTML version
78+
chart.save("plot.html")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library: altair
2+
specification_id: confusion-matrix
3+
created: '2025-12-26T17:37:13Z'
4+
updated: '2025-12-26T17:41:34Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20526592033
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 6.0.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/confusion-matrix/altair/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/confusion-matrix/altair/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/confusion-matrix/altair/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent text contrast logic using alt.condition to switch between white and
17+
dark text based on cell value
18+
- Clean declarative Altair style with proper encoding types (:N, :Q)
19+
- Well-structured long-form DataFrame transformation for Altair grammar
20+
- Good use of layered composition (heatmap + text)
21+
- Square aspect ratio appropriate for confusion matrix
22+
- White cell borders enhance visual clarity
23+
weaknesses:
24+
- Missing interactive tooltips which are a distinctive Altair feature
25+
- Legend positioning could be optimized to be closer to the plot

0 commit comments

Comments
 (0)