Skip to content

BUG: fix reducing numpy ufuncs for Series/Index #23314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,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()
Expand Down
2 changes: 2 additions & 0 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,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)

Expand Down
25 changes: 20 additions & 5 deletions pandas/tests/arithmetic/test_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -833,6 +833,21 @@ def test_ufunc_coercions(self, holder):
exp = tm.box_expected(exp, box)
tm.assert_equal(result, exp)

@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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use paramterize over some more ops here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would only be testing that those work on the underlying numpy array (which they do :)), so I don't think it is that important

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well can't hurt to have a few more then

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What might be more useful is with other result types, like np.logical_and.reduce

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes exactly

# 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)
expected = np.array(True)
assert not isinstance(result, box)
assert tm.assert_numpy_array_equal(result, expected)


class TestObjectDtypeEquivalence(object):
# Tests that arithmetic operations match operations executed elementwise
Expand Down