Skip to content

Commit 0428542

Browse files
Backport PR #40878: REGR: ufunc with DataFrame input not passing all kwargs (#40895)
Co-authored-by: Matthew Zeitlin <[email protected]>
1 parent 57dd3d2 commit 0428542

File tree

3 files changed

+43
-2
lines changed

3 files changed

+43
-2
lines changed

doc/source/whatsnew/v1.2.4.rst

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Fixed regressions
2121
- Fixed regression in :meth:`DataFrame.where` not returning a copy in the case of an all True condition (:issue:`39595`)
2222
- Fixed regression in :meth:`DataFrame.replace` raising ``IndexError`` when ``regex`` was a multi-key dictionary (:issue:`39338`)
2323
- 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`)
24+
- Fixed regression in NumPy ufuncs such as ``np.add`` not passing through all arguments for :class:`DataFrame` (:issue:`40662`)
2425

2526
.. ---------------------------------------------------------------------------
2627

pandas/core/arraylike.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -351,15 +351,17 @@ def reconstruct(result):
351351
# * len(inputs) > 1 is doable when we know that we have
352352
# aligned blocks / dtypes.
353353
inputs = tuple(np.asarray(x) for x in inputs)
354-
result = getattr(ufunc, method)(*inputs)
354+
result = getattr(ufunc, method)(*inputs, **kwargs)
355355
elif self.ndim == 1:
356356
# ufunc(series, ...)
357357
inputs = tuple(extract_array(x, extract_numpy=True) for x in inputs)
358358
result = getattr(ufunc, method)(*inputs, **kwargs)
359359
else:
360360
# ufunc(dataframe)
361-
if method == "__call__":
361+
if method == "__call__" and not kwargs:
362362
# for np.<ufunc>(..) calls
363+
# kwargs cannot necessarily be handled block-by-block, so only
364+
# take this path if there are no kwargs
363365
mgr = inputs[0]._mgr
364366
result = mgr.apply(getattr(ufunc, method))
365367
else:

pandas/tests/frame/test_ufunc.py

+38
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from functools import partial
2+
13
import numpy as np
24
import pytest
35

@@ -55,6 +57,42 @@ def test_binary_input_dispatch_binop(dtype):
5557
tm.assert_frame_equal(result, expected)
5658

5759

60+
@pytest.mark.parametrize(
61+
"func,arg,expected",
62+
[
63+
(np.add, 1, [2, 3, 4, 5]),
64+
(
65+
partial(np.add, where=[[False, True], [True, False]]),
66+
np.array([[1, 1], [1, 1]]),
67+
[0, 3, 4, 0],
68+
),
69+
(np.power, np.array([[1, 1], [2, 2]]), [1, 2, 9, 16]),
70+
(np.subtract, 2, [-1, 0, 1, 2]),
71+
(
72+
partial(np.negative, where=np.array([[False, True], [True, False]])),
73+
None,
74+
[0, -2, -3, 0],
75+
),
76+
],
77+
)
78+
def test_ufunc_passes_args(func, arg, expected, request):
79+
# GH#40662
80+
arr = np.array([[1, 2], [3, 4]])
81+
df = pd.DataFrame(arr)
82+
result_inplace = np.zeros_like(arr)
83+
# 1-argument ufunc
84+
if arg is None:
85+
result = func(df, out=result_inplace)
86+
else:
87+
result = func(df, arg, out=result_inplace)
88+
89+
expected = np.array(expected).reshape(2, 2)
90+
tm.assert_numpy_array_equal(result_inplace, expected)
91+
92+
expected = pd.DataFrame(expected)
93+
tm.assert_frame_equal(result, expected)
94+
95+
5896
@pytest.mark.parametrize("dtype_a", dtypes)
5997
@pytest.mark.parametrize("dtype_b", dtypes)
6098
def test_binary_input_aligns_columns(dtype_a, dtype_b):

0 commit comments

Comments
 (0)