Skip to content
induwara.lk
Premium
induwara.lkAI · Machine learning

ROC Curve & AUC Calculator

Paste your binary labels and predicted scores to get the ROC curve, the AUC, the Gini coefficient, and the Youden-optimal threshold — every value shown with its formula and cross-checked two ways. It matches scikit-learn's roc_auc_score, runs entirely in your browser, and needs no signup.

By Induwara AshinsanaUpdated Sep 3, 2026
ROC curve & AUC calculator

Label is the true class (0 or 1); score is the model's predicted probability or any real-valued output. Separate the two with a space, tab, or comma. Needs at least one positive and one negative row.

Positive class

Which label counts as the positive (event) class.

Score direction

Whether a larger or a smaller score indicates the positive class.

Presets
AUC
0.8056
Area under the ROC curve (0–1)
Gini coefficient
0.6111
2·AUC − 1
Optimal threshold
0.82
max Youden's J = 0.5000
Samples (P / N)
6 / 6
12 total

AUC 0.8056 Excellent discrimination. An AUC of 0.8056 means a randomly chosen positive sample outranks a randomly chosen negative one about 80.56% of the time (ties counted as half).

Decimals

ROC curve

False positive rateTrue positive rate0101
ModelNo-skill (AUC 0.5) Optimal threshold

Formulas

  • TPR = TP / P, FPR = FP / N
  • AUC = Σ (FPRᵢ − FPRᵢ₋₁)·(TPRᵢ + TPRᵢ₋₁)/2
  • AUC = (R⁺ − P(P+1)/2) / (P·N)  (Mann–Whitney)
  • Gini = 2·AUC − 1
  • Youden's J = TPR − FPR

Cross-check. The trapezoidal rule gives AUC = 0.8056; the independent Mann–Whitney rank form (R⁺ = 50 over P = 6, N = 6) gives 0.8056. They reconcile, as they must — the result is verified.

Recommended operating point. Threshold 0.82 maximises Youden's J at 0.5000 — TPR 0.5000, FPR 0.0000 (TP 3, FP 0, TN 6, FN 3).

Threshold table

ThresholdTPFPTNFNTPRFPRYouden's J
0.9510650.16670.00000.1667
0.8820640.33330.00000.3333
0.82optimal30630.50000.00000.5000
0.7831530.50000.16670.3333
0.6541520.66670.16670.5000
0.642420.66670.33330.3333
0.5552410.83330.33330.5000
0.4853310.83330.50000.3333
0.454210.83330.66670.1667
0.3564201.00000.66670.3333
0.2865101.00000.83330.1667
0.2266001.00001.00000.0000

Method: TPR = TP/P, FPR = FP/N swept across every distinct score; AUC = Σ (ΔFPR)(TPR + TPRₚᵣₑᵥ)/2 (trapezoidal, scikit-learn roc_auc_score), cross-checked against the Mann–Whitney rank form (Hanley & McNeil 1982). Sources cited below the calculator. No data leaves this page.

How it works

The ROC curveplots a classifier's true positive rate against its false positive rate as the decision threshold moves, and the AUCis the single number underneath it. The method here follows scikit-learn's roc_curve / roc_auc_score and the definitions in Fawcett's 2006 ROC primer.

With P positive and N negative samples, each threshold yields a confusion matrix and a point:

TPR = TP / P   FPR = FP / N

  1. Sortthe samples by predicted score, descending. If you pick “lower score = positive”, the scores are negated first so the sweep can always treat a higher value as more positive.
  2. Sweep the threshold through every distinct score. Classify a sample positive when its score meets the threshold, and record the running TPR and FPR — one ROC point per distinct score, with (0,0) prepended and (1,1) appended.
  3. Integrate the area under those points with the trapezoidal rule:

    AUC = Σ (FPRᵢ − FPRᵢ₋₁)·(TPRᵢ + TPRᵢ₋₁)/2

  4. Cross-checkwith the Mann–Whitney / Wilcoxon rank form (Hanley & McNeil 1982). Rank every score ascending — averaging ranks for ties — and with R⁺ the sum of the positive ranks:

    AUC = (R⁺ − P(P+1)/2) / (P·N)

    This equals the probability that a random positive outranks a random negative (ties counted as half) and must match the trapezoidal value — the tool asserts the two agree to floating-point precision.

From the AUC the tool reports the Gini coefficient = 2·AUC − 1 and a plain-English discrimination band. For each threshold it also computes Youden's J = TPR − FPR; the threshold with the highest J — the point on the curve furthest from the no-skill diagonal — is flagged as a common operating-point choice. Inputs with only one class are rejected, because the AUC is then undefined.

