Skip to content

REF/TYP: use OpsMixin for DataFrame #37044

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 24 commits into from
Oct 11, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f1bb6a0
REF/TYP: use OpsMixin for arithmetic methods
jbrockmendel Oct 8, 2020
9424789
Merge branch 'master' of https://github.com/pandas-dev/pandas into op…
jbrockmendel Oct 8, 2020
ac4f9de
Merge branch 'master' of https://github.com/pandas-dev/pandas into op…
jbrockmendel Oct 10, 2020
45c8618
REF: separate arith_method_FRAME from flex_arith_method_FRAME
jbrockmendel Oct 3, 2020
f553646
whatsnew
jbrockmendel Oct 7, 2020
3a855ba
REF/TYP: use OpsMixin for logical methods (#36964)
jbrockmendel Oct 8, 2020
6ed3317
TST: insert 'match' to bare pytest raises in pandas/tests/tools/test_…
krajatcl Oct 10, 2020
c7651cd
TST: insert 'match' to bare pytest raises in pandas/tests/test_flags.…
krajatcl Oct 10, 2020
919cbbc
TYP: generic, series, frame (#36989)
jbrockmendel Oct 10, 2020
81ac02a
CI: pin pymysql #36465 (#36847)
fangchenli Oct 10, 2020
8bec3a7
CLN/REF: de-duplicate DatetimeTZBlock.setitem (#37019)
jbrockmendel Oct 10, 2020
f86ff04
REF/TYP: define NDFrame numeric methods non-dynamically (#37017)
jbrockmendel Oct 10, 2020
04d33b8
CLN: require td64 in TimedeltaBlock (#37018)
jbrockmendel Oct 10, 2020
8912516
BUG: Raise ValueError instead of bare Exception in sanitize_array (#3…
micahjsmith Oct 10, 2020
1581bb1
CLN: collected cleanups, warning suppression in tests (#37021)
jbrockmendel Oct 10, 2020
77d4857
Merge branch 'master' of https://github.com/pandas-dev/pandas into re…
jbrockmendel Oct 10, 2020
a57d174
REF/TYP: use OpsMixin for DataFrame
jbrockmendel Oct 10, 2020
cd2d860
Merge branch 'master' of https://github.com/pandas-dev/pandas into re…
jbrockmendel Oct 10, 2020
8efb461
Merge branch 'master' of https://github.com/pandas-dev/pandas into re…
jbrockmendel Oct 10, 2020
afd25cc
CLN: remove get_op_name
jbrockmendel Oct 10, 2020
db038a5
Merge branch 'master' of https://github.com/pandas-dev/pandas into re…
jbrockmendel Oct 10, 2020
fdc68db
mypy fixup
jbrockmendel Oct 10, 2020
296e45b
de-privatize
jbrockmendel Oct 11, 2020
254ea0f
Merge branch 'master' of https://github.com/pandas-dev/pandas into re…
jbrockmendel Oct 11, 2020
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
Prev Previous commit
Next Next commit
REF: separate arith_method_FRAME from flex_arith_method_FRAME
  • Loading branch information
jbrockmendel committed Oct 10, 2020
commit 45c8618e76b146be84c59a1a362988cb2fae1e1d
38 changes: 27 additions & 11 deletions pandas/core/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,18 +533,13 @@ def _maybe_align_series_as_frame(frame: "DataFrame", series: "Series", axis: int
return type(frame)(rvalues, index=frame.index, columns=frame.columns)


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

na_op = get_array_op(op)

if op_name in _op_descriptions:
# i.e. include "add" but not "__add__"
doc = _make_flex_doc(op_name, "dataframe")
else:
doc = _arith_doc_FRAME % op_name
doc = _make_flex_doc(op_name, "dataframe")

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

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

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

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


def arith_method_FRAME(cls: Type["DataFrame"], op, special: bool):
assert special
op_name = _get_op_name(op, special)
doc = _arith_doc_FRAME % op_name

@Appender(doc)
def f(self, other):

if _should_reindex_frame_op(self, other, op, 1, 1, None, None):
return _frame_arith_method_with_reindex(self, other, op)

axis = 1 # only relevant for Series other case

self, other = align_method_FRAME(self, other, axis, flex=True, level=None)

new_data = dispatch_to_series(self, other, op, axis=axis)
return self._construct_result(new_data)

f.__name__ = op_name

return f


def flex_comp_method_FRAME(cls: Type["DataFrame"], op, special: bool):
assert not special # "special" also means "not flex"
op_name = _get_op_name(op, special)
Expand Down Expand Up @@ -616,7 +632,7 @@ def comp_method_FRAME(cls: Type["DataFrame"], op, special: bool):
def f(self, other):
axis = 1 # only relevant for Series other case

self, other = align_method_FRAME(self, other, axis, level=None, flex=False)
self, other = align_method_FRAME(self, other, axis, flex=False, level=None)

# See GH#4537 for discussion of scalar op behavior
new_data = dispatch_to_series(self, other, op, axis=axis)
Expand Down
3 changes: 2 additions & 1 deletion pandas/core/ops/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def _get_method_wrappers(cls):
from pandas.core.ops import (
arith_method_FRAME,
comp_method_FRAME,
flex_arith_method_FRAME,
flex_comp_method_FRAME,
flex_method_SERIES,
)
Expand All @@ -58,7 +59,7 @@ def _get_method_wrappers(cls):
comp_special = None
bool_special = None
elif issubclass(cls, ABCDataFrame):
arith_flex = arith_method_FRAME
arith_flex = flex_arith_method_FRAME
comp_flex = flex_comp_method_FRAME
arith_special = arith_method_FRAME
comp_special = comp_method_FRAME
Expand Down
7 changes: 7 additions & 0 deletions pandas/tests/frame/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,13 @@ def test_no_warning(self, all_arithmetic_operators):
df = pd.DataFrame({"A": [0.0, 0.0], "B": [0.0, None]})
b = df["B"]
with tm.assert_produces_warning(None):
getattr(df, all_arithmetic_operators)(b)

def test_dunder_methods_binary(self, all_arithmetic_operators):
# GH#??? frame.__foo__ should only accept one argument
df = pd.DataFrame({"A": [0.0, 0.0], "B": [0.0, None]})
b = df["B"]
with pytest.raises(TypeError, match="takes 2 positional arguments"):
getattr(df, all_arithmetic_operators)(b, 0)


Expand Down
13 changes: 0 additions & 13 deletions pandas/tests/series/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,25 +276,12 @@ def test_scalar_na_logical_ops_corners_aligns(self):

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

result = d.__and__(s, axis="columns")
tm.assert_frame_equal(result, expected)

result = d.__and__(s, axis=1)
tm.assert_frame_equal(result, expected)

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

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

expected = (s & s).to_frame("A")
result = d.__and__(s, axis="index")
tm.assert_frame_equal(result, expected)

result = d.__and__(s, axis=0)
tm.assert_frame_equal(result, expected)

@pytest.mark.parametrize("op", [operator.and_, operator.or_, operator.xor])
def test_logical_ops_with_index(self, op):
# GH#22092, GH#19792
Expand Down