Skip to content

Commit e1ca66b

Browse files
authored
API/BUG: make .at raise same exceptions as .loc (#31724)
1 parent e5c65bf commit e1ca66b

File tree

3 files changed

+62
-30
lines changed

3 files changed

+62
-30
lines changed

doc/source/whatsnew/v1.1.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ Backwards incompatible API changes
6363
- :meth:`DataFrameGroupby.mean` and :meth:`SeriesGroupby.mean` (and similarly for :meth:`~DataFrameGroupby.median`, :meth:`~DataFrameGroupby.std`` and :meth:`~DataFrameGroupby.var``)
6464
now raise a ``TypeError`` if a not-accepted keyword argument is passed into it.
6565
Previously a ``UnsupportedFunctionCall`` was raised (``AssertionError`` if ``min_count`` passed into :meth:`~DataFrameGroupby.median``) (:issue:`31485`)
66-
66+
- :meth:`DataFrame.at` and :meth:`Series.at` will raise a ``TypeError`` instead of a ``ValueError`` if an incompatible key is passed, and ``KeyError`` if a missing key is passed, matching the behavior of ``.loc[]`` (:issue:`31722`)
67+
-
6768

6869
.. ---------------------------------------------------------------------------
6970

pandas/core/indexing.py

+5-15
Original file line numberDiff line numberDiff line change
@@ -2087,21 +2087,11 @@ def _convert_key(self, key, is_setter: bool = False):
20872087
if is_setter:
20882088
return list(key)
20892089

2090-
for ax, i in zip(self.obj.axes, key):
2091-
if ax.is_integer():
2092-
if not is_integer(i):
2093-
raise ValueError(
2094-
"At based indexing on an integer index "
2095-
"can only have integer indexers"
2096-
)
2097-
else:
2098-
if is_integer(i) and not (ax.holds_integer() or ax.is_floating()):
2099-
raise ValueError(
2100-
"At based indexing on an non-integer "
2101-
"index can only have non-integer "
2102-
"indexers"
2103-
)
2104-
return key
2090+
lkey = list(key)
2091+
for n, (ax, i) in enumerate(zip(self.obj.axes, key)):
2092+
lkey[n] = ax._convert_scalar_indexer(i, kind="loc")
2093+
2094+
return tuple(lkey)
21052095

21062096

21072097
@Appender(IndexingMixin.iat.__doc__)

pandas/tests/indexing/test_scalar.py

+55-14
Original file line numberDiff line numberDiff line change
@@ -129,38 +129,79 @@ def test_imethods_with_dups(self):
129129
result = df.iat[2, 0]
130130
assert result == 2
131131

132-
def test_at_to_fail(self):
132+
def test_series_at_raises_type_error(self):
133133
# at should not fallback
134134
# GH 7814
135-
s = Series([1, 2, 3], index=list("abc"))
136-
result = s.at["a"]
135+
# GH#31724 .at should match .loc
136+
ser = Series([1, 2, 3], index=list("abc"))
137+
result = ser.at["a"]
137138
assert result == 1
139+
result = ser.loc["a"]
140+
assert result == 1
141+
138142
msg = (
139-
"At based indexing on an non-integer index can only have "
140-
"non-integer indexers"
143+
"cannot do label indexing on <class 'pandas.core.indexes.base.Index'> "
144+
r"with these indexers \[0\] of <class 'int'>"
141145
)
142-
with pytest.raises(ValueError, match=msg):
143-
s.at[0]
146+
with pytest.raises(TypeError, match=msg):
147+
ser.at[0]
148+
with pytest.raises(TypeError, match=msg):
149+
ser.loc[0]
144150

151+
def test_frame_raises_type_error(self):
152+
# GH#31724 .at should match .loc
145153
df = DataFrame({"A": [1, 2, 3]}, index=list("abc"))
146154
result = df.at["a", "A"]
147155
assert result == 1
148-
with pytest.raises(ValueError, match=msg):
156+
result = df.loc["a", "A"]
157+
assert result == 1
158+
159+
msg = (
160+
"cannot do label indexing on <class 'pandas.core.indexes.base.Index'> "
161+
r"with these indexers \[0\] of <class 'int'>"
162+
)
163+
with pytest.raises(TypeError, match=msg):
149164
df.at["a", 0]
165+
with pytest.raises(TypeError, match=msg):
166+
df.loc["a", 0]
167+
168+
def test_series_at_raises_key_error(self):
169+
# GH#31724 .at should match .loc
150170

151-
s = Series([1, 2, 3], index=[3, 2, 1])
152-
result = s.at[1]
171+
ser = Series([1, 2, 3], index=[3, 2, 1])
172+
result = ser.at[1]
153173
assert result == 3
154-
msg = "At based indexing on an integer index can only have integer indexers"
155-
with pytest.raises(ValueError, match=msg):
156-
s.at["a"]
174+
result = ser.loc[1]
175+
assert result == 3
176+
177+
with pytest.raises(KeyError, match="a"):
178+
ser.at["a"]
179+
with pytest.raises(KeyError, match="a"):
180+
# .at should match .loc
181+
ser.loc["a"]
182+
183+
def test_frame_at_raises_key_error(self):
184+
# GH#31724 .at should match .loc
157185

158186
df = DataFrame({0: [1, 2, 3]}, index=[3, 2, 1])
187+
159188
result = df.at[1, 0]
160189
assert result == 3
161-
with pytest.raises(ValueError, match=msg):
190+
result = df.loc[1, 0]
191+
assert result == 3
192+
193+
with pytest.raises(KeyError, match="a"):
162194
df.at["a", 0]
195+
with pytest.raises(KeyError, match="a"):
196+
df.loc["a", 0]
197+
198+
with pytest.raises(KeyError, match="a"):
199+
df.at[1, "a"]
200+
with pytest.raises(KeyError, match="a"):
201+
df.loc[1, "a"]
163202

203+
# TODO: belongs somewhere else?
204+
def test_getitem_list_missing_key(self):
164205
# GH 13822, incorrect error string with non-unique columns when missing
165206
# column is accessed
166207
df = DataFrame({"x": [1.0], "y": [2.0], "z": [3.0]})

0 commit comments

Comments
 (0)