Skip to content

Commit c0ad897

Browse files
committed
Add tests for focal variety (#1040)
Cover correctness on categorical data, NaN handling, all-NaN windows, single-cell rasters, and dask+numpy backend parity. Update the data_focal_stats fixture to include variety expected values.
1 parent 8274133 commit c0ad897

File tree

1 file changed

+83
-1
lines changed

1 file changed

+83
-1
lines changed

xrspatial/tests/test_focal.py

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,13 @@ def data_focal_stats():
439439
[[0, 1, 2, 3.],
440440
[4, 5, 7, 9.],
441441
[8, 13, 15, 17.],
442-
[12, 21, 23, 25.]]
442+
[12, 21, 23, 25.]],
443+
# variety -- arange(16) so every value is unique;
444+
# kernel hits center + upper-left diagonal only
445+
[[1, 1, 1, 1.],
446+
[1, 2, 2, 2.],
447+
[1, 2, 2, 2.],
448+
[1, 2, 2, 2.]]
443449
])
444450
return data, kernel, expected_result
445451

@@ -499,6 +505,82 @@ def test_focal_stats_dask_cupy():
499505
equal_nan=True, rtol=1e-4)
500506

501507

508+
# --- focal variety (issue-1040) ------------------------------------------
509+
510+
def _variety_reference_data():
511+
"""Categorical 6x6 grid with known variety counts for a 3x3 box kernel."""
512+
data = np.array([
513+
[1, 1, 2, 2, 3, 3],
514+
[1, 1, 2, 2, 3, 3],
515+
[4, 4, 5, 5, 6, 6],
516+
[4, 4, 5, 5, 6, 6],
517+
[7, 7, 8, 8, 9, 9],
518+
[7, 7, 8, 8, 9, 9],
519+
], dtype=np.float64)
520+
kernel = np.ones((3, 3))
521+
return data, kernel
522+
523+
524+
def test_variety_numpy():
525+
data, kernel = _variety_reference_data()
526+
agg = create_test_raster(data)
527+
result = focal_stats(agg, kernel, stats_funcs=['variety'])
528+
vals = result.sel(stats='variety').values
529+
# Interior pixel (2,2) sees values {1,2,4,5} -> 4
530+
assert vals[2, 2] == 4.0
531+
# Corner pixel (0,0) window is 2x2 -> {1,1,1,1} -> 1
532+
assert vals[0, 0] == 1.0
533+
# Edge pixel (0,2) window is 2x3 -> {1,1,2,2,2,2} -> 2
534+
assert vals[0, 2] == 2.0
535+
# All interior values should be positive integers
536+
assert np.all(vals[1:-1, 1:-1] >= 1)
537+
538+
539+
@dask_array_available
540+
def test_variety_dask_numpy():
541+
data, kernel = _variety_reference_data()
542+
np_agg = create_test_raster(data)
543+
dk_agg = create_test_raster(data, backend='dask')
544+
np_result = focal_stats(np_agg, kernel, stats_funcs=['variety'])
545+
dk_result = focal_stats(dk_agg, kernel, stats_funcs=['variety'])
546+
np.testing.assert_allclose(
547+
np_result.values, dk_result.values, equal_nan=True)
548+
549+
550+
def test_variety_nan_handling():
551+
"""NaN cells should not count as a distinct value."""
552+
data = np.array([
553+
[1, np.nan, 2],
554+
[1, 1, 2],
555+
[3, 3, 3],
556+
], dtype=np.float64)
557+
kernel = np.ones((3, 3))
558+
agg = create_test_raster(data)
559+
result = focal_stats(agg, kernel, stats_funcs=['variety'])
560+
vals = result.sel(stats='variety').values
561+
# Center pixel (1,1) sees {1,2,1,1,2,3,3,3} (NaN skipped) -> {1,2,3} -> 3
562+
assert vals[1, 1] == 3.0
563+
564+
565+
def test_variety_all_nan():
566+
"""A window of all NaN should produce NaN variety."""
567+
data = np.full((3, 3), np.nan)
568+
kernel = np.ones((3, 3))
569+
agg = create_test_raster(data)
570+
result = focal_stats(agg, kernel, stats_funcs=['variety'])
571+
vals = result.sel(stats='variety').values
572+
assert np.all(np.isnan(vals))
573+
574+
575+
def test_variety_single_cell():
576+
"""1x1 raster, 1x1 kernel -> variety 1."""
577+
data = np.array([[42.0]])
578+
kernel = np.ones((1, 1))
579+
agg = create_test_raster(data)
580+
result = focal_stats(agg, kernel, stats_funcs=['variety'])
581+
assert result.sel(stats='variety').values.item() == 1.0
582+
583+
502584
@pytest.fixture
503585
def data_hotspots():
504586
data = np.asarray([

0 commit comments

Comments
 (0)