A negative result from balanced_accuracy_score(..., adjusted=True) is not automatically a software error. It means the unadjusted balanced accuracy is below the metric’s chance reference. The adjustment changes the scale: zero becomes chance-reference performance, while one remains perfect performance. It does not mean a negative number of predictions were correct.
This tutorial explains how to investigate a negative dashboard value using deliberately constructed labels. It is a metric-interpretation exercise, not a trained-model benchmark or evidence that a classifier is suitable for deployment.
Separate the three numbers before investigating
Ordinary accuracy divides correct predictions by the total number of examples. Unadjusted balanced accuracy instead averages recall across the actual classes. Each class gets equal influence on that average, even when class counts differ.
According to the official balanced_accuracy_score API, the default is adjusted=False. For that default, values range from zero to one. Setting adjusted=True rescales the result relative to chance rather than changing the predictions.
For an evaluation containing K actual classes, with K greater than one, the adjustment is (BA - 1/K) / (1 - 1/K), where BA is unadjusted balanced accuracy. In a binary task the reference is one half. In a three-class task it is one third. Do not label an adjusted score as ordinary accuracy in an exported report.
Reproduce below-chance cases without training a model
Run this complete block in a disposable Python environment containing scikit-learn. Each case includes every actual class, and predicted labels stay within that class set. The assertions make the expected relationship explicit rather than relying only on rounded console output.
from math import isclose
from sklearn.metrics import accuracy_score, balanced_accuracy_score
cases = {
"binary reversed": ([0, 0, 1, 1], [1, 1, 0, 0]),
"binary constant": ([0, 0, 0, 1], [0, 0, 0, 0]),
"three-class rotated": ([0, 1, 2], [1, 2, 0]),
"three-class perfect": ([0, 1, 2], [0, 1, 2]),
}
expected = {
"binary reversed": -1.0,
"binary constant": 0.0,
"three-class rotated": -0.5,
"three-class perfect": 1.0,
}
for name, (actual, predicted) in cases.items():
k = len(set(actual))
assert k > 1
assert set(predicted) <= set(actual)
raw = balanced_accuracy_score(actual, predicted)
adjusted = balanced_accuracy_score(actual, predicted, adjusted=True)
reference = 1 / k
assert isclose(adjusted, (raw - reference) / (1 - reference))
assert isclose(adjusted, expected[name], abs_tol=1e-12)
print(name, "accuracy=", round(accuracy_score(actual, predicted), 3),
"balanced=", round(raw, 3), "adjusted=", round(adjusted, 3))
The binary constant case gets three of four labels correct, but finds none of class one. Its balanced accuracy is one half and its adjusted result is zero. The fully reversed binary case has zero recall for both classes, giving an adjusted score of minus one. The rotated three-class example also gets every label wrong, but its adjusted minimum is minus one half rather than minus one. Those minima differ because the chance references differ.
Investigate the labels, not just the sign
First check row alignment: actual and predicted labels must describe the same observations in the same order. Then inspect label encodings. A reversed mapping between displayed class names and stored numeric codes can produce a convincing-looking but incorrect report.
Next review the confusion matrix and per-class recall. The model evaluation guide describes the adjusted range and the equal-class weighting behind balanced accuracy. A summary below chance does not identify the cause by itself. Small evaluation samples, label mistakes and genuinely poor predictions require different responses.
Do not automatically invert predictions to make the displayed score positive. That is not a general multiclass repair, and selecting a transformation using final test outcomes contaminates that evaluation. Investigate on development or validation data, then evaluate a fixed decision procedure independently.
Reporting checklist and common questions
- Record whether adjustment is enabled, along with the package version.
- Keep actual class counts, class names and confusion-matrix order.
- Report unadjusted and adjusted values in separately named columns.
- Investigate missing actual classes before comparing folds; do not assume every fold represents the same task.
- Keep data splits and scoring settings consistent between candidate models.
Is a zero adjusted score a failed calculation? Not necessarily. It is the chance-reference point on this scale, as the constant binary predictor demonstrates. A finite random sample need not land exactly at zero.
Does a positive score prove practical usefulness? No. It does not price false alarms, measure probability calibration or establish acceptable performance for every class. For a separate model-versus-reference experiment, use the DummyClassifier baseline workflow.
This tutorial is for educational use. Test carefully before production. The site owner is not responsible for misuse, damage, data loss or production issues.



