Skip to content

Commit 55e8891

Browse files
authored
BUG: Multiindexed series .at fix (pandas-dev#32520)
1 parent 2740fb4 commit 55e8891

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ Indexing
741741
- Bug in :meth:`Series.__getitem__` allowing missing labels with ``np.ndarray``, :class:`Index`, :class:`Series` indexers but not ``list``, these now all raise ``KeyError`` (:issue:`33646`)
742742
- Bug in :meth:`DataFrame.truncate` and :meth:`Series.truncate` where index was assumed to be monotone increasing (:issue:`33756`)
743743
- Indexing with a list of strings representing datetimes failed on :class:`DatetimeIndex` or :class:`PeriodIndex`(:issue:`11278`)
744+
- Bug in :meth:`Series.at` when used with a :class:`MultiIndex` would raise an exception on valid inputs (:issue:`26989`)
744745

745746
Missing
746747
^^^^^^^

pandas/core/indexing.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -2016,10 +2016,10 @@ def __setitem__(self, key, value):
20162016

20172017
if not isinstance(key, tuple):
20182018
key = _tuplify(self.ndim, key)
2019+
key = list(self._convert_key(key, is_setter=True))
20192020
if len(key) != self.ndim:
20202021
raise ValueError("Not enough indexers for scalar access (setting)!")
20212022

2022-
key = list(self._convert_key(key, is_setter=True))
20232023
self.obj._set_value(*key, value=value, takeable=self._takeable)
20242024

20252025

@@ -2032,6 +2032,12 @@ def _convert_key(self, key, is_setter: bool = False):
20322032
Require they keys to be the same type as the index. (so we don't
20332033
fallback)
20342034
"""
2035+
# GH 26989
2036+
# For series, unpacking key needs to result in the label.
2037+
# This is already the case for len(key) == 1; e.g. (1,)
2038+
if self.ndim == 1 and len(key) > 1:
2039+
key = (key,)
2040+
20352041
# allow arbitrary setting
20362042
if is_setter:
20372043
return list(key)

pandas/tests/indexing/test_scalar.py

+62
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,65 @@ def test_iat_series_with_period_index():
351351
expected = ser[index[0]]
352352
result = ser.iat[0]
353353
assert expected == result
354+
355+
356+
def test_at_with_tuple_index_get():
357+
# GH 26989
358+
# DataFrame.at getter works with Index of tuples
359+
df = DataFrame({"a": [1, 2]}, index=[(1, 2), (3, 4)])
360+
assert df.index.nlevels == 1
361+
assert df.at[(1, 2), "a"] == 1
362+
363+
# Series.at getter works with Index of tuples
364+
series = df["a"]
365+
assert series.index.nlevels == 1
366+
assert series.at[(1, 2)] == 1
367+
368+
369+
def test_at_with_tuple_index_set():
370+
# GH 26989
371+
# DataFrame.at setter works with Index of tuples
372+
df = DataFrame({"a": [1, 2]}, index=[(1, 2), (3, 4)])
373+
assert df.index.nlevels == 1
374+
df.at[(1, 2), "a"] = 2
375+
assert df.at[(1, 2), "a"] == 2
376+
377+
# Series.at setter works with Index of tuples
378+
series = df["a"]
379+
assert series.index.nlevels == 1
380+
series.at[1, 2] = 3
381+
assert series.at[1, 2] == 3
382+
383+
384+
def test_multiindex_at_get():
385+
# GH 26989
386+
# DataFrame.at and DataFrame.loc getter works with MultiIndex
387+
df = DataFrame({"a": [1, 2]}, index=[[1, 2], [3, 4]])
388+
assert df.index.nlevels == 2
389+
assert df.at[(1, 3), "a"] == 1
390+
assert df.loc[(1, 3), "a"] == 1
391+
392+
# Series.at and Series.loc getter works with MultiIndex
393+
series = df["a"]
394+
assert series.index.nlevels == 2
395+
assert series.at[1, 3] == 1
396+
assert series.loc[1, 3] == 1
397+
398+
399+
def test_multiindex_at_set():
400+
# GH 26989
401+
# DataFrame.at and DataFrame.loc setter works with MultiIndex
402+
df = DataFrame({"a": [1, 2]}, index=[[1, 2], [3, 4]])
403+
assert df.index.nlevels == 2
404+
df.at[(1, 3), "a"] = 3
405+
assert df.at[(1, 3), "a"] == 3
406+
df.loc[(1, 3), "a"] = 4
407+
assert df.loc[(1, 3), "a"] == 4
408+
409+
# Series.at and Series.loc setter works with MultiIndex
410+
series = df["a"]
411+
assert series.index.nlevels == 2
412+
series.at[1, 3] = 5
413+
assert series.at[1, 3] == 5
414+
series.loc[1, 3] = 6
415+
assert series.loc[1, 3] == 6

0 commit comments

Comments
 (0)