Skip to content

Commit 7f1c3b1

Browse files
authored
REF: separate flex from non-flex DataFrame ops (#36843)
1 parent 58c2150 commit 7f1c3b1

File tree

5 files changed

+37
-25
lines changed

5 files changed

+37
-25
lines changed

doc/source/whatsnew/v1.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ Numeric
362362
- Bug in :class:`Series` flex arithmetic methods where the result when operating with a ``list``, ``tuple`` or ``np.ndarray`` would have an incorrect name (:issue:`36760`)
363363
- Bug in :class:`IntegerArray` multiplication with ``timedelta`` and ``np.timedelta64`` objects (:issue:`36870`)
364364
- Bug in :meth:`DataFrame.diff` with ``datetime64`` dtypes including ``NaT`` values failing to fill ``NaT`` results correctly (:issue:`32441`)
365+
- Bug in :class:`DataFrame` arithmetic ops incorrectly accepting keyword arguments (:issue:`36843`)
365366

366367
Conversion
367368
^^^^^^^^^^

pandas/core/ops/__init__.py

+27-11
Original file line numberDiff line numberDiff line change
@@ -533,18 +533,13 @@ def _maybe_align_series_as_frame(frame: "DataFrame", series: "Series", axis: int
533533
return type(frame)(rvalues, index=frame.index, columns=frame.columns)
534534

535535

536-
def arith_method_FRAME(cls: Type["DataFrame"], op, special: bool):
537-
# This is the only function where `special` can be either True or False
536+
def flex_arith_method_FRAME(cls: Type["DataFrame"], op, special: bool):
537+
assert not special
538538
op_name = _get_op_name(op, special)
539539
default_axis = None if special else "columns"
540540

541541
na_op = get_array_op(op)
542-
543-
if op_name in _op_descriptions:
544-
# i.e. include "add" but not "__add__"
545-
doc = _make_flex_doc(op_name, "dataframe")
546-
else:
547-
doc = _arith_doc_FRAME % op_name
542+
doc = _make_flex_doc(op_name, "dataframe")
548543

549544
@Appender(doc)
550545
def f(self, other, axis=default_axis, level=None, fill_value=None):
@@ -561,8 +556,6 @@ def f(self, other, axis=default_axis, level=None, fill_value=None):
561556

562557
axis = self._get_axis_number(axis) if axis is not None else 1
563558

564-
# TODO: why are we passing flex=True instead of flex=not special?
565-
# 15 tests fail if we pass flex=not special instead
566559
self, other = align_method_FRAME(self, other, axis, flex=True, level=level)
567560

568561
if isinstance(other, ABCDataFrame):
@@ -585,6 +578,29 @@ def f(self, other, axis=default_axis, level=None, fill_value=None):
585578
return f
586579

587580

581+
def arith_method_FRAME(cls: Type["DataFrame"], op, special: bool):
582+
assert special
583+
op_name = _get_op_name(op, special)
584+
doc = _arith_doc_FRAME % op_name
585+
586+
@Appender(doc)
587+
def f(self, other):
588+
589+
if _should_reindex_frame_op(self, other, op, 1, 1, None, None):
590+
return _frame_arith_method_with_reindex(self, other, op)
591+
592+
axis = 1 # only relevant for Series other case
593+
594+
self, other = align_method_FRAME(self, other, axis, flex=True, level=None)
595+
596+
new_data = dispatch_to_series(self, other, op, axis=axis)
597+
return self._construct_result(new_data)
598+
599+
f.__name__ = op_name
600+
601+
return f
602+
603+
588604
def flex_comp_method_FRAME(cls: Type["DataFrame"], op, special: bool):
589605
assert not special # "special" also means "not flex"
590606
op_name = _get_op_name(op, special)
@@ -616,7 +632,7 @@ def comp_method_FRAME(cls: Type["DataFrame"], op, special: bool):
616632
def f(self, other):
617633
axis = 1 # only relevant for Series other case
618634

619-
self, other = align_method_FRAME(self, other, axis, level=None, flex=False)
635+
self, other = align_method_FRAME(self, other, axis, flex=False, level=None)
620636

621637
# See GH#4537 for discussion of scalar op behavior
622638
new_data = dispatch_to_series(self, other, op, axis=axis)

pandas/core/ops/methods.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def _get_method_wrappers(cls):
4646
from pandas.core.ops import (
4747
arith_method_FRAME,
4848
comp_method_FRAME,
49+
flex_arith_method_FRAME,
4950
flex_comp_method_FRAME,
5051
flex_method_SERIES,
5152
)
@@ -58,7 +59,7 @@ def _get_method_wrappers(cls):
5859
comp_special = None
5960
bool_special = None
6061
elif issubclass(cls, ABCDataFrame):
61-
arith_flex = arith_method_FRAME
62+
arith_flex = flex_arith_method_FRAME
6263
comp_flex = flex_comp_method_FRAME
6364
arith_special = arith_method_FRAME
6465
comp_special = comp_method_FRAME

pandas/tests/frame/test_arithmetic.py

+7
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,13 @@ def test_no_warning(self, all_arithmetic_operators):
14841484
df = pd.DataFrame({"A": [0.0, 0.0], "B": [0.0, None]})
14851485
b = df["B"]
14861486
with tm.assert_produces_warning(None):
1487+
getattr(df, all_arithmetic_operators)(b)
1488+
1489+
def test_dunder_methods_binary(self, all_arithmetic_operators):
1490+
# GH#??? frame.__foo__ should only accept one argument
1491+
df = pd.DataFrame({"A": [0.0, 0.0], "B": [0.0, None]})
1492+
b = df["B"]
1493+
with pytest.raises(TypeError, match="takes 2 positional arguments"):
14871494
getattr(df, all_arithmetic_operators)(b, 0)
14881495

14891496

pandas/tests/series/test_operators.py

-13
Original file line numberDiff line numberDiff line change
@@ -276,25 +276,12 @@ def test_scalar_na_logical_ops_corners_aligns(self):
276276

277277
expected = DataFrame(False, index=range(9), columns=["A"] + list(range(9)))
278278

279-
result = d.__and__(s, axis="columns")
280-
tm.assert_frame_equal(result, expected)
281-
282-
result = d.__and__(s, axis=1)
283-
tm.assert_frame_equal(result, expected)
284-
285279
result = s & d
286280
tm.assert_frame_equal(result, expected)
287281

288282
result = d & s
289283
tm.assert_frame_equal(result, expected)
290284

291-
expected = (s & s).to_frame("A")
292-
result = d.__and__(s, axis="index")
293-
tm.assert_frame_equal(result, expected)
294-
295-
result = d.__and__(s, axis=0)
296-
tm.assert_frame_equal(result, expected)
297-
298285
@pytest.mark.parametrize("op", [operator.and_, operator.or_, operator.xor])
299286
def test_logical_ops_with_index(self, op):
300287
# GH#22092, GH#19792

0 commit comments

Comments
 (0)