fix: nan-variant reducers drop behavior and attrs#4093
Conversation
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 Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files
|
ikrommyd
left a comment
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
This file has test classes. I'm pretty sure we never do that here.
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
fedb22a to
55c5bca
Compare
🤖 AI text below 🤖
Summary
Fix a bug where nan-variant reducers (
nanmin,nanmax,nanmean,nanvar) lost the input array'sbehaviordict andattrsduring computation because the intermediatenan_to_nonecall was invoked withhighlevel=Falseand/orNonefor behavior/attrs.src/awkward/operations/ak_min.py:147—nan_to_none._impl(array, False, None, None)→(array, True, behavior, attrs). Matches siblingsnansum,nanprod,nanstd,nanargmin,nanargmax.src/awkward/operations/ak_max.py:147— same fix asak_min.py.src/awkward/operations/ak_mean.py:179—nan_to_none._impl(x, False, behavior, attrs)→(x, True, behavior, attrs).src/awkward/operations/ak_var.py:174—nan_to_none._impl(x, highlevel, behavior, attrs)→(x, True, behavior, attrs). Passing the user'shighlevelflag into the intermediate conversion changed propagation semantics whenhighlevel=Falsewas requested.src/awkward/_reducers.py:249-256—ArgMax.apply's kernel-lookup dtype tuple had(result, fromptr, parents, starts, offsets)whereasArgMinand 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, verifyingresult.behaviorandresult.attrsare 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
tests/test_4093_nan_reducer_behavior_attrs.py(5 parametrized cases, one per nan-variant reducer)prek -a --quietpasses (no lint issues)🤖 Generated with Claude Code