How to compare ROC AUC scores of different binary classifiers and assess statistical significance in Python? p-value, confidence interval
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Comparing ROC AUC values is not just a matter of subtracting two scores. To decide whether classifier A is really better than classifier B, you need paired predictions on the same test set and a statistical method for the AUC difference, such as DeLong's test, a paired bootstrap confidence interval, or a permutation test.
First compute paired AUCs on the same test set
The most important design rule is that both models must be evaluated on the same observations. If model A and model B are scored on different test sets, a paired AUC comparison is no longer valid.
Here is a simple starting point:
That tells you the point estimate, but not the uncertainty around the difference.
Bootstrap the AUC difference for a confidence interval
A bootstrap is easy to implement and works well in practice. The idea is to resample the test rows with replacement, recompute both AUCs on each bootstrap sample, and store the difference.
If the confidence interval for auc_a - auc_b excludes 0, that is evidence that the models differ on the tested data. This bootstrap p-value is an approximation, but it is often good enough when you want a practical answer in Python without implementing DeLong from scratch.
Use a permutation test for a paired p-value
A permutation test is another good option. Under the null hypothesis that the models are equally good, you can randomly swap the two model scores for each observation and see how often the resulting AUC difference is at least as extreme as the observed one.
This gives a paired p-value without assuming normality. It is especially attractive when you want a method you can explain clearly and reproduce from basic building blocks.
Where DeLong fits in
DeLong's test is a classic method for comparing correlated ROC AUCs, and it is often the most efficient analytical approach when both models are evaluated on the same cases. The practical issue is that scikit-learn does not ship a DeLong implementation directly, so many teams either use a third-party implementation or rely on bootstrap and permutation procedures instead.
That means the engineering choice is often:
- Bootstrap for confidence intervals.
- Permutation test for a robust paired p-value.
- DeLong if you need the canonical analytical test and are comfortable adding another implementation.
Common Pitfalls
The biggest mistake is comparing AUCs from different test sets as if the scores were paired. Most significance procedures for model comparison assume the same observations were scored by both models.
Another common issue is using hard class predictions instead of probability-like scores. ROC AUC should be computed from continuous decision scores or probabilities, not from already-thresholded labels.
Developers also over-interpret tiny p-values on very large test sets. Statistical significance does not automatically mean the AUC gap is practically meaningful.
Finally, do not ignore class imbalance and data leakage. A beautifully computed p-value does not rescue a flawed evaluation design.
Summary
- Compare ROC AUCs on the same test cases so the comparison is paired.
- Report the point estimate of the AUC difference and an uncertainty measure.
- A bootstrap gives a practical confidence interval for
AUC_A - AUC_B. - A permutation test gives a clean paired p-value with minimal assumptions.
- DeLong is the classic analytical test, but bootstrap and permutation methods are often easier to implement in Python.