What the AUC actually measures

The rank form is the most useful way to read the number: an AUC of 0.82 says that if you draw one positive and one negative sample at random, the model gives the positive the higher score 82% of the time (ties counted as half). That framing makes two properties obvious. First, the AUC only cares about ordering, not about the score values — multiply every score by 10, or push them all through a monotone squashing function, and the AUC does not move by a thousandth. Second, it is a whole-curve summary: two models can share an AUC of 0.80 while one is far better in the low-FPR corner where you actually plan to operate, so read the curve, not only the area.

The flip side is what the AUC hides. It says nothing about calibration — a model whose probabilities are all squeezed between 0.48 and 0.52 can still score 0.99 if the ordering is right, which is why probability outputs deserve a separate check (the logprob → probability calculator is handy when you are working from raw model logits). It also says nothing about the threshold you will ship. Once you have picked one, the per-threshold counts belong in a confusion matrix calculator so you can read precision, recall and specificity at that exact cut-off.

Edge cases this tool handles explicitly

  • Only one class present. If every label is 1 (or every label is 0) then P·N = 0, the denominator of both formulas vanishes and the AUC is undefined. scikit-learn raises a ValueError here; this tool refuses the input with the same reasoning rather than printing a misleading 0.5.
  • Every score tied. The sweep produces a single threshold, the curve becomes the diagonal, and both formulas return exactly 0.5 — the no-skill baseline. See the third worked example below for the arithmetic.
  • AUC below 0.5.This is not a broken model so much as a reversed one. If the AUC comes out at 0.23, switching the “lower score = positive” direction gives 1 − 0.23 = 0.77. The usual causes are a positive-class label mix-up or a distance metric being fed where a similarity was expected.
  • Tiny samples. With four or five rows the AUC can only take a handful of discrete values (with P = N = 2 it is one of 0, 0.25, 0.5, 0.75, 1) and its confidence interval is enormous. Treat single-digit-sample AUCs as illustrations, not evidence.
  • Duplicate rows and near-ties. Floating-point scores that differ in the fifteenth decimal are not ties and will produce a visible extra ROC step. Round your scores before pasting if you intend them to tie.

Worked examples

Four samples, no ties — AUC 0.75 (the Textbook preset)

  1. Data (label, score): (1, 0.9) (0, 0.6) (1, 0.4) (0, 0.2). P = 2, N = 2
  2. t = 0.9: TP 1, FP 0 → (FPR 0.0, TPR 0.5)
  3. t = 0.6: TP 1, FP 1 → (0.5, 0.5)
  4. t = 0.4: TP 2, FP 1 → (0.5, 1.0)
  5. t = 0.2: TP 2, FP 2 → (1.0, 1.0)
  6. Trapezoid: 0.25 + 0.50 = 0.75; Gini = 2(0.75) − 1 = 0.5
  7. Mann–Whitney: ranks 0.2→1, 0.4→2, 0.6→3, 0.9→4; R⁺ = 4 + 2 = 6
  8. AUC = (6 − 2·3/2) / (2·2) = 3/4 = 0.75 ✓ (both forms agree)

Tied scores at a threshold — AUC 0.875 (average-rank handling)

  1. Data: (1, 0.8) (1, 0.5) (0, 0.5) (0, 0.2). P = 2, N = 2
  2. t = 0.8: TP 1, FP 0 → (0.0, 0.5)
  3. t = 0.5: a tied 1 and 0 enter together → TP 2, FP 1 → (0.5, 1.0)
  4. t = 0.2: TP 2, FP 2 → (1.0, 1.0)
  5. Trapezoid: 0.375 + 0.50 = 0.875
  6. Mann–Whitney: the two 0.5 share ranks 2 and 3 → average 2.5
  7. R⁺ = 4 (for 0.8) + 2.5 (for the positive 0.5) = 6.5
  8. AUC = (6.5 − 3) / 4 = 0.875 ✓ — ties keep both forms identical

No skill — AUC 0.5 (all scores tied)

  1. Data: (1, 0.5) (0, 0.5) (1, 0.5) (0, 0.5). P = 2, N = 2
  2. One distinct threshold (0.5): every sample is predicted positive
  3. TP 2, FP 2 → the curve runs straight from (0,0) to (1,1)
  4. Trapezoid area of the diagonal = 0.5; Gini = 0
  5. Mann–Whitney: all four ranks average to 2.5; R⁺ = 5
  6. AUC = (5 − 3) / 4 = 0.5 ✓ — exactly the random-guess baseline

