Skip to content

BUG: Fix _binop for operators for serials which has more than one returns (divmod/rdivmod). #25588

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

Merged
merged 7 commits into from
Mar 20, 2019
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Fixed Regressions
- Fixed regression in ``IntervalDtype`` construction where passing an incorrect string with 'Interval' as a prefix could result in a ``RecursionError``. (:issue:`25338`)
- Fixed regression in :class:`Categorical`, where constructing it from a categorical ``Series`` and an explicit ``categories=`` that differed from that in the ``Series`` created an invalid object which could trigger segfaults. (:issue:`25318`)
- Fixed pip installing from source into an environment without NumPy (:issue:`25193`)
- Fixed bug in :meth:`Series.divmod` and :meth:`Series.rdivmod` (:issue:`25557`)
Copy link
Contributor

Choose a reason for hiding this comment

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

move to 0.25.0. pls explain what exactly the bug is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


.. _whatsnew_0242.enhancements:

Expand Down
12 changes: 7 additions & 5 deletions pandas/core/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -1494,18 +1494,20 @@ def _construct_result(left, result, index, name, dtype=None):
not be enough; we still need to override the name attribute.
"""
out = left._constructor(result, index=index, dtype=dtype)

out.name = name
out.__finalize__(left)
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't this be out = out.__finalize__(left)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Thanks!

if name is None:
out.name = name
Copy link
Contributor

Choose a reason for hiding this comment

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

hmm, this i pretty odd, what exactly is the issue here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have realized that the if condition is not correct here. Assign name to out.name directly should be ok.

Fixed now.

return out


def _construct_divmod_result(left, result, index, name, dtype=None):
"""divmod returns a tuple of like indexed series instead of a single series.
"""
constructor = left._constructor
return (
constructor(result[0], index=index, name=name, dtype=dtype),
constructor(result[1], index=index, name=name, dtype=dtype),
_construct_result(left, result[0], index=index, name=name,
dtype=dtype),
_construct_result(left, result[1], index=index, name=name,
dtype=dtype),
)


Expand Down
13 changes: 7 additions & 6 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2502,6 +2502,7 @@ def _binop(self, other, func, level=None, fill_value=None):
-------
Series
"""

if not isinstance(other, Series):
raise AssertionError('Other operand must be Series')

Expand All @@ -2518,13 +2519,13 @@ def _binop(self, other, func, level=None, fill_value=None):

with np.errstate(all='ignore'):
result = func(this_vals, other_vals)

name = ops.get_op_result_name(self, other)
result = self._constructor(result, index=new_index, name=name)
result = result.__finalize__(self)
if name is None:
# When name is None, __finalize__ overwrites current name
result.name = None
return result
if func.__name__ in ['divmod', 'rdivmod']:
ret = ops._construct_divmod_result(self, result, new_index, name)
Copy link
Contributor

Choose a reason for hiding this comment

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

you could just return on each statement

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previous review comment suggested me to use _construct_divmod_result.

else:
ret = ops._construct_result(self, result, new_index, name)
return ret

def combine(self, other, func, fill_value=None):
"""
Expand Down
15 changes: 15 additions & 0 deletions pandas/tests/series/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,21 @@ def test_op_duplicate_index(self):
expected = pd.Series([11, 12, np.nan], index=[1, 1, 2])
assert_series_equal(result, expected)

def test_divmod(self):
# GH25557
a = Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
Copy link
Contributor

Choose a reason for hiding this comment

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

Add the GitHub issue number as a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do.

b = Series([2, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])

r1 = divmod(a, b)
Copy link
Contributor

Choose a reason for hiding this comment

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

use the format
result=
expected=
assert_series_equal

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

r2 = a.divmod(b)
assert_series_equal(r1[0], r2[0])
assert_series_equal(r1[1], r2[1])

r3 = divmod(b, a)
r4 = a.rdivmod(b)
assert_series_equal(r3[0], r4[0])
assert_series_equal(r3[1], r4[1])


class TestSeriesUnaryOps(object):
# __neg__, __pos__, __inv__
Expand Down