Data Science AUC-ROC Curve

The ROC curve plots a classifier's true positive rate against its false positive rate across every possible decision threshold, and the AUC score summarizes that curve as a single number describing how well the model separates the two classes.

Beyond a Single Accuracy Number

A classifier like logistic regression outputs a probability, and a threshold (commonly 0.5) turns that probability into a class label. But 0.5 is just one choice. Lowering the threshold catches more true positives at the cost of more false positives; raising it does the opposite. The ROC (Receiver Operating Characteristic) curve shows this entire trade-off at once, by plotting performance across every possible threshold rather than committing to just one.

True Positive Rate and False Positive Rate

The ROC curve has two axes. The y-axis is the true positive rate (TPR), also called recall or sensitivity: of all the actual positive cases, what fraction did the model correctly catch? The x-axis is the false positive rate (FPR): of all the actual negative cases, what fraction did the model incorrectly flag as positive? As you sweep the decision threshold from high to low, both TPR and FPR change, tracing out the curve from the bottom-left corner (0,0) to the top-right corner (1,1).

  • TPR = True Positives / (True Positives + False Negatives) — how many real positives were caught.
  • FPR = False Positives / (False Positives + True Negatives) — how many real negatives were wrongly flagged.
  • A high threshold makes the model cautious: low FPR, but also lower TPR.
  • A low threshold makes the model liberal: high TPR, but also higher FPR.

Example: Plotting an ROC curve

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, roc_auc_score

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=1)

model = LogisticRegression(max_iter=5000)
model.fit(X_train, y_train)

# Probability of the positive class, needed for the curve
y_scores = model.predict_proba(X_test)[:, 1]

fpr, tpr, thresholds = roc_curve(y_test, y_scores)
auc = roc_auc_score(y_test, y_scores)

print('AUC:', round(auc, 3))
# fpr and tpr are arrays you can plot: plt.plot(fpr, tpr)
Note: AUC stands for Area Under the (ROC) Curve. It condenses the whole curve into one number between 0 and 1, representing the probability that the model ranks a randomly chosen positive example higher than a randomly chosen negative example.

Interpreting the AUC Score

An AUC of 1.0 means perfect separation: for every possible threshold, the model correctly ranks all positives above all negatives, with no overlap at all. An AUC of 0.5 means the model performs no better than random guessing, equivalent to the diagonal line from (0,0) to (1,1) on the ROC plot. In practice, most useful models land somewhere between these extremes, and higher AUC always means better ranking ability regardless of which threshold you eventually choose to deploy.

AUC rangeInterpretation
1.0Perfect separation between classes
0.9 - 1.0Excellent discrimination
0.7 - 0.9Good, generally useful discrimination
0.5 - 0.7Weak, only slightly better than chance
0.5No better than random guessing
Below 0.5Worse than random (predictions may be inverted)
Note: AUC-ROC is especially useful for comparing models independent of a specific threshold choice, and it's more robust than plain accuracy when classes are imbalanced, since it evaluates ranking quality across all thresholds rather than one fixed cutoff.