From f7dad6d9329114506054fa2955b6720d2030300a Mon Sep 17 00:00:00 2001 From: Johannes Mueller Date: Fri, 2 Sep 2022 12:17:18 +0200 Subject: [PATCH] Fix 48224: DataFrame.at must fail when adding more than one value The problem occurred because when adding a new column to a DataFrame using DataFrame.at, DataFrame._set_value falls back to DataFrame.loc before the InvalidIndexError is raised. The proposed solution makes sure that both, the column index and the row index are determined so that both have a chance to raise. --- doc/source/whatsnew/v1.6.0.rst | 41 +++++++++++++++++++++++++++++--- pandas/core/frame.py | 16 +++++++++++-- pandas/tests/indexing/test_at.py | 7 ++++++ 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v1.6.0.rst b/doc/source/whatsnew/v1.6.0.rst index 4b7f136be3c16..03b3acbae6eb3 100644 --- a/doc/source/whatsnew/v1.6.0.rst +++ b/doc/source/whatsnew/v1.6.0.rst @@ -39,10 +39,45 @@ Notable bug fixes These are bug fixes that might have notable behavior changes. -.. _whatsnew_160.notable_bug_fixes.notable_bug_fix1: +.. _whatsnew_160.notable_bug_fixes.notable_bug_frame_at_new_column_with_slice: + +Using :meth:`DataFrame.at` raises when trying to add a new column with multiple values +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +*Old behavior* + +.. code-block:: ipython + + In [1]: import pandas as pd + + In [2]: df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) + + In [3]: # should raise InvalidIndexError + df.at[slice(0, 1, None), 'c'] = 7 + + In [4]: df + Out[4]: + a b c + 0 1 4 7.0 + 1 2 5 7.0 + 2 3 6 NaN + + +*New behavior* + +.. code-block:: ipython + + In [1]: import pandas as pd + + In [2]: df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) + + In [3]: df.at[slice(0, 1, None), 'c'] = 7 + + --------------------------------------------------------------------------- + InvalidIndexError Traceback (most recent call last) + in + ----> 1 df.at[slice(0, 1, None), 'c'] = 7 -notable_bug_fix1 -^^^^^^^^^^^^^^^^ .. _whatsnew_160.notable_bug_fixes.notable_bug_fix2: diff --git a/pandas/core/frame.py b/pandas/core/frame.py index b20decab26928..caaaa309ae541 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4206,8 +4206,20 @@ def _set_value( icol = col iindex = cast(int, index) else: - icol = self.columns.get_loc(col) - iindex = self.index.get_loc(index) + exception = None + try: + icol = self.columns.get_loc(col) + except (KeyError, TypeError, ValueError) as e: + exception = e + + try: + iindex = self.index.get_loc(index) + except (KeyError, TypeError, ValueError) as e: + exception = e + + if exception is not None: + raise exception + self._mgr.column_setitem(icol, iindex, value) self._clear_item_cache() diff --git a/pandas/tests/indexing/test_at.py b/pandas/tests/indexing/test_at.py index f6d2fb12c5d81..2527838209ded 100644 --- a/pandas/tests/indexing/test_at.py +++ b/pandas/tests/indexing/test_at.py @@ -214,6 +214,13 @@ def test_at_frame_multiple_columns(self): with pytest.raises(InvalidIndexError, match=r"slice\(None, None, None\)"): df.at[5] = [6, 7] + def test_at_frame_new_column_when_index_is_slice(self): + # GH-48224 + df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) + + with pytest.raises(InvalidIndexError, match=r"slice\(0, 1, None\)"): + df.at[slice(0, 1, None), "c"] = 7 + def test_at_getitem_mixed_index_no_fallback(self): # GH#19860 ser = Series([1, 2, 3, 4, 5], index=["a", "b", "c", 1, 2])