Skip to content

Fix class_labels mutation across multi-metric write_metrics_reports#8902

Open
aymuos15 wants to merge 2 commits into
Project-MONAI:devfrom
aymuos15:fix/handlers-class-labels-mutation
Open

Fix class_labels mutation across multi-metric write_metrics_reports#8902
aymuos15 wants to merge 2 commits into
Project-MONAI:devfrom
aymuos15:fix/handlers-class-labels-mutation

Conversation

@aymuos15

@aymuos15 aymuos15 commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Description

write_metrics_reports rebinds and appends to its class_labels parameter inside the metric_details loop. After the first metric's CSV is written, class_labels is no longer None. It holds ["class0", ..., "classN", "mean"]. On every subsequent metric the else branch runs and appends another "mean", producing headers like class0,class1,mean,mean,mean.

This hits any evaluation with two or more metrics in metric_details, for example Dice + IoU + Hausdorff. The only real caller MetricsSaver always passes class_labels=None. The first metric's CSV is correct, but every one after is structurally corrupt with mismatched header and data columns.

The fix uses a local labels variable per iteration so the original class_labels parameter is never modified. The existing test only verified existence of subsequent metric files, not their headers. A regression test is included.

Types of changes

  • Non-breaking change (fix or new feature that would not break existing functionality).
  • New tests added to cover the changes.
  • Integration tests passed locally by running ./runtests.sh -f -u --net --coverage.
  • Quick tests passed locally by running ./runtests.sh --quick --unittests --disttests.
  • In-line docstrings updated.

aymuos15 added 2 commits June 7, 2026 16:10
Use a local `labels` variable per loop iteration instead of
rebinding and appending to the `class_labels` parameter. Previously,
after the first metric detail was written, `class_labels` was no
longer None, so the else branch ran on subsequent iterations and
appended an extra "mean" entry each time. This produced corrupted
CSV headers (e.g. "class0,class1,mean,mean") for every metric
after the first.

Signed-off-by: Soumya Snigdha Kundu <soumya_snigdha.kundu@kcl.ac.uk>
Verifies that every metric detail CSV has exactly one "mean" header
column, regardless of how many metrics are in the dict. Fails before
the fix because `class_labels` accumulated an extra "mean" per
metric beyond the first.

Signed-off-by: Soumya Snigdha Kundu <soumya_snigdha.kundu@kcl.ac.uk>
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 019d4ba5-27f4-4b60-9c7d-8b39e1aef859

📥 Commits

Reviewing files that changed from the base of the PR and between 2a7d0cf and e609d0a.

📒 Files selected for processing (2)
  • monai/handlers/utils.py
  • tests/handlers/test_write_metrics_reports.py

📝 Walkthrough

Walkthrough

Renamed the class label variable from class_labels to labels in the write_metrics_reports function. Updated both the raw CSV header generation (lines 125–133) and summary CSV row writing (line 167) to reference the new name. Added a test case that verifies correct header structure in raw CSV files when writing multiple metrics with differing class counts.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly describes the main fix: preventing class_labels mutation in write_metrics_reports across multiple metrics.
Description check ✅ Passed Description covers the bug, root cause, solution, and test additions; missing only optional integration/documentation checkboxes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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.

1 participant