From 3a361b5dc90abdf43bd9ae05ffcae9fb0fdc8696 Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Thu, 25 Jun 2026 11:36:06 -0700 Subject: [PATCH 1/2] docs(classify): fix stale example outputs and add missing examples The reclassify and equal_interval docstring examples printed arrays that do not match what the functions return for the given input; binary used np.nan in the array repr where numpy prints plain nan. Correct all three to the actual output (test_classify.py fixtures confirm the code is right, so only the docstrings were stale). Add Examples sections to std_mean, head_tail_breaks, percentiles, maximum_breaks, and box_plot, which had Parameters/Returns/References but no example. Docs only; no behavior change. Refs #3506 --- .claude/sweep-documentation-state.csv | 1 + xrspatial/classify.py | 163 +++++++++++++++++++++++--- 2 files changed, 150 insertions(+), 14 deletions(-) diff --git a/.claude/sweep-documentation-state.csv b/.claude/sweep-documentation-state.csv index 7c342d626..cf1009495 100644 --- a/.claude/sweep-documentation-state.csv +++ b/.claude/sweep-documentation-state.csv @@ -1,2 +1,3 @@ module,last_inspected,issue,severity_max,categories_found,notes,doc_coverage +classify,2026-06-25,3506,MEDIUM,1;3,"Cat3: reclassify (numpy/dask/cupy blocks) + equal_interval example outputs were stale/wrong, binary used np.nan in array repr; corrected to actual output (tests confirm code is correct). Cat1: added missing Examples to std_mean, head_tail_breaks, percentiles, maximum_breaks, box_plot. Fixed in deep-sweep-documentation-classify-2026-06-25 (PR for #3506). Cat2 natural_breaks num_sample-None omission already tracked in #3501 (left alone). All 10 public funcs listed in reference/classification.rst (no Cat4 gap). CUDA available: ran numpy examples; cupy/dask reprs reviewed statically.",10/10 perlin,2026-06-23,,MEDIUM,2;5,"name param undocumented (Cat2) + float-dtype requirement/ValueError undocumented, no Raises section (Cat5); fixed in deep-sweep-documentation-perlin-2026-06-23; repo has issues disabled so no issue number; example runs and output matches; 1 public func (perlin) listed in reference/surface.rst",1/1 diff --git a/xrspatial/classify.py b/xrspatial/classify.py index d3261a5b9..555d5ad9d 100644 --- a/xrspatial/classify.py +++ b/xrspatial/classify.py @@ -149,10 +149,10 @@ def binary(agg: xr.DataArray, values, name: Optional[str] = 'binary') -> xr.Data >>> agg_binary = binary(agg, values) >>> print(agg_binary) - array([[np.nan, 1., 1., 1., 0.], - [0., 0., 0., 0., 0.], - [0., 0., 0., 0., 0.], - [0., 0., 0., 0., np.nan]], dtype=float32) + array([[nan, 1., 1., 1., 0.], + [ 0., 0., 0., 0., 0.], + [ 0., 0., 0., 0., 0.], + [ 0., 0., 0., 0., nan]], dtype=float32) Dimensions without coordinates: dim_0, dim_1 """ _validate_raster(agg, func_name='binary', name='agg', ndim=None) @@ -353,9 +353,9 @@ def reclassify(agg: xr.DataArray, >>> print(agg_reclassify) array([[nan, 1., 1., 1., 1.], + [ 1., 1., 1., 1., 1.], [ 1., 2., 2., 2., 2.], - [ 2., 2., 2., 2., 2.], - [ 2., 3., 3., 3., 3.]], dtype=float32) + [ 2., 3., 3., 3., nan]], dtype=float32) Dimensions without coordinates: dim_0, dim_1 Reclassify works with Dask with NumPy backed xarray DataArray @@ -376,9 +376,9 @@ def reclassify(agg: xr.DataArray, >>> print(agg_reclassify_da.compute()) # print the computed the results array([[nan, 1., 1., 1., 1.], + [ 1., 1., 1., 1., 1.], [ 1., 2., 2., 2., 2.], - [ 2., 2., 2., 2., 2.], - [ 2., 3., 3., 3., 3.]], dtype=float32) + [ 2., 3., 3., 3., nan]], dtype=float32) Dimensions without coordinates: dim_0, dim_1 Reclassify works with CuPy backed xarray DataArray. @@ -394,9 +394,9 @@ def reclassify(agg: xr.DataArray, >>> print(agg_reclassify_cupy) array([[nan, 1., 1., 1., 1.], + [ 1., 1., 1., 1., 1.], [ 1., 2., 2., 2., 2.], - [ 2., 2., 2., 2., 2.], - [ 2., 3., 3., 3., 3.]], dtype=float32) + [ 2., 3., 3., 3., nan]], dtype=float32) Dimensions without coordinates: dim_0, dim_1 Reclassify works with Dask with CuPy backed xarray DataArray. @@ -1021,10 +1021,10 @@ def equal_interval(agg: xr.DataArray, >>> print(numpy_equal_interval) array([[nan, 0., 0., 0., 0.], - [ 0., 0., 0., 0., 1.], - [ 1., 1., 1., 1., 1.], - [ 1., 2., 2., 2., 2.], - [ 2., 2., 2., 2., nan]], dtype=float32) + [ 0., 1., 1., 1., 1.], + [ 2., 2., 2., 2., 2.], + [ 3., 3., 3., 3., 4.], + [ 4., 4., 4., 4., nan]], dtype=float32) Dimensions without coordinates: dim_0, dim_1 Attributes: res: (10.0, 10.0) @@ -1101,6 +1101,33 @@ def std_mean(agg: xr.DataArray, References ---------- - PySAL: https://pysal.org/mapclassify/_modules/mapclassify/classifiers.html#StdMean + + Examples + -------- + .. sourcecode:: python + + >>> import numpy as np + >>> import xarray as xr + >>> from xrspatial.classify import std_mean + >>> elevation = np.array([ + [np.nan, 1., 2., 3., 4.], + [ 5., 6., 7., 8., 9.], + [10., 11., 12., 13., 14.], + [15., 16., 17., 18., 19.], + [20., 21., 22., 23., np.inf] + ]) + >>> agg_numpy = xr.DataArray(elevation, attrs={'res': (10.0, 10.0)}) + >>> numpy_std_mean = std_mean(agg_numpy) + >>> print(numpy_std_mean) + + array([[nan, 1., 1., 1., 1.], + [ 1., 2., 2., 2., 2.], + [ 2., 2., 2., 2., 2.], + [ 2., 2., 2., 2., 3.], + [ 3., 3., 3., 3., nan]], dtype=float32) + Dimensions without coordinates: dim_0, dim_1 + Attributes: + res: (10.0, 10.0) """ _validate_raster(agg, func_name='std_mean', name='agg', ndim=None) @@ -1212,6 +1239,33 @@ def head_tail_breaks(agg: xr.DataArray, References ---------- - PySAL: https://pysal.org/mapclassify/_modules/mapclassify/classifiers.html#HeadTailBreaks + + Examples + -------- + .. sourcecode:: python + + >>> import numpy as np + >>> import xarray as xr + >>> from xrspatial.classify import head_tail_breaks + >>> elevation = np.array([ + [np.nan, 1., 2., 3., 4.], + [ 5., 6., 7., 8., 9.], + [10., 11., 12., 13., 14.], + [15., 16., 17., 18., 19.], + [20., 21., 22., 23., np.inf] + ]) + >>> agg_numpy = xr.DataArray(elevation, attrs={'res': (10.0, 10.0)}) + >>> numpy_head_tail_breaks = head_tail_breaks(agg_numpy) + >>> print(numpy_head_tail_breaks) + + array([[nan, 0., 0., 0., 0.], + [ 0., 0., 0., 0., 0.], + [ 0., 0., 0., 1., 1.], + [ 1., 1., 1., 1., 1.], + [ 1., 1., 1., 1., nan]], dtype=float32) + Dimensions without coordinates: dim_0, dim_1 + Attributes: + res: (10.0, 10.0) """ _validate_raster(agg, func_name='head_tail_breaks', name='agg', ndim=None) @@ -1293,6 +1347,33 @@ def percentiles(agg: xr.DataArray, References ---------- - PySAL: https://pysal.org/mapclassify/_modules/mapclassify/classifiers.html#Percentiles + + Examples + -------- + .. sourcecode:: python + + >>> import numpy as np + >>> import xarray as xr + >>> from xrspatial.classify import percentiles + >>> elevation = np.array([ + [np.nan, 1., 2., 3., 4.], + [ 5., 6., 7., 8., 9.], + [10., 11., 12., 13., 14.], + [15., 16., 17., 18., 19.], + [20., 21., 22., 23., np.inf] + ]) + >>> agg_numpy = xr.DataArray(elevation, attrs={'res': (10.0, 10.0)}) + >>> numpy_percentiles = percentiles(agg_numpy) + >>> print(numpy_percentiles) + + array([[nan, 0., 1., 1., 2.], + [ 2., 2., 2., 2., 2.], + [ 2., 2., 2., 3., 3.], + [ 3., 3., 3., 3., 3.], + [ 3., 4., 4., 5., nan]], dtype=float32) + Dimensions without coordinates: dim_0, dim_1 + Attributes: + res: (10.0, 10.0) """ _validate_raster(agg, func_name='percentiles', name='agg', ndim=None) @@ -1432,6 +1513,33 @@ def maximum_breaks(agg: xr.DataArray, References ---------- - PySAL: https://pysal.org/mapclassify/_modules/mapclassify/classifiers.html#MaximumBreaks + + Examples + -------- + .. sourcecode:: python + + >>> import numpy as np + >>> import xarray as xr + >>> from xrspatial.classify import maximum_breaks + >>> elevation = np.array([ + [np.nan, 1., 2., 3., 4.], + [ 5., 6., 7., 8., 9.], + [10., 11., 12., 13., 14.], + [15., 16., 17., 18., 19.], + [20., 21., 22., 23., np.inf] + ]) + >>> agg_numpy = xr.DataArray(elevation, attrs={'res': (10.0, 10.0)}) + >>> numpy_maximum_breaks = maximum_breaks(agg_numpy) + >>> print(numpy_maximum_breaks) + + array([[nan, 0., 0., 0., 0.], + [ 0., 0., 0., 0., 0.], + [ 0., 0., 0., 0., 0.], + [ 0., 0., 0., 0., 0.], + [ 1., 2., 3., 4., nan]], dtype=float32) + Dimensions without coordinates: dim_0, dim_1 + Attributes: + res: (10.0, 10.0) """ _validate_raster(agg, func_name='maximum_breaks', name='agg', ndim=None) _validate_scalar(k, func_name='maximum_breaks', name='k', dtype=int, min_val=2) @@ -1566,6 +1674,33 @@ def box_plot(agg: xr.DataArray, References ---------- - PySAL: https://pysal.org/mapclassify/_modules/mapclassify/classifiers.html#BoxPlot + + Examples + -------- + .. sourcecode:: python + + >>> import numpy as np + >>> import xarray as xr + >>> from xrspatial.classify import box_plot + >>> elevation = np.array([ + [np.nan, 1., 2., 3., 4.], + [ 5., 6., 7., 8., 9.], + [10., 11., 12., 13., 14.], + [15., 16., 17., 18., 19.], + [20., 21., 22., 23., np.inf] + ]) + >>> agg_numpy = xr.DataArray(elevation, attrs={'res': (10.0, 10.0)}) + >>> numpy_box_plot = box_plot(agg_numpy) + >>> print(numpy_box_plot) + + array([[nan, 1., 1., 1., 1.], + [ 1., 1., 2., 2., 2.], + [ 2., 2., 2., 3., 3.], + [ 3., 3., 3., 4., 4.], + [ 4., 4., 4., 4., nan]], dtype=float32) + Dimensions without coordinates: dim_0, dim_1 + Attributes: + res: (10.0, 10.0) """ _validate_raster(agg, func_name='box_plot', name='agg', ndim=None) From ef98faa9c905acfc33caf13c07c75a63e6413f87 Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Thu, 25 Jun 2026 12:59:35 -0700 Subject: [PATCH 2/2] docs(classify): refresh stale dask/cupy reprs in reclassify example Address review feedback on PR #3516: the reclassify docstring still showed pre-refactor lazy reprs next to the corrected result arrays. - agg_da repr: dtype float32 -> float64 (the example input is float64) - lazy result repr: task name _run_numpy_bin -> xrspatial.reclassify (renamed via _dask_task_name_kwargs) - cupy type: cupy.core.core.ndarray -> cupy.ndarray Verified each repr against output on a CUDA host. Docs only. Refs #3506 --- xrspatial/classify.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xrspatial/classify.py b/xrspatial/classify.py index 555d5ad9d..4421bd54e 100644 --- a/xrspatial/classify.py +++ b/xrspatial/classify.py @@ -366,12 +366,12 @@ def reclassify(agg: xr.DataArray, >>> agg_da = xr.DataArray(data_da, name='agg_da') >>> print(agg_da) - dask.array + dask.array Dimensions without coordinates: dim_0, dim_1 >>> agg_reclassify_da = reclassify(agg_da, bins=bins, new_values=new_values) # noqa >>> print(agg_reclassify_da) - dask.array<_run_numpy_bin, shape=(4, 5), dtype=float32, chunksize=(3, 3), chunktype=numpy.ndarray> + dask.array Dimensions without coordinates: dim_0, dim_1 >>> print(agg_reclassify_da.compute()) # print the computed the results @@ -390,7 +390,7 @@ def reclassify(agg: xr.DataArray, >>> agg_cupy = xr.DataArray(data_cupy) >>> agg_reclassify_cupy = reclassify(agg_cupy, bins, new_values) >>> print(type(agg_reclassify_cupy.data)) - + >>> print(agg_reclassify_cupy) array([[nan, 1., 1., 1., 1.],