Skip to content

Commit

Permalink
Backport PR #40878: REGR: ufunc with DataFrame input not passing all …
Browse files Browse the repository at this point in the history
…kwargs (#40895)

Co-authored-by: Matthew Zeitlin <[email protected]>
  • Loading branch information
meeseeksmachine and mzeitlin11 authored Apr 12, 2021
1 parent 57dd3d2 commit 0428542
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 2 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.2.4.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Fixed regressions
- Fixed regression in :meth:`DataFrame.where` not returning a copy in the case of an all True condition (:issue:`39595`)
- Fixed regression in :meth:`DataFrame.replace` raising ``IndexError`` when ``regex`` was a multi-key dictionary (:issue:`39338`)
- Fixed regression in repr of floats in an ``object`` column not respecting ``float_format`` when printed in the console or outputted through :meth:`DataFrame.to_string`, :meth:`DataFrame.to_html`, and :meth:`DataFrame.to_latex` (:issue:`40024`)
- Fixed regression in NumPy ufuncs such as ``np.add`` not passing through all arguments for :class:`DataFrame` (:issue:`40662`)

.. ---------------------------------------------------------------------------
Expand Down
6 changes: 4 additions & 2 deletions pandas/core/arraylike.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,17 @@ def reconstruct(result):
# * len(inputs) > 1 is doable when we know that we have
# aligned blocks / dtypes.
inputs = tuple(np.asarray(x) for x in inputs)
result = getattr(ufunc, method)(*inputs)
result = getattr(ufunc, method)(*inputs, **kwargs)
elif self.ndim == 1:
# ufunc(series, ...)
inputs = tuple(extract_array(x, extract_numpy=True) for x in inputs)
result = getattr(ufunc, method)(*inputs, **kwargs)
else:
# ufunc(dataframe)
if method == "__call__":
if method == "__call__" and not kwargs:
# for np.<ufunc>(..) calls
# kwargs cannot necessarily be handled block-by-block, so only
# take this path if there are no kwargs
mgr = inputs[0]._mgr
result = mgr.apply(getattr(ufunc, method))
else:
Expand Down
38 changes: 38 additions & 0 deletions pandas/tests/frame/test_ufunc.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from functools import partial

import numpy as np
import pytest

Expand Down Expand Up @@ -55,6 +57,42 @@ def test_binary_input_dispatch_binop(dtype):
tm.assert_frame_equal(result, expected)


@pytest.mark.parametrize(
"func,arg,expected",
[
(np.add, 1, [2, 3, 4, 5]),
(
partial(np.add, where=[[False, True], [True, False]]),
np.array([[1, 1], [1, 1]]),
[0, 3, 4, 0],
),
(np.power, np.array([[1, 1], [2, 2]]), [1, 2, 9, 16]),
(np.subtract, 2, [-1, 0, 1, 2]),
(
partial(np.negative, where=np.array([[False, True], [True, False]])),
None,
[0, -2, -3, 0],
),
],
)
def test_ufunc_passes_args(func, arg, expected, request):
# GH#40662
arr = np.array([[1, 2], [3, 4]])
df = pd.DataFrame(arr)
result_inplace = np.zeros_like(arr)
# 1-argument ufunc
if arg is None:
result = func(df, out=result_inplace)
else:
result = func(df, arg, out=result_inplace)

expected = np.array(expected).reshape(2, 2)
tm.assert_numpy_array_equal(result_inplace, expected)

expected = pd.DataFrame(expected)
tm.assert_frame_equal(result, expected)


@pytest.mark.parametrize("dtype_a", dtypes)
@pytest.mark.parametrize("dtype_b", dtypes)
def test_binary_input_aligns_columns(dtype_a, dtype_b):
Expand Down

0 comments on commit 0428542

Please sign in to comment.