[MRG] FIX macro_averaged_mean_absolute_error: skip classes absent from y_true (#1094)#1172
[MRG] FIX macro_averaged_mean_absolute_error: skip classes absent from y_true (#1094)#1172jbbqqf wants to merge 2 commits into
Conversation
…ue (scikit-learn-contrib#1094) When a class appeared only in y_pred (predicted but not present in ground truth), the per-class loop dispatched mean_absolute_error on an empty ground-truth slice, which sklearn rejects with "Found array with 0 sample(s)". The macro average is now computed only over classes present in y_true, mirroring the docstring intent ("computes each MAE for each class") and aligning the behaviour with sklearn's f1_score(average='macro') on labels missing from y_true. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Looking at the code, it is not the right fix. We should add |
|
I reviewed this alongside the alternative PR #1168 for the same issue — this is the cleaner of the two and I'd lean towards it. The fix ( Why I prefer this over #1168: that PR keeps Checks:
LGTM. One optional nit: a brief inline note that |
Reference Issue
Fixes #1094
What does this implement/fix? Explain your changes.
macro_averaged_mean_absolute_error(y_true, y_pred)raisedValueError: Found array with 0 sample(s)whenever a class appeared iny_predbut was absent fromy_true. The implementation iterated overunique_labels(y_true, y_pred)and calledmean_absolute_erroron theslice
y_true == c; when no ground-truth samples carried labelc,the slice was empty and
sklearn'smean_absolute_errorraised onzero-length input.
The fix iterates over
unique_labels(y_true)only — per-class MAE isundefined for a class with zero ground-truth samples, so it is excluded
from the macro average. This matches the docstring intent ("computes
each MAE for each class") and aligns with the graceful handling of
absent labels in
sklearn.metrics.f1_score(average='macro').Reproduce BEFORE/AFTER yourself (copy-paste)
A reviewer can verify this fix end-to-end by pasting the block below.
What I ran locally
pytest imblearn/metrics/ -v→ 210 passed (208 existing + 2 newparametrised assertions in
test_macro_averaged_mean_absolute_error_class_only_in_y_pred).test_macro_averaged_mean_absolute_error_class_only_in_y_pred)fails on
origin/masterwith the exactValueErrorfrom the bugreport and passes on this branch.
(
test_macro_averaged_mean_absolute_error,..._sample_weight)still pass — the change only affects the degenerate "class missing
from
y_true" case.Edge cases tested
y_pred, single ground-truth class[0,0], [0,1]0.5(single-class macro = MAE on full slice)test_macro_averaged_mean_absolute_error_class_only_in_y_predy_true[0,1], [0,0]0.5(per-class MAE =[0, 1], mean =0.5)test_macro_averaged_mean_absolute_error_class_only_in_y_pred[1,1,1,2,2,2], [1,2,1,2,1,2]0.333(unchanged)test_macro_averaged_mean_absolute_error[y_true0-...]Risk / blast radius
Strictly narrower behaviour: the function used to raise on inputs where
a label is predicted but absent from
y_trueand now returns afinite value. Code paths that hit the bug were getting an exception,
not a meaningful number, so no working callers can regress.
Any other comments?
doc/whats_new/v0.15.rst(current dev version). The PR-number placeholder is
:pr:\0`; I'll push a follow-up commit with the real number once GitHub assigns one, per thecheck-changelog.yml` action.[MRG]prefix is on the title.PR drafted with assistance from Claude Code. The change was reviewed
manually against scikit-learn-contrib/imbalanced-learn's source and the
upstream spec/docs cited above. The reproducer block above was used
during development; it is the same one a reviewer can paste verbatim.