Skip to content

Commit 73d7867

Browse files
jbrockmendelproost
authored andcommitted
OPS: Remove mask_cmp_op fallback behavior (pandas-dev#28601)
1 parent 5a8446d commit 73d7867

File tree

3 files changed

+34
-49
lines changed

3 files changed

+34
-49
lines changed

doc/source/whatsnew/v1.0.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ Timezones
199199
Numeric
200200
^^^^^^^
201201
- Bug in :meth:`DataFrame.quantile` with zero-column :class:`DataFrame` incorrectly raising (:issue:`23925`)
202-
-
202+
- :class:`DataFrame` inequality comparisons with object-dtype and ``complex`` entries failing to raise ``TypeError`` like their :class:`Series` counterparts (:issue:`28079`)
203203
-
204204

205205
Conversion

pandas/core/ops/__init__.py

+4-44
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
ABCIndexClass,
2929
ABCSeries,
3030
)
31-
from pandas.core.dtypes.missing import isna, notna
31+
from pandas.core.dtypes.missing import isna
3232

3333
from pandas._typing import ArrayLike
3434
from pandas.core.construction import array, extract_array
@@ -354,38 +354,6 @@ def fill_binop(left, right, fill_value):
354354
return left, right
355355

356356

357-
def mask_cmp_op(x, y, op):
358-
"""
359-
Apply the function `op` to only non-null points in x and y.
360-
361-
Parameters
362-
----------
363-
x : array-like
364-
y : array-like
365-
op : binary operation
366-
367-
Returns
368-
-------
369-
result : ndarray[bool]
370-
"""
371-
xrav = x.ravel()
372-
result = np.empty(x.size, dtype=bool)
373-
if isinstance(y, (np.ndarray, ABCSeries)):
374-
yrav = y.ravel()
375-
mask = notna(xrav) & notna(yrav)
376-
result[mask] = op(np.array(list(xrav[mask])), np.array(list(yrav[mask])))
377-
else:
378-
mask = notna(xrav)
379-
result[mask] = op(np.array(list(xrav[mask])), y)
380-
381-
if op == operator.ne: # pragma: no cover
382-
np.putmask(result, ~mask, True)
383-
else:
384-
np.putmask(result, ~mask, False)
385-
result = result.reshape(x.shape)
386-
return result
387-
388-
389357
# -----------------------------------------------------------------------------
390358
# Dispatch logic
391359

@@ -905,14 +873,6 @@ def _flex_comp_method_FRAME(cls, op, special):
905873
op_name = _get_op_name(op, special)
906874
default_axis = _get_frame_op_default_axis(op_name)
907875

908-
def na_op(x, y):
909-
try:
910-
with np.errstate(invalid="ignore"):
911-
result = op(x, y)
912-
except TypeError:
913-
result = mask_cmp_op(x, y, op)
914-
return result
915-
916876
doc = _flex_comp_doc_FRAME.format(
917877
op_name=op_name, desc=_op_descriptions[op_name]["desc"]
918878
)
@@ -926,16 +886,16 @@ def f(self, other, axis=default_axis, level=None):
926886
# Another DataFrame
927887
if not self._indexed_same(other):
928888
self, other = self.align(other, "outer", level=level, copy=False)
929-
new_data = dispatch_to_series(self, other, na_op, str_rep)
889+
new_data = dispatch_to_series(self, other, op, str_rep)
930890
return self._construct_result(new_data)
931891

932892
elif isinstance(other, ABCSeries):
933893
return _combine_series_frame(
934-
self, other, na_op, fill_value=None, axis=axis, level=level
894+
self, other, op, fill_value=None, axis=axis, level=level
935895
)
936896
else:
937897
# in this case we always have `np.ndim(other) == 0`
938-
new_data = dispatch_to_series(self, other, na_op)
898+
new_data = dispatch_to_series(self, other, op)
939899
return self._construct_result(new_data)
940900

941901
f.__name__ = op_name

pandas/tests/frame/test_arithmetic.py

+29-4
Original file line numberDiff line numberDiff line change
@@ -235,21 +235,46 @@ def _test_seq(df, idx_ser, col_ser):
235235
rs = df.le(df)
236236
assert not rs.loc[0, 0]
237237

238+
def test_bool_flex_frame_complex_dtype(self):
238239
# complex
239240
arr = np.array([np.nan, 1, 6, np.nan])
240241
arr2 = np.array([2j, np.nan, 7, None])
241242
df = pd.DataFrame({"a": arr})
242243
df2 = pd.DataFrame({"a": arr2})
243-
rs = df.gt(df2)
244-
assert not rs.values.any()
244+
245+
msg = "|".join(
246+
[
247+
"'>' not supported between instances of '.*' and 'complex'",
248+
r"unorderable types: .*complex\(\)", # PY35
249+
]
250+
)
251+
with pytest.raises(TypeError, match=msg):
252+
# inequalities are not well-defined for complex numbers
253+
df.gt(df2)
254+
with pytest.raises(TypeError, match=msg):
255+
# regression test that we get the same behavior for Series
256+
df["a"].gt(df2["a"])
257+
with pytest.raises(TypeError, match=msg):
258+
# Check that we match numpy behavior here
259+
df.values > df2.values
260+
245261
rs = df.ne(df2)
246262
assert rs.values.all()
247263

248264
arr3 = np.array([2j, np.nan, None])
249265
df3 = pd.DataFrame({"a": arr3})
250-
rs = df3.gt(2j)
251-
assert not rs.values.any()
252266

267+
with pytest.raises(TypeError, match=msg):
268+
# inequalities are not well-defined for complex numbers
269+
df3.gt(2j)
270+
with pytest.raises(TypeError, match=msg):
271+
# regression test that we get the same behavior for Series
272+
df3["a"].gt(2j)
273+
with pytest.raises(TypeError, match=msg):
274+
# Check that we match numpy behavior here
275+
df3.values > 2j
276+
277+
def test_bool_flex_frame_object_dtype(self):
253278
# corner, dtype=object
254279
df1 = pd.DataFrame({"col": ["foo", np.nan, "bar"]})
255280
df2 = pd.DataFrame({"col": ["foo", datetime.now(), "bar"]})

0 commit comments

Comments
 (0)