You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #906 / PR #978, which gave Combiner.median_combine a spec-only NaN-aware median (ccdproc/_nanmedian.py) for namespaces that do not provide nanmedian. Its three siblings did not get the same treatment.
_default_average, _default_sum and _default_std (ccdproc/combiner.py:44-84) all still end in
try:
returnxp.nanmean# or xp.nansum / xp.nanstdexceptAttributeErrorase:
raiseRuntimeError(
"No NaN-aware mean function available. Please install bottleneck."
) frome
nanmean/nansum/nanstd are not in the array API standard, so on any conforming-but-minimal namespace the average and sum paths raise instead of computing. The error message is also misleading in that situation: installing bottleneck does not help, because _default_* only reaches for bottleneck when the namespace is numpy.
Impact
On main (d85524a) the array-api-strict job reports 99 failures. 23 of them are this bug — the single largest remaining cluster, all in ccdproc/tests/test_combiner.py:
RuntimeError: No NaN-aware mean function available (14)
No failure is currently attributed to _default_std, but that is only because average_combine calls _default_average (line 687) and sum_combine calls _default_sum (line 771) before either reaches _default_std (lines 693 / 774). Fixing mean and sum without std would just move the same RuntimeError a few lines down, so all three need to land together.
What is needed
Three fallbacks in the spirit of _nanmedian.nanmedian, but much simpler — no sort is required, only masking and counting:
nanmean(x, axis=0) — the above divided by the count of non-NaN entries
nanstd(x, axis=0) — the usual two-pass or sum-of-squares form over the same count, ddof=0
Points to get right, mirroring what nanmedian already had to handle:
All-NaN slices.numpy.nansum returns 0.0, while numpy.nanmean and numpy.nanstd return NaN (verified locally; both emit a RuntimeWarning, which the fallbacks should not do since pytest turns warnings into errors here). Confirm bottleneck agrees before relying on it.
Dtype promotion. Integer/boolean input should be promoted via xp.__array_namespace_info__().default_dtypes(device=...)["real floating"] rather than a hardcoded float64 — jax without JAX_ENABLE_X64 has no float64 and warns when asked for one.
Axis. All call sites use axis=0 only (combiner.py:687-705, 771-790), so matching nanmedian's "single integer axis, NotImplementedError otherwise" signature is sufficient.
The error message in the remaining raise paths, if any survive, should stop recommending bottleneck for non-numpy namespaces.
Tests
ccdproc/tests/test_nanmedian.py is the model: exercise the fallbacks directly against the numpy reference across dtypes, all-NaN slices, partially-masked slices and non-default devices, independently of whether the active backend happens to provide native versions.
Notes
Once this lands, the strict job should drop from 99 to roughly 76, and the Combiner failures that remain are the .any()/.nbytes method calls at combiner.py:517 and combiner.py:844-859 plus the astropy.stats densification tracked in #929.
Follow-up to #906 / PR #978, which gave
Combiner.median_combinea spec-only NaN-aware median (ccdproc/_nanmedian.py) for namespaces that do not providenanmedian. Its three siblings did not get the same treatment._default_average,_default_sumand_default_std(ccdproc/combiner.py:44-84) all still end innanmean/nansum/nanstdare not in the array API standard, so on any conforming-but-minimal namespace the average and sum paths raise instead of computing. The error message is also misleading in that situation: installing bottleneck does not help, because_default_*only reaches for bottleneck when the namespace is numpy.Impact
On
main(d85524a) thearray-api-strictjob reports 99 failures. 23 of them are this bug — the single largest remaining cluster, all inccdproc/tests/test_combiner.py:RuntimeError: No NaN-aware mean function available(14)test_combiner_average,test_combiner_mask_average,test_combiner_dtype,test_combiner_3dtest_1Dweights,test_combiner_weighted_average_with_mask_by_pixel,test_combiner_weighted_average_with_clipping,test_combiner_weighted_average_preserves_custom_scale_functest_average_combine_uncertainty,test_combiner_uncertainty_averagetest_combine_result_uncertainty_and_mask[average_combine-True/False]test_writeable_after_combine[average_combine],test_user_supplied_combine_func_that_relies_on_masks[average_combine]RuntimeError: No NaN-aware sum function available(9)test_combiner_sum,test_combiner_mask_sum,test_combiner_sum_weighted,test_combiner_sum_weighted_by_pixel,test_combiner_sum_weighted_with_masktest_sum_combine_uncertainty,test_combine_result_uncertainty_and_mask[sum_combine-True/False],test_writeable_after_combine[sum_combine]No failure is currently attributed to
_default_std, but that is only becauseaverage_combinecalls_default_average(line 687) andsum_combinecalls_default_sum(line 771) before either reaches_default_std(lines 693 / 774). Fixing mean and sum without std would just move the sameRuntimeErrora few lines down, so all three need to land together.What is needed
Three fallbacks in the spirit of
_nanmedian.nanmedian, but much simpler — no sort is required, only masking and counting:nansum(x, axis=0)—xp.sum(xp.where(xp.isnan(x), 0, x), axis=axis)nanmean(x, axis=0)— the above divided by the count of non-NaN entriesnanstd(x, axis=0)— the usual two-pass or sum-of-squares form over the same count,ddof=0Points to get right, mirroring what
nanmedianalready had to handle:numpy.nansumreturns0.0, whilenumpy.nanmeanandnumpy.nanstdreturnNaN(verified locally; both emit aRuntimeWarning, which the fallbacks should not do since pytest turns warnings into errors here). Confirm bottleneck agrees before relying on it.xp.__array_namespace_info__().default_dtypes(device=...)["real floating"]rather than a hardcodedfloat64— jax withoutJAX_ENABLE_X64has nofloat64and warns when asked for one.device=array_api_compat.device(x); the device-mismatch failures in array-api-strict job: device-mismatch noise from inline array creation in test bodies #946 are exactly what happens when that is missed.axis=0only (combiner.py:687-705,771-790), so matchingnanmedian's "single integer axis,NotImplementedErrorotherwise" signature is sufficient.The error message in the remaining
raisepaths, if any survive, should stop recommending bottleneck for non-numpy namespaces.Tests
ccdproc/tests/test_nanmedian.pyis the model: exercise the fallbacks directly against the numpy reference across dtypes, all-NaN slices, partially-masked slices and non-default devices, independently of whether the active backend happens to provide native versions.Notes
Once this lands, the strict job should drop from 99 to roughly 76, and the
Combinerfailures that remain are the.any()/.nbytesmethod calls atcombiner.py:517andcombiner.py:844-859plus theastropy.statsdensification tracked in #929.