Skip to content

Fix ValueError in macro_averaged_mean_absolute_error for missing classes (#1094)#1168

Open
prodduturisindhurdy wants to merge 1 commit into
scikit-learn-contrib:masterfrom
prodduturisindhurdy:fix-mae-crash-1094
Open

Fix ValueError in macro_averaged_mean_absolute_error for missing classes (#1094)#1168
prodduturisindhurdy wants to merge 1 commit into
scikit-learn-contrib:masterfrom
prodduturisindhurdy:fix-mae-crash-1094

Conversation

@prodduturisindhurdy

Copy link
Copy Markdown

Problem
The function macro_averaged_mean_absolute_error currently crashes with a ValueError (Found array with 0 samples) when a label exists in the predictions (y_pred) or the global labels set, but is entirely absent from the ground truth (y_true). This happens because the function attempts to calculate mean_absolute_error on an empty slice of data.

Solution
This PR introduces a defensive check within the label iteration loop:

Skip Empty Classes: If a class has zero occurrences in y_true, the loop now continues to the next label instead of attempting a calculation on empty arrays.

Robust Return: Changed the final return to use np.mean(mae) with a fallback to 0.0. This prevents a potential ZeroDivisionError if all classes were somehow skipped.

Changes
Modified imblearn/metrics/_classification.py to add the if len(indices) == 0: continue safety guard.

Added a new regression test test_macro_averaged_mean_absolute_error_missing_class in imblearn/metrics/tests/test_classification.py to prevent future regressions.

Verification
Manual Test: Verified that the reproduction script provided in #1094 now returns 0.5 instead of crashing.

Automated Tests: Ran pytest imblearn/metrics/tests/test_classification.py and all 52 tests passed.

Fixes #1094

@immu4989

immu4989 commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for the fix — the diagnosis is right and the regression test is a good addition. I reviewed it next to the alternative PR #1172, which targets the same issue, and wanted to share a couple of observations.

Approach. This PR keeps unique_labels(y_true, y_pred) and skips empty classes inside the loop:

if len(indices) == 0:
    continue
...
return np.mean(mae) if mae else 0.0

That's functionally correct, but it handles the symptom rather than the cause: the loop still enumerates y_pred-only classes just to skip them. #1172 instead computes unique_labels(y_true) up front, so those classes are never iterated and the averaging logic stays untouched. Both give the same result on #1094's examples (0.5), but the up-front-filter version is a bit simpler to reason about.

Two small things worth tidying if this is the one that lands:

  1. The else 0.0 fallback for an empty mae list isn't really reachable — if y_true is non-empty there's always at least one ground-truth class. It's harmless but slightly misleading (a MAE of 0.0 would read as "perfect" rather than "undefined"). Filtering on y_true avoids needing the fallback at all.
  2. The patch leaves no newline at end of file (\ No newline at end of file in the diff) and adds the test function without a blank-line separator — pre-commit/flake8 may flag both.

Also worth adding a changelog entry under doc/whats_new/v0.15.rst (#1172 includes one).

Either PR resolves the bug; my mild preference is the unique_labels(y_true) approach in #1172 for simplicity, but if you'd rather carry this one forward, addressing the two nits above would get it there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] macro_averaged_mean_absolute_error() raises ValueError

2 participants