From 615b878f937160da6566f314872fb8b1d81d07cc Mon Sep 17 00:00:00 2001 From: NoiceHax Date: Sat, 15 Aug 2026 15:06:58 +0530 Subject: [PATCH] fix: accept sets in isin numpy turns a set into a 0d object array, so Dataset.isin and DataArray.isin returned False everywhere when given a set. Convert set-like arguments to a list before handing them to numpy. Co-authored-by: Claude --- doc/whats-new.rst | 5 +++++ xarray/core/common.py | 7 +++++++ xarray/tests/test_dataarray.py | 7 +++++++ xarray/tests/test_dataset.py | 5 ++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 0e74ca1e2fc..70ec89f96c9 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -69,6 +69,11 @@ Bug Fixes for zarr writes. Existing zarr stores written with the old ``int8`` encoding are still read correctly. (:issue:`2937`, :pull:`11318`) By `Evan Lyall `_. +- :py:meth:`~xarray.DataArray.isin` and :py:meth:`~xarray.Dataset.isin` now + accept sets, frozensets and other set-like objects such as ``dict.keys()``. + Previously these were turned into a 0-dimensional object array by numpy, so + the result was ``False`` everywhere (:issue:`10022`). + By `Chandan P `_. Documentation diff --git a/xarray/core/common.py b/xarray/core/common.py index 4c506b05325..6d803ea4fd3 100644 --- a/xarray/core/common.py +++ b/xarray/core/common.py @@ -3,6 +3,7 @@ import datetime import warnings from collections.abc import Callable, Hashable, Iterable, Iterator, Mapping +from collections.abc import Set as AbstractSet from contextlib import suppress from html import escape from textwrap import dedent @@ -1366,6 +1367,8 @@ def isin(self, test_elements: Any) -> Self: test_elements : array_like The values against which to test each value of `element`. This argument is flattened if an array or array_like. + Sets (and other set-like objects such as ``dict.keys()``) are + converted to a list first. See numpy notes for behavior with non-array-like parameters. Returns @@ -1394,6 +1397,10 @@ def isin(self, test_elements: Any) -> Self: raise TypeError( f"isin() argument must be convertible to an array: {test_elements}" ) + elif isinstance(test_elements, AbstractSet): + # numpy converts a set to a 0d object array, which never compares + # equal to any element, so convert it to a sequence first + test_elements = list(test_elements) elif isinstance(test_elements, Variable | DataArray): # need to explicitly pull out data to support dask arrays as the # second argument diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index b2619008379..fe5b4fe6b31 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -7018,6 +7018,13 @@ def test_isin(da) -> None: result = da.isin([2, 3]).sel(y=list("de"), z=0) assert_equal(result, expected) + # set-like objects give the same answer as the equivalent list, GH10022 + result = da.isin({2, 3}).sel(y=list("de"), z=0) + assert_equal(result, expected) + + result = da.isin({2: "a", 3: "b"}.keys()).sel(y=list("de"), z=0) + assert_equal(result, expected) + def test_raise_no_warning_for_nan_in_binary_ops() -> None: with assert_no_warnings(): diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index a7178bba512..c3574c979af 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -7815,7 +7815,10 @@ def test_query(self, backend, engine, parser) -> None: # pytest tests — new tests should go here, rather than in the class. -@pytest.mark.parametrize("test_elements", ([1, 2], np.array([1, 2]), DataArray([1, 2]))) +@pytest.mark.parametrize( + "test_elements", + ([1, 2], np.array([1, 2]), DataArray([1, 2]), {1, 2}, frozenset({1, 2})), +) def test_isin(test_elements, backend) -> None: expected = Dataset( data_vars={