classify: return all-NaN instead of crashing on all-non-finite input (#3510)#3548
Merged
Conversation
…3510) head_tail_breaks, percentiles, and box_plot reduced over the empty finite subset of an all-NaN/all-inf raster and raised an opaque reduction error. Guard the empty-finite case in each so they degrade to an all-NaN result, matching equal_interval, std_mean, and maximum_breaks. Flip the xfail regression tests to plain assertions and add all-inf coverage.
brendancol
commented
Jun 27, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: classify all-non-finite handling (#3510)
Read the full classify.py context, not just the diff. The fix is correct and the backend coverage holds up.
Blockers
None.
Suggestions
percentilesnow reachesnp.nanmax(clean)in the wrapper on all-NaN input (the crash that used to preempt it is gone), so it emits a benignRuntimeWarning: All-NaN slice encountered.std_meanhas the same behaviour and its test suppresses it withwarnings.catch_warnings(). For consistency,test_percentiles_all_nan/test_percentiles_all_infcould do the same (xrspatial/tests/test_classify.py). It does not fail CI -- RuntimeWarning is not promoted to error in setup.cfg -- so this is housekeeping, not a correctness issue.head_tail_breaksandbox_plotshort-circuit before any nan-reduction and stay warning-free.
Nits
None.
What looks good
- The guard sits in the shared helpers (
_compute_head_tail_bins,_run_percentiles,_run_dask_percentiles,_box_plot_bins_from_sample) plus the eager_run_box_plotbranch, so a single guard per code path covers all four backends without duplicating logic. Verified the box_plot mapper routes numpy/cupy through_run_box_plotand both dask paths through_box_plot_bins_from_sample. - Returning a
[nan]bin (or_bin(agg, [nan], [0])) is the same degradationequal_interval/std_mean/maximum_breaksalready use, so behaviour is uniform across classifiers. - all-inf coverage uses both
+infand-inf, exercising theisinf -> nanmapping. - Full test_classify.py passes (117), including cupy and dask+cupy on a CUDA box.
Checklist
- Degradation matches sibling classifiers
- All four backends produce all-NaN consistently
- NaN/inf handling correct (isinf catches both signs)
- Edge case (all-non-finite) covered by tests
- Dask sample paths guarded
- No premature materialization introduced
- No new public API (no README/docstring change needed)
… tests (#3510) percentiles still takes nanmax over the cleaned array to set the top bin edge, which warns on all-non-finite input like std_mean does. Suppress it in the regression tests to match the test_std_mean_all_nan convention.
brendancol
commented
Jun 27, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
Follow-up review (#3510)
Re-reviewed after the follow-up commit and the clean merge of origin/main.
Disposition of prior findings
- Suggestion (percentiles all-NaN RuntimeWarning): fixed in c94d36e.
test_percentiles_all_nanandtest_percentiles_all_infnow wrap the call inwarnings.catch_warnings()the same waytest_std_mean_all_nandoes. Confirmed by running the all-NaN/all-inf subset with-W error::RuntimeWarning: 16 passed, no unsuppressed warnings.
New state
- Merged origin/main (geotiff symbology sidecar commit, unrelated to classify). No conflicts. Full test_classify.py still passes (117).
No remaining findings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3510
What
head_tail_breaks,percentiles, andbox_plotraised an opaque reduction error when every pixel was non-finite (all NaN, or all +/-inf, which is mapped to NaN before binning). Each one took a max or percentile over the finite subset of the data, and on this input that subset is empty.head_tail_breaks->ValueError: zero-size array to reduction operation fmaxpercentiles->IndexError: index -1 is out of bounds for axis 0 with size 0box_plot->ValueError: zero-size array to reduction operation fmaxThey now guard the empty-finite case and return an all-NaN array, matching
equal_interval,std_mean, andmaximum_breaks.Backends
numpy, cupy, dask+numpy, dask+cupy. The guard sits in the shared bin/percentile helpers and the eager box_plot branch, so it covers all four.
Test plan