From ff4395157428fcfa6c84a8c7394af9e721dee34f Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Tue, 30 Jun 2026 12:09:08 -0700 Subject: [PATCH] docs(mahalanobis): add Examples and document NaN propagation The public mahalanobis() docstring had Parameters and Returns but no Examples section, unlike peer utilities such as normalize.rescale. Its NaN-propagation rule was also undocumented despite being pinned by test_nan_propagation. Add a runnable Examples block (whose input includes a NaN so the output shows propagation directly), a Notes section covering the NaN rule, the auto-stats N+1 all-finite requirement, and the four supported backends, and note default='mahalanobis' on the name parameter. Documentation only; no behavior change. Refs #3579 --- .claude/sweep-documentation-state.csv | 1 + xrspatial/mahalanobis.py | 28 ++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/.claude/sweep-documentation-state.csv b/.claude/sweep-documentation-state.csv index a29f4e0ea..fe6b63381 100644 --- a/.claude/sweep-documentation-state.csv +++ b/.claude/sweep-documentation-state.csv @@ -4,5 +4,6 @@ fire,2026-06-25,,MEDIUM,1;5,"all 7 public funcs (dnbr, rdnbr, burn_severity_clas flood,2026-06-25,,HIGH,1;4;5,"Cat4 HIGH: vegetation_roughness, vegetation_curve_number, flood_depth_vegetation public but absent from reference/flood.rst; Cat1 MEDIUM: no Examples on any of 7 public funcs; Cat5 MEDIUM: backend support undocumented (all 4 backends) + NaN propagation undocumented for curve_number_runoff/travel_time. Fixed in deep-sweep-documentation-flood-2026-06-25: added 3 rst entries, Examples+backend Notes to all 7 funcs (examples executed OK on CUDA host), NaN notes. PR #3502 opened with the fix; gh issue create blocked by auto-mode classifier so no issue number.",7/7 geotiff,2026-06-25,,MEDIUM,1,"to_geotiff (public write entry point) had Parameters/Returns/Raises but no Examples section while open_geotiff does (Cat1 MEDIUM); added Examples block (plain GeoTIFF, cog=True, .vrt mosaic) modeled on open_geotiff; fixed on deep-sweep-documentation-geotiff-2026-06-25; repo issues disabled so no issue number. Cat2/3/4/5 clean: open_geotiff/to_geotiff signature-docstring parity locked by parity/test_signature_contract.py + write/test_bigtiff.py + test_polish.py; both funcs in autosummary in reference/geotiff.rst; reference page mirrors SUPPORTED_FEATURES tiers (tier-parity gate); CUDA available, all docstring examples are +SKIP illustrative only",2/2 interpolate-idw,2026-06-26,,MEDIUM,1;5,"Cat1 MEDIUM: idw (only public func in _idw.py) had Parameters/Returns/Raises but no Examples section. Cat5 MEDIUM: backend support undocumented (numpy/cupy/dask+numpy/dask+cupy via ArrayTypeFunctionMapping) + k-nearest GPU rejection (NotImplementedError) and NaN/inf input-point dropping undocumented. Doc-only fix on deep-sweep-documentation-interpolate-idw-2026-06-26: added backend-support line, Examples block (executed, output matches), Returns dtype/NaN/fill_value notes. Cat2 clean (all 9 params documented, defaults/types match signature). Cat3 n/a (no prior examples). Cat4 clean (idw listed in reference/interpolation.rst autosummary). CUDA available: ran numpy example; cupy/dask paths covered by test_interpolation.py (92 pass). repo issues disabled so no issue number.",1/1 +mahalanobis,2026-06-30,3579,MEDIUM,1;2;5,"Cat1 MEDIUM: mahalanobis (only public func) had Parameters/Returns but no Examples section (peers normalize.rescale/standardize do). Cat5 MEDIUM: NaN propagation (any non-finite band -> NaN pixel) and auto-stats N+1 all-finite requirement undocumented. Cat2 LOW: name param default not noted. Doc-only fix on deep-sweep-documentation-mahalanobis-2026-06-30: added Notes (NaN+backends) and a runnable Examples block (executed, output matches incl. NaN), set name default='mahalanobis'. Returns float64/shape and 4-backend claim verified against ArrayTypeFunctionMapping (accurate, left as-is). Cat3 n/a (no prior examples). Cat4 clean (listed in reference/utilities.rst). issue #3579. CUDA available: ran numpy example; cupy/dask covered by test_mahalanobis.py (24 pass).",1/1 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 templates,2026-06-26,3541,MEDIUM,3;5,"Cat3/Cat5 MEDIUM: from_template docstring example 'from_template(""FRA"", preserve=""shape"").attrs[""crs""]' showed 32631/UTM 31N but code returns 32630/UTM 30N (GADM FRA bbox includes overseas territories, centroid lon -2.98 -> UTM 30N; code correct per documented centroid-UTM contract). Doc-only fix on deep-sweep-documentation-templates-2026-06-26; issue #3541, PR pending. Both public funcs (from_template, list_templates) fully documented and in reference/templates.rst (no Cat1/Cat2/Cat4). CUDA available: ran all docstring examples incl. preserve paths; 63/63 test_templates.py pass.",2/2 diff --git a/xrspatial/mahalanobis.py b/xrspatial/mahalanobis.py index 4ede6264f..377137114 100644 --- a/xrspatial/mahalanobis.py +++ b/xrspatial/mahalanobis.py @@ -386,13 +386,39 @@ def mahalanobis( inv_cov : numpy array, shape (N, N), optional Inverse covariance matrix. Must be provided together with *mean*, or both omitted (auto-computed). - name : str + name : str, default='mahalanobis' Name for the output DataArray. Returns ------- xr.DataArray 2-D float64 raster with same coords/dims/attrs as ``bands[0]``. + + Notes + ----- + A pixel is set to NaN in the output when any band is non-finite at + that pixel. When *mean* and *inv_cov* are omitted they are estimated + from the pixels that are finite across all bands, which requires at + least ``N + 1`` such pixels. Supports the numpy, cupy, dask+numpy, + and dask+cupy backends. + + Examples + -------- + .. sourcecode:: python + + >>> import numpy as np + >>> import xarray as xr + >>> from xrspatial.mahalanobis import mahalanobis + + >>> red = xr.DataArray( + np.array([[0., 1.], [2., 3.]]), dims=['y', 'x']) + >>> green = xr.DataArray( + np.array([[0., np.nan], [1., 1.]]), dims=['y', 'x']) + >>> mahalanobis([red, green], mean=np.zeros(2), inv_cov=np.eye(2)) + + array([[0. , nan], + [2.23606798, 3.16227766]]) + Dimensions without coordinates: y, x """ # --- input validation --- if len(bands) < 2: