Skip to content

Commit daf68ce

Browse files
feat(seaborn): implement roc-curve (#2305)
## Implementation: `roc-curve` - seaborn Implements the **seaborn** version of `roc-curve`. **File:** `plots/roc-curve/implementations/seaborn.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20526593384)* --------- 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 99c5d76 commit daf68ce

2 files changed

Lines changed: 143 additions & 0 deletions

File tree

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
""" pyplots.ai
2+
roc-curve: ROC Curve with AUC
3+
Library: seaborn 0.13.2 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-26
5+
"""
6+
7+
import matplotlib.pyplot as plt
8+
import numpy as np
9+
import seaborn as sns
10+
11+
12+
# Set random seed for reproducibility
13+
np.random.seed(42)
14+
15+
# Generate synthetic classification data for three models with different performance levels
16+
n_samples = 1000
17+
18+
# True labels (binary)
19+
y_true = np.concatenate([np.zeros(500), np.ones(500)])
20+
21+
# Model 1: Good classifier (AUC ~0.92)
22+
y_scores_model1 = np.concatenate(
23+
[
24+
np.random.beta(2, 5, 500), # Low scores for class 0
25+
np.random.beta(5, 2, 500), # High scores for class 1
26+
]
27+
)
28+
29+
# Model 2: Moderate classifier (AUC ~0.78)
30+
y_scores_model2 = np.concatenate([np.random.beta(2, 3, 500), np.random.beta(3, 2, 500)])
31+
32+
# Model 3: Weak classifier (AUC ~0.65)
33+
y_scores_model3 = np.concatenate([np.random.beta(2, 2.5, 500), np.random.beta(2.5, 2, 500)])
34+
35+
# Compute ROC curves manually at various thresholds
36+
n_thresholds = 200
37+
thresholds = np.linspace(0, 1, n_thresholds)
38+
39+
# Model 1 ROC
40+
tpr1, fpr1 = [], []
41+
for thresh in thresholds:
42+
y_pred = (y_scores_model1 >= thresh).astype(int)
43+
tp = np.sum((y_pred == 1) & (y_true == 1))
44+
fp = np.sum((y_pred == 1) & (y_true == 0))
45+
fn = np.sum((y_pred == 0) & (y_true == 1))
46+
tn = np.sum((y_pred == 0) & (y_true == 0))
47+
tpr1.append(tp / (tp + fn) if (tp + fn) > 0 else 0)
48+
fpr1.append(fp / (fp + tn) if (fp + tn) > 0 else 0)
49+
fpr1, tpr1 = np.array(fpr1), np.array(tpr1)
50+
51+
# Model 2 ROC
52+
tpr2, fpr2 = [], []
53+
for thresh in thresholds:
54+
y_pred = (y_scores_model2 >= thresh).astype(int)
55+
tp = np.sum((y_pred == 1) & (y_true == 1))
56+
fp = np.sum((y_pred == 1) & (y_true == 0))
57+
fn = np.sum((y_pred == 0) & (y_true == 1))
58+
tn = np.sum((y_pred == 0) & (y_true == 0))
59+
tpr2.append(tp / (tp + fn) if (tp + fn) > 0 else 0)
60+
fpr2.append(fp / (fp + tn) if (fp + tn) > 0 else 0)
61+
fpr2, tpr2 = np.array(fpr2), np.array(tpr2)
62+
63+
# Model 3 ROC
64+
tpr3, fpr3 = [], []
65+
for thresh in thresholds:
66+
y_pred = (y_scores_model3 >= thresh).astype(int)
67+
tp = np.sum((y_pred == 1) & (y_true == 1))
68+
fp = np.sum((y_pred == 1) & (y_true == 0))
69+
fn = np.sum((y_pred == 0) & (y_true == 1))
70+
tn = np.sum((y_pred == 0) & (y_true == 0))
71+
tpr3.append(tp / (tp + fn) if (tp + fn) > 0 else 0)
72+
fpr3.append(fp / (fp + tn) if (fp + tn) > 0 else 0)
73+
fpr3, tpr3 = np.array(fpr3), np.array(tpr3)
74+
75+
# Calculate AUC scores using trapezoidal rule
76+
idx1 = np.argsort(fpr1)
77+
auc1 = np.trapezoid(tpr1[idx1], fpr1[idx1])
78+
idx2 = np.argsort(fpr2)
79+
auc2 = np.trapezoid(tpr2[idx2], fpr2[idx2])
80+
idx3 = np.argsort(fpr3)
81+
auc3 = np.trapezoid(tpr3[idx3], fpr3[idx3])
82+
83+
# Set seaborn style
84+
sns.set_style("whitegrid")
85+
sns.set_context("talk", font_scale=1.2)
86+
87+
# Create figure
88+
fig, ax = plt.subplots(figsize=(16, 9))
89+
90+
# Plot ROC curves using seaborn lineplot
91+
sns.lineplot(x=fpr1, y=tpr1, ax=ax, linewidth=3.5, color="#306998", label=f"Random Forest (AUC = {auc1:.3f})")
92+
sns.lineplot(x=fpr2, y=tpr2, ax=ax, linewidth=3.5, color="#FFD43B", label=f"Logistic Regression (AUC = {auc2:.3f})")
93+
sns.lineplot(x=fpr3, y=tpr3, ax=ax, linewidth=3.5, color="#E74C3C", label=f"Decision Tree (AUC = {auc3:.3f})")
94+
95+
# Diagonal reference line (random classifier)
96+
ax.plot([0, 1], [0, 1], linestyle="--", linewidth=2.5, color="#7F8C8D", label="Random Classifier (AUC = 0.500)")
97+
98+
# Styling
99+
ax.set_xlabel("False Positive Rate (FPR)", fontsize=22)
100+
ax.set_ylabel("True Positive Rate (TPR)", fontsize=22)
101+
ax.set_title("roc-curve · seaborn · pyplots.ai", fontsize=26, fontweight="bold")
102+
ax.tick_params(axis="both", labelsize=18)
103+
104+
# Set axis limits and aspect
105+
ax.set_xlim([-0.02, 1.02])
106+
ax.set_ylim([-0.02, 1.02])
107+
ax.set_aspect("equal", adjustable="box")
108+
109+
# Legend
110+
ax.legend(loc="lower right", fontsize=18, framealpha=0.95)
111+
112+
# Grid styling
113+
ax.grid(True, alpha=0.3, linestyle="--")
114+
115+
plt.tight_layout()
116+
plt.savefig("plot.png", dpi=300, bbox_inches="tight")
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library: seaborn
2+
specification_id: roc-curve
3+
created: '2025-12-26T17:38:39Z'
4+
updated: '2025-12-26T17:47:35Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20526593384
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 0.13.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/roc-curve/seaborn/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/roc-curve/seaborn/plot_thumb.png
12+
preview_html: null
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent visual clarity with properly sized text and well-contrasted line colors
17+
- Complete spec compliance including diagonal reference line, AUC annotations, and
18+
correct title format
19+
- Realistic machine learning context comparing three classifiers with believable
20+
performance differences
21+
- Clean, readable code structure following KISS principles
22+
- Equal aspect ratio makes the curve shapes easy to interpret correctly
23+
weaknesses:
24+
- Uses seaborn minimally - only lineplot and styling, which does not showcase seaborn
25+
distinctive features
26+
- Yellow/gold color for Logistic Regression could be slightly harder to distinguish
27+
for colorblind viewers

0 commit comments

Comments
 (0)