Edge case: an inverted score — AUC 0.25 becomes 0.75

  1. Data: (1, 0.2) (0, 0.4) (1, 0.6) (0, 0.9). P = 2, N = 2
  2. Higher = positive: t=0.9 → (0.5, 0.0); t=0.6 → (0.5, 0.5)
  3. t = 0.4: (1.0, 0.5); t = 0.2: (1.0, 1.0)
  4. Trapezoid: 0 + 0.25 = 0.25 — worse than a coin flip
  5. Mann–Whitney: R⁺ = 1 (for 0.2) + 3 (for 0.6) = 4
  6. AUC = (4 − 3) / 4 = 0.25 ✓ — the two forms still agree
  7. Diagnosis: the score ranks negatives above positives, so it is inverted
  8. Switch direction to 'lower score = positive' → AUC = 1 − 0.25 = 0.75
  9. Same data, same ranking information — only the sign convention changed

Imbalanced set — a high AUC with poor precision

  1. Screening set: P = 4 positives, N = 96 negatives (4% prevalence)
  2. At the Youden-optimal threshold: TP 3, FN 1, FP 10, TN 86
  3. TPR = 3/4 = 0.75; FPR = 10/96 = 0.104 → J = 0.646
  4. The ROC point (0.104, 0.75) sits well above the diagonal
  5. But precision = TP/(TP+FP) = 3/13 = 0.231
  6. So ~77% of everything flagged is a false alarm, at AUC ≈ 0.9
  7. Lesson: FPR divides by 96 negatives, precision divides by 13 flags
  8. Always pair the AUC with precision at the threshold you will ship

Turning the curve into a threshold

The AUC is a model-selection number. Shipping a classifier needs one more decision: the cut-off at which a score becomes a positive prediction. The threshold table under the chart exists for exactly that step, and the row flagged as Youden-optimal is a default, not an answer.

Youden's J maximises TPR − FPR, which quietly assumes a false positive and a false negative cost the same and that the two classes are balanced. Both assumptions are usually wrong. When the costs differ, pick the threshold that minimises expected cost instead:

cost(t) = c_FN · P · (1 − TPR(t)) + c_FP · N · FPR(t)

Read TPR(t) and FPR(t)straight out of the threshold table, plug in your own two costs, and take the row with the smallest total. A loan-default model where a missed default costs fifty times a wasted review call will land on a much lower threshold than Youden's J suggests. A spam filter, where a false positive buries someone's payslip email, moves the other way.

Three constraints show up often enough to be worth naming. A capacity constraint fixes how many positives you can act on — if the review team handles 50 cases a day, sort by score and cut at the 50th, then read the resulting TPR off the table. A sensitivity floor is the medical-screening pattern: fix TPR at, say, 0.95 and accept whatever FPR that costs. A precision floor is the alerting pattern: nobody trusts a pager that cries wolf, so hold precision above a set level and take the recall you can get.

Once a threshold is chosen, stop reporting the AUC alone. The single most useful follow-up is the confusion matrix at that cut-off, from which the F1 score calculator gives the precision–recall balance, and the Matthews correlation coefficient calculator gives a single balanced score that, unlike accuracy, does not collapse when 96% of the data is one class. For ranking and retrieval work — search results, recommendations, RAG candidate lists — the ROC is the wrong instrument entirely, and precision@K and recall@K describe what users actually see.

Finally, treat one AUC number as an estimate with error bars. Hanley and McNeil give a standard error that widens sharply as the sample shrinks, so two models scoring 0.81 and 0.83 on a 200-row validation set are not meaningfully different. Cross-validate, report the spread across folds, and re-check the curve on data from the period you plan to deploy in — score distributions drift, and a threshold tuned on last quarter's data can silently move to a different point on the curve.

Frequently asked questions

Sources & references

The formulas on this page were last cross-checked against these sources on 2026-06-10. ROC and AUC are stable mathematical definitions, so this tool needs no rate or schedule updates — only the worked examples are periodically re-reconciled against scikit-learn.

Related tools

Rate this tool
Be the first to rate

Comments & feedback

Spotted a bug or want an improvement? Tell us — our team reviews every comment, and good ideas get built. Comments are public and anonymous.

Found a bug, edge case, or want to suggest an improvement?

Email me at [email protected] — most fixes ship within 24 hours.