Skip to content

Commit 61b0e25

Browse files
jbrockmendeljreback
authored andcommitted
BUG: Series.loc with MultiIndex whose first level is all-NaN (pandas-dev#42335)
Co-authored-by: Jeff Reback <[email protected]>
1 parent b9cfae1 commit 61b0e25

File tree

3 files changed

+27
-7
lines changed

3 files changed

+27
-7
lines changed

doc/source/whatsnew/v1.4.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,9 @@ Interval
216216

217217
Indexing
218218
^^^^^^^^
219-
- Bug in indexing on a :class:`Series` or :class:`DataFrame` with a :class:`DatetimeIndex` when passing a string, the return type depended on whether the index was monotonic (:issue:`24892`)
220219
- Bug in :meth:`DataFrame.truncate` and :meth:`Series.truncate` when the object's Index has a length greater than one but only one unique value (:issue:`42365`)
220+
- Bug in :meth:`Series.loc` when with a :class:`MultiIndex` whose first level contains only ``np.nan`` values (:issue:`42055`)
221+
- Bug in indexing on a :class:`Series` or :class:`DataFrame` with a :class:`DatetimeIndex` when passing a string, the return type depended on whether the index was monotonic (:issue:`24892`)
221222
- Bug in indexing on a :class:`MultiIndex` failing to drop scalar levels when the indexer is a tuple containing a datetime-like string (:issue:`42476`)
222223
-
223224

pandas/core/indexes/base.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -1886,14 +1886,21 @@ def _drop_level_numbers(self, levnums: list[int]):
18861886
new_names.pop(i)
18871887

18881888
if len(new_levels) == 1:
1889+
lev = new_levels[0]
18891890

1890-
# set nan if needed
1891-
mask = new_codes[0] == -1
1892-
result = new_levels[0].take(new_codes[0])
1893-
if mask.any():
1894-
result = result.putmask(mask, np.nan)
1891+
if len(lev) == 0:
1892+
# If lev is empty, lev.take will fail GH#42055
1893+
res_values = algos.take(lev._values, new_codes[0], allow_fill=True)
1894+
result = type(lev)._simple_new(res_values, name=new_names[0])
1895+
else:
1896+
# set nan if needed
1897+
mask = new_codes[0] == -1
1898+
result = new_levels[0].take(new_codes[0])
1899+
if mask.any():
1900+
result = result.putmask(mask, np.nan)
1901+
1902+
result._name = new_names[0]
18951903

1896-
result._name = new_names[0]
18971904
return result
18981905
else:
18991906
from pandas.core.indexes.multi import MultiIndex

pandas/tests/indexing/test_loc.py

+12
Original file line numberDiff line numberDiff line change
@@ -1663,6 +1663,18 @@ def test_loc_multiindex_levels_contain_values_not_in_index_anymore(self, lt_valu
16631663
with pytest.raises(KeyError, match=r"\['b'\] not in index"):
16641664
df.loc[df["a"] < lt_value, :].loc[["b"], :]
16651665

1666+
def test_loc_multiindex_null_slice_na_level(self):
1667+
# GH#42055
1668+
lev1 = np.array([np.nan, np.nan])
1669+
lev2 = ["bar", "baz"]
1670+
mi = MultiIndex.from_arrays([lev1, lev2])
1671+
ser = Series([0, 1], index=mi)
1672+
result = ser.loc[:, "bar"]
1673+
1674+
# TODO: should we have name="bar"?
1675+
expected = Series([0], index=[np.nan])
1676+
tm.assert_series_equal(result, expected)
1677+
16661678
def test_loc_drops_level(self):
16671679
# Based on test_series_varied_multiindex_alignment, where
16681680
# this used to fail to drop the first level

0 commit comments

Comments
 (0)