From a6cb690694a459fb8bb1c137ee541024ddfa413d Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Wed, 24 Oct 2018 11:50:24 +0200 Subject: [PATCH 1/4] BUG: fix reducing numpy ufuncs for Series/Index --- pandas/core/indexes/base.py | 2 +- pandas/core/series.py | 2 ++ pandas/tests/arithmetic/test_numeric.py | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index e5760f0141efb..d1049aa481c91 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -677,7 +677,7 @@ def __array_wrap__(self, result, context=None): """ Gets called after a ufunc """ - if is_bool_dtype(result): + if is_bool_dtype(result) or (getattr(result, 'ndim', None) == 0): return result attrs = self._get_attributes_dict() diff --git a/pandas/core/series.py b/pandas/core/series.py index 7ebbe0dfb4bb7..679e6237dd56d 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -633,6 +633,8 @@ def __array_wrap__(self, result, context=None): """ Gets called after a ufunc """ + if getattr(result, 'ndim', None) == 0: + return result return self._constructor(result, index=self.index, copy=False).__finalize__(self) diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index 25845dd8b3151..edeb16fa97731 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -833,6 +833,15 @@ def test_ufunc_coercions(self, holder): exp = tm.box_expected(exp, box) tm.assert_equal(result, exp) + @pytest.mark.parametrize('holder', [pd.Int64Index, pd.UInt64Index, + pd.Float64Index, pd.Series]) + def test_ufunc_reduce(self, holder): + idx = holder([1, 2, 3, 4, 5], name='x') + + result = np.add.reduce(idx) + assert not isinstance(result, holder) + assert result == 15 + class TestObjectDtypeEquivalence(object): # Tests that arithmetic operations match operations executed elementwise From c0d55827db3ab287a6990dae6c3269601c04fb8d Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Wed, 24 Oct 2018 14:37:38 +0200 Subject: [PATCH 2/4] feedback --- pandas/tests/arithmetic/test_numeric.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index edeb16fa97731..82b5678d32816 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -790,11 +790,11 @@ def check(series, other): class TestUFuncCompat(object): - @pytest.mark.parametrize('holder', [pd.Int64Index, pd.UInt64Index, - pd.Float64Index, pd.Series]) - def test_ufunc_coercions(self, holder): - idx = holder([1, 2, 3, 4, 5], name='x') - box = pd.Series if holder is pd.Series else pd.Index + @pytest.mark.parametrize('box', [pd.Int64Index, pd.UInt64Index, + pd.Float64Index, pd.Series]) + def test_ufunc_coercions(self, box): + idx = box([1, 2, 3, 4, 5], name='x') + box = pd.Series if box is pd.Series else pd.Index result = np.sqrt(idx) assert result.dtype == 'f8' and isinstance(result, box) @@ -833,15 +833,20 @@ def test_ufunc_coercions(self, holder): exp = tm.box_expected(exp, box) tm.assert_equal(result, exp) - @pytest.mark.parametrize('holder', [pd.Int64Index, pd.UInt64Index, - pd.Float64Index, pd.Series]) - def test_ufunc_reduce(self, holder): - idx = holder([1, 2, 3, 4, 5], name='x') + @pytest.mark.parametrize('box', [pd.Int64Index, pd.UInt64Index, + pd.Float64Index, pd.Series]) + def test_ufunc_reduce(self, box): + idx = box([1, 2, 3, 4, 5], name='x') result = np.add.reduce(idx) - assert not isinstance(result, holder) + # GH23312 result was equal to 15 but boxed as 0-dim array in Index + assert not isinstance(result, box) assert result == 15 + result = np.logical_and.reduce(idx) + assert not isinstance(result, box) + assert result is True + class TestObjectDtypeEquivalence(object): # Tests that arithmetic operations match operations executed elementwise From 1baf5c0b2b375f58236a38ff50afa4f677a62869 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Fri, 26 Oct 2018 14:41:29 +0200 Subject: [PATCH 3/4] fixup expected result of reducing boolean op --- pandas/tests/arithmetic/test_numeric.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index 82b5678d32816..ee12bc606345e 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -844,8 +844,9 @@ def test_ufunc_reduce(self, box): assert result == 15 result = np.logical_and.reduce(idx) + expected = np.array(True) assert not isinstance(result, box) - assert result is True + assert tm.assert_numpy_array_equal(result, expected) class TestObjectDtypeEquivalence(object): From 733d0276b77dc966ded8afd7b7b89f79ae23da58 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Fri, 26 Oct 2018 14:44:37 +0200 Subject: [PATCH 4/4] add whatsnew --- doc/source/whatsnew/v0.24.0.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 768868d585721..97c8e8bd9cd22 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -1079,6 +1079,7 @@ Numeric - Bug in :meth:`DataFrame.astype` to extension dtype may raise ``AttributeError`` (:issue:`22578`) - Bug in :class:`DataFrame` with ``timedelta64[ns]`` dtype arithmetic operations with ``ndarray`` with integer dtype incorrectly treating the narray as ``timedelta64[ns]`` dtype (:issue:`23114`) - Bug in :meth:`Series.rpow` with object dtype ``NaN`` for ``1 ** NA`` instead of ``1`` (:issue:`22922`). +- Bug in calling a reducing numpy ufunc on a Series or Index where the scalar result was broadcasted to the original shape of the Series or Index (:issue:`23312`). Strings ^^^^^^^