Skip to content

Commit 75b25b3

Browse files
authored
Bug in xs raising KeyError for MultiIndex columns with droplevel False and list indexe (#41789)
1 parent 8f24700 commit 75b25b3

File tree

5 files changed

+43
-3
lines changed

5 files changed

+43
-3
lines changed

doc/source/whatsnew/v1.2.5.rst

+7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ Fixed regressions
2121

2222
.. ---------------------------------------------------------------------------
2323
24+
.. _whatsnew_125.deprecations:
25+
26+
Deprecations
27+
~~~~~~~~~~~~
28+
29+
- Deprecated passing lists as ``key`` to :meth:`DataFrame.xs` and :meth:`Series.xs` (:issue:`41760`)
30+
2431
.. _whatsnew_125.bug_fixes:
2532

2633
Bug fixes

pandas/core/generic.py

+9
Original file line numberDiff line numberDiff line change
@@ -3756,6 +3756,15 @@ class animal locomotion
37563756
"""
37573757
axis = self._get_axis_number(axis)
37583758
labels = self._get_axis(axis)
3759+
3760+
if isinstance(key, list):
3761+
warnings.warn(
3762+
"Passing lists as key for xs is deprecated and will be removed in a "
3763+
"future version. Pass key as a tuple instead.",
3764+
FutureWarning,
3765+
stacklevel=2,
3766+
)
3767+
37593768
if level is not None:
37603769
if not isinstance(labels, MultiIndex):
37613770
raise TypeError("Index must be a MultiIndex")

pandas/tests/frame/indexing/test_xs.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ def test_xs_keep_level(self):
106106
expected = df[:1]
107107
tm.assert_frame_equal(result, expected)
108108

109-
result = df.xs([2008, "sat"], level=["year", "day"], drop_level=False)
109+
with tm.assert_produces_warning(FutureWarning):
110+
result = df.xs([2008, "sat"], level=["year", "day"], drop_level=False)
110111
tm.assert_frame_equal(result, expected)
111112

112113
def test_xs_view(self, using_array_manager):
@@ -187,7 +188,11 @@ def test_xs_with_duplicates(self, key, level, multiindex_dataframe_random_data):
187188
assert df.index.is_unique is False
188189
expected = concat([frame.xs("one", level="second")] * 2)
189190

190-
result = df.xs(key, level=level)
191+
if isinstance(key, list):
192+
with tm.assert_produces_warning(FutureWarning):
193+
result = df.xs(key, level=level)
194+
else:
195+
result = df.xs(key, level=level)
191196
tm.assert_frame_equal(result, expected)
192197

193198
def test_xs_missing_values_in_index(self):
@@ -358,3 +363,11 @@ def test_xs_droplevel_false_view(self, using_array_manager):
358363
df.iloc[0, 0] = 2
359364
expected = DataFrame({"a": [1]})
360365
tm.assert_frame_equal(result, expected)
366+
367+
def test_xs_list_indexer_droplevel_false(self):
368+
# GH#41760
369+
mi = MultiIndex.from_tuples([("x", "m", "a"), ("x", "n", "b"), ("y", "o", "c")])
370+
df = DataFrame([[1, 2, 3], [4, 5, 6]], columns=mi)
371+
with tm.assert_produces_warning(FutureWarning):
372+
with pytest.raises(KeyError, match="y"):
373+
df.xs(["x", "y"], drop_level=False, axis=1)

pandas/tests/indexing/multiindex/test_partial.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ def test_xs_partial(
6969
)
7070
df = DataFrame(np.random.randn(8, 4), index=index, columns=list("abcd"))
7171

72-
result = df.xs(["foo", "one"])
72+
with tm.assert_produces_warning(FutureWarning):
73+
result = df.xs(["foo", "one"])
7374
expected = df.loc["foo", "one"]
7475
tm.assert_frame_equal(result, expected)
7576

pandas/tests/series/indexing/test_xs.py

+10
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,13 @@ def test_series_xs_droplevel_false(self):
6969
),
7070
)
7171
tm.assert_series_equal(result, expected)
72+
73+
def test_xs_key_as_list(self):
74+
# GH#41760
75+
mi = MultiIndex.from_tuples([("a", "x")], names=["level1", "level2"])
76+
ser = Series([1], index=mi)
77+
with tm.assert_produces_warning(FutureWarning):
78+
ser.xs(["a", "x"], axis=0, drop_level=False)
79+
80+
with tm.assert_produces_warning(FutureWarning):
81+
ser.xs(["a"], axis=0, drop_level=False)

0 commit comments

Comments
 (0)