Skip to content

fix: nan-variant reducers drop behavior and attrs#4093

Open
henryiii wants to merge 4 commits into
mainfrom
henryiii/fix-nan-reducers
Open

fix: nan-variant reducers drop behavior and attrs#4093
henryiii wants to merge 4 commits into
mainfrom
henryiii/fix-nan-reducers

Conversation

@henryiii

@henryiii henryiii commented Jun 10, 2026

Copy link
Copy Markdown
Member

🤖 AI text below 🤖

Summary

Fix a bug where nan-variant reducers (nanmin, nanmax, nanmean, nanvar) lost the input array's behavior dict and attrs during computation because the intermediate nan_to_none call was invoked with highlevel=False and/or None for behavior/attrs.

  • src/awkward/operations/ak_min.py:147nan_to_none._impl(array, False, None, None)(array, True, behavior, attrs). Matches siblings nansum, nanprod, nanstd, nanargmin, nanargmax.
  • src/awkward/operations/ak_max.py:147 — same fix as ak_min.py.
  • src/awkward/operations/ak_mean.py:179nan_to_none._impl(x, False, behavior, attrs)(x, True, behavior, attrs).
  • src/awkward/operations/ak_var.py:174nan_to_none._impl(x, highlevel, behavior, attrs)(x, True, behavior, attrs). Passing the user's highlevel flag into the intermediate conversion changed propagation semantics when highlevel=False was requested.
  • src/awkward/_reducers.py:249-256ArgMax.apply's kernel-lookup dtype tuple had (result, fromptr, parents, starts, offsets) whereas ArgMin and the kernel spec use (..., parents, offsets, starts). Reordered to match.

Regression tests are in tests/test_4093_nan_reducer_behavior_attrs.py — one parametrized function over all five nan-variant reducers, verifying result.behavior and result.attrs are preserved on array-valued output (axis=1), which is the exact code path where the bug was observable.

AI assistance

This fix was generated using Claude Code (automated multi-agent review, model: claude-fable-5). The findings were pre-verified against sibling functions (nansum, nanstd, etc.) that already implement the correct pattern.

Test plan

  • New regression tests: tests/test_4093_nan_reducer_behavior_attrs.py (5 parametrized cases, one per nan-variant reducer)
  • prek -a --quiet passes (no lint issues)

🤖 Generated with Claude Code

henryiii added a commit that referenced this pull request Jun 10, 2026
24 tests covering: behavior dict and attrs preservation across nanmin,
nanmax, nanmean, nanvar, nanstd; correctness on nan-free data vs non-nan
variants; correct NaN exclusion; highlevel=False semantics for nanvar/nanmean.

Assisted-by: ClaudeCode:claude-fable-5
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.34%. Comparing base (712dac0) to head (55c5bca).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
Files with missing lines Coverage Δ
src/awkward/_reducers.py 98.31% <ø> (ø)
src/awkward/operations/ak_max.py 94.73% <ø> (ø)
src/awkward/operations/ak_mean.py 94.23% <ø> (+5.76%) ⬆️
src/awkward/operations/ak_min.py 94.73% <ø> (ø)
src/awkward/operations/ak_var.py 89.47% <ø> (+5.26%) ⬆️

... and 1 file with indirect coverage changes

@ikrommyd ikrommyd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I searched a bit for ak_nan_to_none in the code and I notice that the arguments we pass when it's called inside another highlevel function are inconsistent. Does this PR fix all of them?

Intuitively I'd think we want highlevel=False inside the highlevel function implementation cause it's already unwrapped and we'd want to wrap it once at the end. highlevel=True should do extra wrapping/unwrapping when its called inside another highlevel function.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file has test classes. I'm pretty sure we never do that here.

henryiii and others added 4 commits June 11, 2026 15:39
Pass highlevel=True, behavior, attrs into the intermediate nan_to_none call
in ak_min, ak_max, ak_mean, and ak_var so that custom behavior dicts and
attrs are not silently dropped during nan filtering. Also reorder the
dtype-tuple in ArgMax.apply to match ArgMin and the kernel spec
(parents, offsets, starts instead of parents, starts, offsets).

Assisted-by: ClaudeCode:claude-fable-5
24 tests covering: behavior dict and attrs preservation across nanmin,
nanmax, nanmean, nanvar, nanstd; correctness on nan-free data vs non-nan
variants; correct NaN exclusion; highlevel=False semantics for nanvar/nanmean.

Assisted-by: ClaudeCode:claude-fable-5
Rename test file from hyphens to underscores (required by validate-test-names.py).
Replace 24-test class-based suite with a single parametrized function covering
all five nan-variant reducers; the new tests actually verify result.behavior and
result.attrs on array-valued output, which is where the original bug was observable.

Assisted-by: ClaudeCode:claude-sonnet-4-6
@henryiii henryiii force-pushed the henryiii/fix-nan-reducers branch from fedb22a to 55c5bca Compare June 11, 2026 19:52
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.

2 participants