Anime ninja owl presenting ROC AUC vs PR AUC, with pink-accented conceptual data displays.

ROC AUC vs PR AUC: Imbalanced Data Explained with Python

Hey friends! Your model has a reassuring ROC AUC, but the alerts are full of false alarms. How can both things be true? ROC AUC measures how well scores rank positives above negatives; a precision-recall curve shows how many positive predictions are correct as recall changes. When positives are rare, that difference matters. Let’s work through it together.

We’ll keep this practical: one controlled example, two populations, and Python you can run yourself. If you’d like a refresher first, start with ROC curves and precision versus recall.

ROC AUC vs PR AUC: The Main Differences

MeasureWhat it summarisesReference point
ROC AUCTrue positive rate versus false positive rate across thresholdsChance ranking: 0.5
Precision-recall curvePrecision versus recall across thresholdsNo-skill precision reference: positive-class prevalence
Average precision (AP)Precision weighted by increases in recallReport prevalence alongside the score
Trapezoidal PR AUCArea from straight lines between PR operating pointsCan differ substantially from AP

ROC AUC gives half credit to tied positive-negative score pairs. Neither a ranking metric nor a precision-recall summary tells you whether a probability such as 0.8 is well calibrated. “PR AUC” is also an ambiguous label: specify whether you calculated average precision or trapezoidal area.

Same ROC Curve, Very Different Alert Quality

Consider a synthetic classifier with only two score levels. At a threshold between them it catches 8 of 10 positives and flags 10% of negatives. We keep those within-class proportions fixed while changing how common the positive class is. This isolates prevalence; it is not a claim about a trained model on real data.

At the chosen threshold10% positives1% positives
Actual positives / negatives10 / 9010 / 990
True positives / false positives8 / 98 / 99
Recall80%80%
False positive rate10%10%
Precision8/17 = 47.1%8/107 = 7.5%

Here’s the part I want you to notice. Both ROC curves pass through FPR = 0.10 and TPR = 0.80. With only the endpoints as the other available points, both have ROC AUC 0.85. Yet in the rarer-positive population, only about one in thirteen alerts is correct. The ROC result is valid; we need another view to understand what those alerts will mean for the people reviewing them.

Both populations have ROC AUC 0.85. Precision at the chosen threshold falls from 47.1% to 7.5% as positive prevalence falls from 10% to 1%.
Same ROC curve, different alert quality: the synthetic example holds recall and false-positive rate fixed while changing class prevalence.

Why Class Prevalence Changes Precision

Let π be the fraction of positive cases. At a fixed operating point: precision = (TPR × π) / [(TPR × π) + FPR × (1 − π)]. This follows by expressing true positives and false positives as fractions of the population.

With TPR = 0.8, FPR = 0.1 and π = 0.01, precision is 0.008 / 0.107 ≈ 7.5%. Lower prevalence reduces precision when the within-class score distributions stay fixed. Under that same condition the ROC curve stays fixed. In real deployment, score distributions may change too, so neither result should be assumed stable.

Illustration of the 90-day machine learning roadmap PDF

Stay on track. Get the Roadmap.

Download the 90-Day Roadmap PDF + get weekly tips and resources to help you succeed

Python Example: ROC AUC, Average Precision and PR Area

The following code reproduces both populations. It requires NumPy and scikit-learn. The score values are only ranking scores; they were constructed for the example and should not be interpreted as estimated probabilities.

Try it yourself: the rare-positive detective lab

Ready to investigate, friends? Follow seven guided steps, explore four charts, and try a few mini-challenges. Change how common the positive class is, count the false alarms, and discover why average precision and trapezoidal PR AUC give different answers.

Run the cells from top to bottom, then change the experiment settings and predict the results before running them again. The notebook creates its own synthetic data. You can also view the notebook and saved charts on GitHub.

import numpy as np
from sklearn.metrics import (
    roc_auc_score, average_precision_score,
    precision_recall_curve, auc
)

# Synthetic scores, not calibrated probabilities.
# Preserve the score distribution within each class.
for n_negative in [90, 990]:
    y_true = np.r_[np.ones(10), np.zeros(n_negative)]
    scores = np.r_[
        np.full(8, 0.9), np.full(2, 0.1),
        np.full(n_negative // 10, 0.9),
        np.full(n_negative - n_negative // 10, 0.1)
    ]

    precision, recall, _ = precision_recall_curve(y_true, scores)
    roc_auc = roc_auc_score(y_true, scores)
    ap = average_precision_score(y_true, scores)
    pr_auc = auc(recall, precision)

    print(
        f"Prevalence={y_true.mean():.2%} "
        f"ROC_AUC={roc_auc:.3f} AP={ap:.3f} "
        f"trapezoidal_PR_AUC={pr_auc:.3f}"
    )

Output from running this example:

Prevalence=10.00% ROC_AUC=0.850 AP=0.396 trapezoidal_PR_AUC=0.645
Prevalence=1.00% ROC_AUC=0.850 AP=0.062 trapezoidal_PR_AUC=0.438

Average Precision Is Not Trapezoidal PR AUC

Average precision weights the precision at each operating point by the increase in recall. Trapezoidal integration instead joins neighbouring points with straight lines. Those are different summaries, particularly when the curve has few points or many tied scores.

Our deliberately coarse, two-score example makes the difference visible. At 1% prevalence, AP is 0.062 while the trapezoidal area is 0.438. Do not compare these as if they were the same metric or select whichever number looks better. Name the calculation in your report. The scikit-learn precision-recall guide explains both conventions.

Which Metric Should You Report?

For rare-positive detection: report a PR curve and clearly labelled AP or PR area, the positive rate, and precision and recall at the threshold you intend to use. Include the underlying counts so the review workload is visible.

For ranking across classes: ROC AUC remains useful, but inspect the relevant false-positive-rate region. If investigators can review only a fixed number of cases, also examine lift at that review budget.

For decisions based on probabilities: add a probability score such as the Brier score and a calibration assessment. Good ranking does not establish good calibration.

Common Evaluation Mistakes

Comparing AP across different class balances. A change in AP can reflect prevalence rather than a change in the ranking mechanism. Use the same evaluation population to compare candidates, and describe differences between datasets.

Balancing the test set without considering deployment. Oversampling may be a training technique, but an artificially balanced test set does not directly estimate precision under a rare-positive deployment population. Keep evaluation representative or explicitly account for the sampling design.

Selecting thresholds on the final test set. Choose the operating threshold using validation data, then evaluate once on untouched test data. With time-ordered or grouped observations, first review cross-validation and leakage.

Calling AP “accuracy.” It is a precision-recall summary, not the fraction of correct predictions. There is no universal AP cutoff that makes a classifier ready for deployment.

What to Do Next

And that’s the takeaway, friends: before celebrating a score, ask how many useful cases your team will find and how many false alarms it must handle. Use the model-evaluation guide for the wider workflow, then try choosing a decision threshold for the action you actually want to take.

Sources

Google: ROC and AUC; scikit-learn: average_precision_score; Saito and Rehmsmeier (2015): precision-recall plots on imbalanced datasets. The two-population numerical example above is constructed for this tutorial.

Subscribe to our awesome newsletter to get the best content on your journey to learn Machine Learning, including some exclusive free goodies!

HOW IS MACHINE LEARNING