Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/elyall>`_.
- :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 <https://github.com/NoiceHax>`_.


Documentation
Expand Down
7 changes: 7 additions & 0 deletions xarray/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions xarray/tests/test_dataarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
5 changes: 4 additions & 1 deletion xarray/tests/test_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -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={
Expand Down
Loading