From 09cc7f46f7a95e0ca5ecf3d45fbcb9cc036bdd58 Mon Sep 17 00:00:00 2001 From: phofl Date: Sat, 23 Jan 2021 22:35:27 +0100 Subject: [PATCH 1/7] BUG: Series.setitem raising ValueError when setting Series with scalar indexer --- doc/source/whatsnew/v1.3.0.rst | 1 + pandas/core/indexing.py | 3 +++ pandas/tests/series/indexing/test_setitem.py | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 381a05a18b278..fa0e0c1c48d3d 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -287,6 +287,7 @@ Indexing - Bug in :meth:`DataFrame.__setitem__` raising ``ValueError`` with empty :class:`DataFrame` and specified columns for string indexer and non empty :class:`DataFrame` to set (:issue:`38831`) - Bug in :meth:`DataFrame.loc.__setitem__` raising ValueError when expanding unique column for :class:`DataFrame` with duplicate columns (:issue:`38521`) - Bug in :meth:`DataFrame.iloc.__setitem__` and :meth:`DataFrame.loc.__setitem__` with mixed dtypes when setting with a dictionary value (:issue:`38335`) +- Bug in :meth:`Series.__setitem__` raising ``ValueError`` when setting a :class:`Series` with a scalar indexer (:issue:`38303`) - Bug in :meth:`DataFrame.loc` dropping levels of :class:`MultiIndex` when :class:`DataFrame` used as input has only one row (:issue:`10521`) - Bug in setting ``timedelta64`` values into numeric :class:`Series` failing to cast to object dtype (:issue:`39086`) - Bug in setting :class:`Interval` values into a :class:`Series` or :class:`DataFrame` with mismatched :class:`IntervalDtype` incorrectly casting the new values to the existing dtype (:issue:`39120`) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 998a34ce834ea..e7b200155fd47 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2023,6 +2023,9 @@ def ravel(i): return ser._values.copy() return ser.reindex(ax)._values + elif is_scalar(indexer) and isinstance(self.obj, ABCSeries): + return ser + elif is_scalar(indexer): ax = self.obj._get_axis(1) diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py index 7f469f361fec7..34e714bae7da8 100644 --- a/pandas/tests/series/indexing/test_setitem.py +++ b/pandas/tests/series/indexing/test_setitem.py @@ -93,6 +93,13 @@ def test_setitem_negative_out_of_bounds(self): with pytest.raises(IndexError, match=msg): ser[-11] = "foo" + def test_setitem_series(self): + # GH#38303 + ser = Series(0, index=[0, 1, 2]) + ser[0] = Series([42]) + expected = Series([42, 0, 0]) + tm.assert_series_equal(ser, expected) + class TestSetitemSlices: def test_setitem_slice_float_raises(self, datetime_series): From 2cb7a7b9e97a88d9e0cb93408a3887dca82b3a61 Mon Sep 17 00:00:00 2001 From: phofl Date: Sat, 23 Jan 2021 23:02:37 +0100 Subject: [PATCH 2/7] Handle non object dtype series --- pandas/core/indexing.py | 9 ++++++++- pandas/tests/series/indexing/test_setitem.py | 17 +++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index e7b200155fd47..f85adb2a931b7 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2024,7 +2024,14 @@ def ravel(i): return ser.reindex(ax)._values elif is_scalar(indexer) and isinstance(self.obj, ABCSeries): - return ser + if is_object_dtype(self.obj): + return ser + ax = self.obj._get_axis(0) + + if ser.index.equals(ax): + return ser._values.copy() + + return ser.reindex(ax)._values[[indexer]] elif is_scalar(indexer): ax = self.obj._get_axis(1) diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py index 34e714bae7da8..c1301513bf532 100644 --- a/pandas/tests/series/indexing/test_setitem.py +++ b/pandas/tests/series/indexing/test_setitem.py @@ -93,11 +93,20 @@ def test_setitem_negative_out_of_bounds(self): with pytest.raises(IndexError, match=msg): ser[-11] = "foo" - def test_setitem_series(self): + @pytest.mark.parametrize("ser_index", [0, 1]) + def test_setitem_series_object_dtype(self, ser_index): # GH#38303 - ser = Series(0, index=[0, 1, 2]) - ser[0] = Series([42]) - expected = Series([42, 0, 0]) + ser = Series([0, 0], dtype="object") + ser.loc[0] = Series([42], index=[ser_index]) + expected = Series([Series([42], index=[ser_index]), 0], dtype="object") + tm.assert_series_equal(ser, expected) + + @pytest.mark.parametrize("index, exp_value", [(0, 42.0), (1, np.nan)]) + def test_setitem_series(self, index, exp_value): + # GH#38303 + ser = Series([0, 0]) + ser.loc[0] = Series([42], index=[index]) + expected = Series([exp_value, 0]) tm.assert_series_equal(ser, expected) From 78450b91f43d6954790e62bb65add8fa60e36385 Mon Sep 17 00:00:00 2001 From: phofl Date: Sat, 23 Jan 2021 23:06:29 +0100 Subject: [PATCH 3/7] Remove brackets --- pandas/core/indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index f85adb2a931b7..7908c5494222e 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2031,7 +2031,7 @@ def ravel(i): if ser.index.equals(ax): return ser._values.copy() - return ser.reindex(ax)._values[[indexer]] + return ser.reindex(ax)._values[indexer] elif is_scalar(indexer): ax = self.obj._get_axis(1) From 33845f4262206d1c4775b7e4336c307f69a670c0 Mon Sep 17 00:00:00 2001 From: phofl Date: Mon, 25 Jan 2021 23:41:57 +0100 Subject: [PATCH 4/7] Improve performance --- pandas/core/indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 7908c5494222e..2126dc13eb9f6 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2023,7 +2023,7 @@ def ravel(i): return ser._values.copy() return ser.reindex(ax)._values - elif is_scalar(indexer) and isinstance(self.obj, ABCSeries): + elif is_scalar(indexer) and self.ndim == 1: if is_object_dtype(self.obj): return ser ax = self.obj._get_axis(0) From 9e7a46a1f1425a93148f5a2d447cdffcdb0080f7 Mon Sep 17 00:00:00 2001 From: phofl Date: Mon, 25 Jan 2021 23:46:01 +0100 Subject: [PATCH 5/7] Use is integer --- pandas/core/indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 2126dc13eb9f6..ed345f65d8c13 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2023,7 +2023,7 @@ def ravel(i): return ser._values.copy() return ser.reindex(ax)._values - elif is_scalar(indexer) and self.ndim == 1: + elif is_integer(indexer) and self.ndim == 1: if is_object_dtype(self.obj): return ser ax = self.obj._get_axis(0) From 38a66e934da6b4f71aa727bc2a1122d47f99873f Mon Sep 17 00:00:00 2001 From: phofl Date: Tue, 26 Jan 2021 20:26:01 +0100 Subject: [PATCH 6/7] Parametrize --- pandas/tests/series/indexing/test_setitem.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py index c1301513bf532..c53e2ba31aba0 100644 --- a/pandas/tests/series/indexing/test_setitem.py +++ b/pandas/tests/series/indexing/test_setitem.py @@ -93,11 +93,13 @@ def test_setitem_negative_out_of_bounds(self): with pytest.raises(IndexError, match=msg): ser[-11] = "foo" + @pytest.mark.parametrize("indexer", [tm.loc, tm.at]) @pytest.mark.parametrize("ser_index", [0, 1]) - def test_setitem_series_object_dtype(self, ser_index): + def test_setitem_series_object_dtype(self, indexer, ser_index): # GH#38303 ser = Series([0, 0], dtype="object") - ser.loc[0] = Series([42], index=[ser_index]) + idxr = indexer(ser) + idxr[0] = Series([42], index=[ser_index]) expected = Series([Series([42], index=[ser_index]), 0], dtype="object") tm.assert_series_equal(ser, expected) From 371f88f96036bfbf52b3c255f0c68325b82234a7 Mon Sep 17 00:00:00 2001 From: phofl Date: Wed, 27 Jan 2021 20:34:36 +0100 Subject: [PATCH 7/7] Replace is_scalar --- pandas/core/indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index ed345f65d8c13..d7ff34ac774fe 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2033,7 +2033,7 @@ def ravel(i): return ser.reindex(ax)._values[indexer] - elif is_scalar(indexer): + elif is_integer(indexer): ax = self.obj._get_axis(1) if ser.index.equals(ax):