diff --git a/xarray/core/indexes.py b/xarray/core/indexes.py index 2242e57e482..e4b8fe948f4 100644 --- a/xarray/core/indexes.py +++ b/xarray/core/indexes.py @@ -9,6 +9,7 @@ import numpy as np import pandas as pd +from pandas.errors import InvalidIndexError from xarray.core import formatting, nputils, utils from xarray.core.coordinate_transform import CoordinateTransform @@ -1367,7 +1368,14 @@ def sel(self, labels, method=None, tolerance=None) -> IndexSelResult: elif isinstance(label, tuple): if _is_nested_tuple(label): - indexer = self.index.get_locs(label) + # When the tuple contains sub-tuples/lists/slices, it could be: + # 1. A single key with tuple-valued levels (e.g., ((1, 1), 2)) + # 2. A multi-value selection (e.g., (1, slice(1, 2))) + # Try get_loc first for case 1, fall back to get_locs for case 2 + try: + indexer = self.index.get_loc(label) + except (InvalidIndexError, KeyError): + indexer = self.index.get_locs(label) elif len(label) == self.index.nlevels: indexer = self.index.get_loc(label) else: diff --git a/xarray/tests/test_indexes.py b/xarray/tests/test_indexes.py index 94adcc3b935..a43095c2711 100644 --- a/xarray/tests/test_indexes.py +++ b/xarray/tests/test_indexes.py @@ -559,6 +559,22 @@ def test_copy(self) -> None: assert actual.dim == expected.dim assert actual.level_coords_dtype == expected.level_coords_dtype + def test_sel_nested_tuple_key_collapses_dimension(self) -> None: + # Regression test for GH#11341 + # When indexing with a single nested tuple key (where one level has + # tuple-valued entries), the dimension should collapse like scalar selection + nested_level_0 = pd.Index( + [(1, 1), (1, 1), (2, 2), (3, 3)], name="a", tupleize_cols=False + ) + nested_level_1 = pd.Index([1, 2, 10, 20], name="b") + nested_mi = pd.MultiIndex.from_arrays([nested_level_0, nested_level_1]) + index = PandasMultiIndex(nested_mi, "index") + + # A single nested tuple key should produce a scalar indexer (int), + # not an array, collapsing the dimension + actual = index.sel({"index": ((1, 1), 2)}) + assert isinstance(actual.dim_indexers["index"], (int, np.integer)) + class TestIndexes: @pytest.fixture