From 668b0e1ea6a65078e19702866940f481e2ea11cb Mon Sep 17 00:00:00 2001 From: Andrew Wieteska Date: Fri, 20 Nov 2020 21:25:43 -0500 Subject: [PATCH 01/12] BUG: update dtype check in _inplace_method --- pandas/core/generic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 3aa692c5d3d43..e2b3406c6b1c5 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -70,6 +70,7 @@ is_datetime64_any_dtype, is_datetime64tz_dtype, is_dict_like, + is_dtype_equal, is_extension_array_dtype, is_float, is_list_like, @@ -11266,7 +11267,11 @@ def _inplace_method(self, other, op): """ result = op(self, other) - if self.ndim == 1 and result._indexed_same(self) and result.dtype == self.dtype: + if ( + self.ndim == 1 + and result._indexed_same(self) + and is_dtype_equal(result.dtype, self.dtype) + ): # GH#36498 this inplace op can _actually_ be inplace. self._values[:] = result._values return self From f0938f600717527579f4e8c72fe8039cc5b89efd Mon Sep 17 00:00:00 2001 From: Andrew Wieteska Date: Fri, 20 Nov 2020 21:56:27 -0500 Subject: [PATCH 02/12] TST: add tests for inplace ops --- .../tests/series/methods/test_inplace_ops.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 pandas/tests/series/methods/test_inplace_ops.py diff --git a/pandas/tests/series/methods/test_inplace_ops.py b/pandas/tests/series/methods/test_inplace_ops.py new file mode 100644 index 0000000000000..2ae2cb60e3bcf --- /dev/null +++ b/pandas/tests/series/methods/test_inplace_ops.py @@ -0,0 +1,60 @@ +import pytest + +from pandas import Series +import pandas._testing as tm + + +@pytest.mark.parametrize( + "ser1, ser2, expected_add, expected_sub, expected_mul", + ( + [ + Series([1], dtype="Int64"), + Series([2], dtype="Int64"), + Series([3], dtype="Int64"), + Series([-1], dtype="Int64"), + Series([2], dtype="Int64"), + ], + [ + Series([1], dtype="float"), + Series([2.0], dtype="float"), + Series([3.0], dtype="float"), + Series([-1.0], dtype="float"), + Series([2.0], dtype="float"), + ], + [ + Series([1], dtype="Int64"), + Series([2.0], dtype="float"), + Series([3.0], dtype="float"), + Series([-1.0], dtype="float"), + Series([2], dtype="float"), + ], + [ + Series([1.0], dtype="float"), + Series([2], dtype="Int64"), + Series([3.0], dtype="float"), + Series([-1.0], dtype="float"), + Series([2], dtype="float"), + ], + pytest.param( + Series([1], dtype="Int64"), + Series([2.0], dtype="Float64"), + Series([3.0], dtype="Float64"), + Series([-1.0], dtype="Float64"), + Series([2], dtype="Float64"), + marks=pytest.mark.xfail(reason="Not implemented yet"), + ), + ), +) +def test_series_inplace_ops(ser1, ser2, expected_add, expected_sub, expected_mul): + + res = ser1.copy() + res += ser2 + tm.assert_series_equal(res, expected_add) + + res = ser1.copy() + res -= ser2 + tm.assert_series_equal(res, expected_sub) + + res = ser1.copy() + res *= ser2 + tm.assert_series_equal(res, expected_mul) From 2eda0c61ae8f23e13417d420415ffc349dc6fb55 Mon Sep 17 00:00:00 2001 From: Andrew Wieteska Date: Fri, 20 Nov 2020 21:25:43 -0500 Subject: [PATCH 03/12] BUG: update dtype check in _inplace_method --- pandas/core/generic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 3aa692c5d3d43..e2b3406c6b1c5 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -70,6 +70,7 @@ is_datetime64_any_dtype, is_datetime64tz_dtype, is_dict_like, + is_dtype_equal, is_extension_array_dtype, is_float, is_list_like, @@ -11266,7 +11267,11 @@ def _inplace_method(self, other, op): """ result = op(self, other) - if self.ndim == 1 and result._indexed_same(self) and result.dtype == self.dtype: + if ( + self.ndim == 1 + and result._indexed_same(self) + and is_dtype_equal(result.dtype, self.dtype) + ): # GH#36498 this inplace op can _actually_ be inplace. self._values[:] = result._values return self From 4107a96481bbff745e8f575747d01b99a71dd495 Mon Sep 17 00:00:00 2001 From: Andrew Wieteska Date: Fri, 20 Nov 2020 21:56:27 -0500 Subject: [PATCH 04/12] TST: add tests for inplace ops --- .../tests/series/methods/test_inplace_ops.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 pandas/tests/series/methods/test_inplace_ops.py diff --git a/pandas/tests/series/methods/test_inplace_ops.py b/pandas/tests/series/methods/test_inplace_ops.py new file mode 100644 index 0000000000000..2ae2cb60e3bcf --- /dev/null +++ b/pandas/tests/series/methods/test_inplace_ops.py @@ -0,0 +1,60 @@ +import pytest + +from pandas import Series +import pandas._testing as tm + + +@pytest.mark.parametrize( + "ser1, ser2, expected_add, expected_sub, expected_mul", + ( + [ + Series([1], dtype="Int64"), + Series([2], dtype="Int64"), + Series([3], dtype="Int64"), + Series([-1], dtype="Int64"), + Series([2], dtype="Int64"), + ], + [ + Series([1], dtype="float"), + Series([2.0], dtype="float"), + Series([3.0], dtype="float"), + Series([-1.0], dtype="float"), + Series([2.0], dtype="float"), + ], + [ + Series([1], dtype="Int64"), + Series([2.0], dtype="float"), + Series([3.0], dtype="float"), + Series([-1.0], dtype="float"), + Series([2], dtype="float"), + ], + [ + Series([1.0], dtype="float"), + Series([2], dtype="Int64"), + Series([3.0], dtype="float"), + Series([-1.0], dtype="float"), + Series([2], dtype="float"), + ], + pytest.param( + Series([1], dtype="Int64"), + Series([2.0], dtype="Float64"), + Series([3.0], dtype="Float64"), + Series([-1.0], dtype="Float64"), + Series([2], dtype="Float64"), + marks=pytest.mark.xfail(reason="Not implemented yet"), + ), + ), +) +def test_series_inplace_ops(ser1, ser2, expected_add, expected_sub, expected_mul): + + res = ser1.copy() + res += ser2 + tm.assert_series_equal(res, expected_add) + + res = ser1.copy() + res -= ser2 + tm.assert_series_equal(res, expected_sub) + + res = ser1.copy() + res *= ser2 + tm.assert_series_equal(res, expected_mul) From bba3c5dba1a00a5c5b588bcd048055333a359e25 Mon Sep 17 00:00:00 2001 From: arw2019 Date: Sat, 21 Nov 2020 16:52:51 +0000 Subject: [PATCH 05/12] DOC: whatsnew --- doc/source/whatsnew/v1.1.5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.5.rst b/doc/source/whatsnew/v1.1.5.rst index 323342cb43950..9b42acdbe95fd 100644 --- a/doc/source/whatsnew/v1.1.5.rst +++ b/doc/source/whatsnew/v1.1.5.rst @@ -17,7 +17,7 @@ Fixed regressions - Regression in addition of a timedelta-like scalar to a :class:`DatetimeIndex` raising incorrectly (:issue:`37295`) - Fixed regression in :meth:`Series.groupby` raising when the :class:`Index` of the :class:`Series` had a tuple as its name (:issue:`37755`) - Fixed regression in :meth:`DataFrame.loc` and :meth:`Series.loc` for ``__setitem__`` when one-dimensional tuple was given to select from :class:`MultiIndex` (:issue:`37711`) -- +- Fixed regression in inplace operations on :class:``ExtensionArray`` with NumPy array argument (:issue:`37910`) .. --------------------------------------------------------------------------- From 0af5e5906c7f0efa533d22fe7c8b45ee315056a8 Mon Sep 17 00:00:00 2001 From: arw2019 Date: Sat, 21 Nov 2020 17:20:54 +0000 Subject: [PATCH 06/12] feedback: reparametrize tests --- .../tests/series/methods/test_inplace_ops.py | 64 ++++++------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/pandas/tests/series/methods/test_inplace_ops.py b/pandas/tests/series/methods/test_inplace_ops.py index 2ae2cb60e3bcf..1904b0236eeb5 100644 --- a/pandas/tests/series/methods/test_inplace_ops.py +++ b/pandas/tests/series/methods/test_inplace_ops.py @@ -5,56 +5,32 @@ @pytest.mark.parametrize( - "ser1, ser2, expected_add, expected_sub, expected_mul", + "dtype1, dtype2, dtype_expected, dtype_mul", ( - [ - Series([1], dtype="Int64"), - Series([2], dtype="Int64"), - Series([3], dtype="Int64"), - Series([-1], dtype="Int64"), - Series([2], dtype="Int64"), - ], - [ - Series([1], dtype="float"), - Series([2.0], dtype="float"), - Series([3.0], dtype="float"), - Series([-1.0], dtype="float"), - Series([2.0], dtype="float"), - ], - [ - Series([1], dtype="Int64"), - Series([2.0], dtype="float"), - Series([3.0], dtype="float"), - Series([-1.0], dtype="float"), - Series([2], dtype="float"), - ], - [ - Series([1.0], dtype="float"), - Series([2], dtype="Int64"), - Series([3.0], dtype="float"), - Series([-1.0], dtype="float"), - Series([2], dtype="float"), - ], + ["Int64"] * 4, + ["float"] * 4, + ["Int64"] + ["float"] * 3, pytest.param( - Series([1], dtype="Int64"), - Series([2.0], dtype="Float64"), - Series([3.0], dtype="Float64"), - Series([-1.0], dtype="Float64"), - Series([2], dtype="Float64"), + "Int64", + "Float64", + "Float64", + "Float64", marks=pytest.mark.xfail(reason="Not implemented yet"), ), ), ) -def test_series_inplace_ops(ser1, ser2, expected_add, expected_sub, expected_mul): +def test_series_inplace_ops(dtype1, dtype2, dtype_expected, dtype_mul): - res = ser1.copy() - res += ser2 - tm.assert_series_equal(res, expected_add) + ser1 = Series([1], dtype=dtype1) + ser2 = Series([2], dtype=dtype2) + ser1 += ser2 + expected = Series([3], dtype=dtype_expected) + tm.assert_series_equal(ser1, expected) - res = ser1.copy() - res -= ser2 - tm.assert_series_equal(res, expected_sub) + ser1 -= ser2 + expected = Series([1], dtype=dtype_expected) + tm.assert_series_equal(ser1, expected) - res = ser1.copy() - res *= ser2 - tm.assert_series_equal(res, expected_mul) + ser1 *= ser2 + expected = Series([2], dtype=dtype_mul) + tm.assert_series_equal(ser1, expected) From 474cd73dd515e11a7370d6c843a2d1879c228fe6 Mon Sep 17 00:00:00 2001 From: arw2019 Date: Sun, 22 Nov 2020 04:41:41 +0000 Subject: [PATCH 07/12] GH ref --- pandas/tests/series/methods/test_inplace_ops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/tests/series/methods/test_inplace_ops.py b/pandas/tests/series/methods/test_inplace_ops.py index 1904b0236eeb5..e9c227bfc0996 100644 --- a/pandas/tests/series/methods/test_inplace_ops.py +++ b/pandas/tests/series/methods/test_inplace_ops.py @@ -20,6 +20,7 @@ ), ) def test_series_inplace_ops(dtype1, dtype2, dtype_expected, dtype_mul): + # GH 37910 ser1 = Series([1], dtype=dtype1) ser2 = Series([2], dtype=dtype2) From 383b936aabfc59d0bb93c70acfde517d18398eb2 Mon Sep 17 00:00:00 2001 From: arw2019 Date: Sun, 22 Nov 2020 04:46:04 +0000 Subject: [PATCH 08/12] feedback: move test to test_arithmetic --- pandas/tests/series/test_arithmetic.py | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 6aad2cadf78ba..dbeffb70ce775 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -832,6 +832,40 @@ def test_scalarop_preserve_name(self, datetime_series): assert result.name == datetime_series.name +class TestInplaceOperations: + @pytest.mark.parametrize( + "dtype1, dtype2, dtype_expected, dtype_mul", + ( + ["Int64"] * 4, + ["float"] * 4, + ["Int64"] + ["float"] * 3, + pytest.param( + "Int64", + "Float64", + "Float64", + "Float64", + marks=pytest.mark.xfail(reason="Not implemented yet"), + ), + ), + ) + def test_series_inplace_ops(self, dtype1, dtype2, dtype_expected, dtype_mul): + # GH 37910 + + ser1 = Series([1], dtype=dtype1) + ser2 = Series([2], dtype=dtype2) + ser1 += ser2 + expected = Series([3], dtype=dtype_expected) + tm.assert_series_equal(ser1, expected) + + ser1 -= ser2 + expected = Series([1], dtype=dtype_expected) + tm.assert_series_equal(ser1, expected) + + ser1 *= ser2 + expected = Series([2], dtype=dtype_mul) + tm.assert_series_equal(ser1, expected) + + def test_none_comparison(series_with_simple_index): series = series_with_simple_index if isinstance(series.index, IntervalIndex): From 3be07698f1c952ac5ad14313885274314ec0d2a5 Mon Sep 17 00:00:00 2001 From: arw2019 Date: Sun, 22 Nov 2020 15:44:11 +0000 Subject: [PATCH 09/12] remove tests from old location --- .../tests/series/methods/test_inplace_ops.py | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 pandas/tests/series/methods/test_inplace_ops.py diff --git a/pandas/tests/series/methods/test_inplace_ops.py b/pandas/tests/series/methods/test_inplace_ops.py deleted file mode 100644 index e9c227bfc0996..0000000000000 --- a/pandas/tests/series/methods/test_inplace_ops.py +++ /dev/null @@ -1,37 +0,0 @@ -import pytest - -from pandas import Series -import pandas._testing as tm - - -@pytest.mark.parametrize( - "dtype1, dtype2, dtype_expected, dtype_mul", - ( - ["Int64"] * 4, - ["float"] * 4, - ["Int64"] + ["float"] * 3, - pytest.param( - "Int64", - "Float64", - "Float64", - "Float64", - marks=pytest.mark.xfail(reason="Not implemented yet"), - ), - ), -) -def test_series_inplace_ops(dtype1, dtype2, dtype_expected, dtype_mul): - # GH 37910 - - ser1 = Series([1], dtype=dtype1) - ser2 = Series([2], dtype=dtype2) - ser1 += ser2 - expected = Series([3], dtype=dtype_expected) - tm.assert_series_equal(ser1, expected) - - ser1 -= ser2 - expected = Series([1], dtype=dtype_expected) - tm.assert_series_equal(ser1, expected) - - ser1 *= ser2 - expected = Series([2], dtype=dtype_mul) - tm.assert_series_equal(ser1, expected) From 8b18f50647345a45579de6c33aa0aa12220cf65d Mon Sep 17 00:00:00 2001 From: Andrew Wieteska Date: Mon, 23 Nov 2020 11:17:18 -0500 Subject: [PATCH 10/12] review comment: use tuples in test parametrization --- pandas/tests/series/test_arithmetic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index dbeffb70ce775..c5196cea5d3bb 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -836,9 +836,9 @@ class TestInplaceOperations: @pytest.mark.parametrize( "dtype1, dtype2, dtype_expected, dtype_mul", ( - ["Int64"] * 4, - ["float"] * 4, - ["Int64"] + ["float"] * 3, + ("Int64", "Int64", "Int64", "Int64"), + ("float", "float", "float", "float"), + ("Int64", "float", "float", "float"), pytest.param( "Int64", "Float64", From 40170b6b43fdc31eda345a2d26dbec5b524e7641 Mon Sep 17 00:00:00 2001 From: Andrew Wieteska Date: Mon, 23 Nov 2020 11:21:37 -0500 Subject: [PATCH 11/12] review comment: whatsnew --- doc/source/whatsnew/v1.1.5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.5.rst b/doc/source/whatsnew/v1.1.5.rst index 9b42acdbe95fd..609c3650c8cc2 100644 --- a/doc/source/whatsnew/v1.1.5.rst +++ b/doc/source/whatsnew/v1.1.5.rst @@ -17,7 +17,7 @@ Fixed regressions - Regression in addition of a timedelta-like scalar to a :class:`DatetimeIndex` raising incorrectly (:issue:`37295`) - Fixed regression in :meth:`Series.groupby` raising when the :class:`Index` of the :class:`Series` had a tuple as its name (:issue:`37755`) - Fixed regression in :meth:`DataFrame.loc` and :meth:`Series.loc` for ``__setitem__`` when one-dimensional tuple was given to select from :class:`MultiIndex` (:issue:`37711`) -- Fixed regression in inplace operations on :class:``ExtensionArray`` with NumPy array argument (:issue:`37910`) +- Fixed regression in inplace operations on :class:`Series` with ``ExtensionDtype`` with NumPy dtyped operand (:issue:`37910`) .. --------------------------------------------------------------------------- From cfb18748c087af0dc0c005ad2ffc1cd7860f9840 Mon Sep 17 00:00:00 2001 From: Andrew Wieteska Date: Mon, 23 Nov 2020 11:23:21 -0500 Subject: [PATCH 12/12] merge conflict --- .../tests/series/methods/test_inplace_ops.py | 60 ------------------- 1 file changed, 60 deletions(-) delete mode 100644 pandas/tests/series/methods/test_inplace_ops.py diff --git a/pandas/tests/series/methods/test_inplace_ops.py b/pandas/tests/series/methods/test_inplace_ops.py deleted file mode 100644 index 2ae2cb60e3bcf..0000000000000 --- a/pandas/tests/series/methods/test_inplace_ops.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest - -from pandas import Series -import pandas._testing as tm - - -@pytest.mark.parametrize( - "ser1, ser2, expected_add, expected_sub, expected_mul", - ( - [ - Series([1], dtype="Int64"), - Series([2], dtype="Int64"), - Series([3], dtype="Int64"), - Series([-1], dtype="Int64"), - Series([2], dtype="Int64"), - ], - [ - Series([1], dtype="float"), - Series([2.0], dtype="float"), - Series([3.0], dtype="float"), - Series([-1.0], dtype="float"), - Series([2.0], dtype="float"), - ], - [ - Series([1], dtype="Int64"), - Series([2.0], dtype="float"), - Series([3.0], dtype="float"), - Series([-1.0], dtype="float"), - Series([2], dtype="float"), - ], - [ - Series([1.0], dtype="float"), - Series([2], dtype="Int64"), - Series([3.0], dtype="float"), - Series([-1.0], dtype="float"), - Series([2], dtype="float"), - ], - pytest.param( - Series([1], dtype="Int64"), - Series([2.0], dtype="Float64"), - Series([3.0], dtype="Float64"), - Series([-1.0], dtype="Float64"), - Series([2], dtype="Float64"), - marks=pytest.mark.xfail(reason="Not implemented yet"), - ), - ), -) -def test_series_inplace_ops(ser1, ser2, expected_add, expected_sub, expected_mul): - - res = ser1.copy() - res += ser2 - tm.assert_series_equal(res, expected_add) - - res = ser1.copy() - res -= ser2 - tm.assert_series_equal(res, expected_sub) - - res = ser1.copy() - res *= ser2 - tm.assert_series_equal(res, expected_mul)