Skip to content

Commit a568bdc

Browse files
authored
REF: deduplicate nullable arrays _cmp_method (#44548)
1 parent e325320 commit a568bdc

File tree

4 files changed

+50
-154
lines changed

4 files changed

+50
-154
lines changed

pandas/core/arrays/boolean.py

-48
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
TYPE_CHECKING,
66
overload,
77
)
8-
import warnings
98

109
import numpy as np
1110

@@ -44,7 +43,6 @@
4443
BaseMaskedArray,
4544
BaseMaskedDtype,
4645
)
47-
from pandas.core.ops import invalid_comparison
4846

4947
if TYPE_CHECKING:
5048
import pyarrow
@@ -622,52 +620,6 @@ def _logical_method(self, other, op):
622620
# expected "ndarray"
623621
return BooleanArray(result, mask) # type: ignore[arg-type]
624622

625-
def _cmp_method(self, other, op):
626-
from pandas.arrays import (
627-
FloatingArray,
628-
IntegerArray,
629-
)
630-
631-
if isinstance(other, (IntegerArray, FloatingArray)):
632-
return NotImplemented
633-
634-
mask = None
635-
636-
if isinstance(other, BooleanArray):
637-
other, mask = other._data, other._mask
638-
639-
elif is_list_like(other):
640-
other = np.asarray(other)
641-
if other.ndim > 1:
642-
raise NotImplementedError("can only perform ops with 1-d structures")
643-
if len(self) != len(other):
644-
raise ValueError("Lengths must match to compare")
645-
646-
if other is libmissing.NA:
647-
# numpy does not handle pd.NA well as "other" scalar (it returns
648-
# a scalar False instead of an array)
649-
result = np.zeros_like(self._data)
650-
mask = np.ones_like(self._data)
651-
else:
652-
# numpy will show a DeprecationWarning on invalid elementwise
653-
# comparisons, this will raise in the future
654-
with warnings.catch_warnings():
655-
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
656-
with np.errstate(all="ignore"):
657-
method = getattr(self._data, f"__{op.__name__}__")
658-
result = method(other)
659-
660-
if result is NotImplemented:
661-
result = invalid_comparison(self._data, other, op)
662-
663-
# nans propagate
664-
if mask is None:
665-
mask = self._mask.copy()
666-
else:
667-
mask = self._mask | mask
668-
669-
return BooleanArray(result, mask, copy=False)
670-
671623
def _arith_method(self, other, op):
672624
mask = None
673625
op_name = op.__name__

pandas/core/arrays/floating.py

+1-53
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
from __future__ import annotations
22

33
from typing import overload
4-
import warnings
54

65
import numpy as np
76

8-
from pandas._libs import (
9-
lib,
10-
missing as libmissing,
11-
)
7+
from pandas._libs import lib
128
from pandas._typing import (
139
ArrayLike,
1410
AstypeArg,
@@ -24,7 +20,6 @@
2420
is_datetime64_dtype,
2521
is_float_dtype,
2622
is_integer_dtype,
27-
is_list_like,
2823
is_object_dtype,
2924
pandas_dtype,
3025
)
@@ -39,7 +34,6 @@
3934
NumericArray,
4035
NumericDtype,
4136
)
42-
from pandas.core.ops import invalid_comparison
4337
from pandas.core.tools.numeric import to_numeric
4438

4539

@@ -337,52 +331,6 @@ def astype(self, dtype: AstypeArg, copy: bool = True) -> ArrayLike:
337331
def _values_for_argsort(self) -> np.ndarray:
338332
return self._data
339333

340-
def _cmp_method(self, other, op):
341-
from pandas.arrays import (
342-
BooleanArray,
343-
IntegerArray,
344-
)
345-
346-
mask = None
347-
348-
if isinstance(other, (BooleanArray, IntegerArray, FloatingArray)):
349-
other, mask = other._data, other._mask
350-
351-
elif is_list_like(other):
352-
other = np.asarray(other)
353-
if other.ndim > 1:
354-
raise NotImplementedError("can only perform ops with 1-d structures")
355-
356-
if other is libmissing.NA:
357-
# numpy does not handle pd.NA well as "other" scalar (it returns
358-
# a scalar False instead of an array)
359-
# This may be fixed by NA.__array_ufunc__. Revisit this check
360-
# once that's implemented.
361-
result = np.zeros(self._data.shape, dtype="bool")
362-
mask = np.ones(self._data.shape, dtype="bool")
363-
else:
364-
with warnings.catch_warnings():
365-
# numpy may show a FutureWarning:
366-
# elementwise comparison failed; returning scalar instead,
367-
# but in the future will perform elementwise comparison
368-
# before returning NotImplemented. We fall back to the correct
369-
# behavior today, so that should be fine to ignore.
370-
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
371-
with np.errstate(all="ignore"):
372-
method = getattr(self._data, f"__{op.__name__}__")
373-
result = method(other)
374-
375-
if result is NotImplemented:
376-
result = invalid_comparison(self._data, other, op)
377-
378-
# nans propagate
379-
if mask is None:
380-
mask = self._mask.copy()
381-
else:
382-
mask = self._mask | mask
383-
384-
return BooleanArray(result, mask)
385-
386334
def sum(self, *, skipna=True, min_count=0, axis: int | None = 0, **kwargs):
387335
nv.validate_sum((), kwargs)
388336
return super()._reduce("sum", skipna=skipna, min_count=min_count, axis=axis)

pandas/core/arrays/integer.py

+1-53
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
from __future__ import annotations
22

33
from typing import overload
4-
import warnings
54

65
import numpy as np
76

87
from pandas._libs import (
98
iNaT,
109
lib,
11-
missing as libmissing,
1210
)
1311
from pandas._typing import (
1412
ArrayLike,
@@ -30,23 +28,18 @@
3028
is_float,
3129
is_float_dtype,
3230
is_integer_dtype,
33-
is_list_like,
3431
is_object_dtype,
3532
is_string_dtype,
3633
pandas_dtype,
3734
)
3835
from pandas.core.dtypes.missing import isna
3936

4037
from pandas.core.arrays import ExtensionArray
41-
from pandas.core.arrays.masked import (
42-
BaseMaskedArray,
43-
BaseMaskedDtype,
44-
)
38+
from pandas.core.arrays.masked import BaseMaskedDtype
4539
from pandas.core.arrays.numeric import (
4640
NumericArray,
4741
NumericDtype,
4842
)
49-
from pandas.core.ops import invalid_comparison
5043
from pandas.core.tools.numeric import to_numeric
5144

5245

@@ -418,51 +411,6 @@ def _values_for_argsort(self) -> np.ndarray:
418411
data[self._mask] = data.min() - 1
419412
return data
420413

421-
def _cmp_method(self, other, op):
422-
from pandas.core.arrays import BooleanArray
423-
424-
mask = None
425-
426-
if isinstance(other, BaseMaskedArray):
427-
other, mask = other._data, other._mask
428-
429-
elif is_list_like(other):
430-
other = np.asarray(other)
431-
if other.ndim > 1:
432-
raise NotImplementedError("can only perform ops with 1-d structures")
433-
if len(self) != len(other):
434-
raise ValueError("Lengths must match to compare")
435-
436-
if other is libmissing.NA:
437-
# numpy does not handle pd.NA well as "other" scalar (it returns
438-
# a scalar False instead of an array)
439-
# This may be fixed by NA.__array_ufunc__. Revisit this check
440-
# once that's implemented.
441-
result = np.zeros(self._data.shape, dtype="bool")
442-
mask = np.ones(self._data.shape, dtype="bool")
443-
else:
444-
with warnings.catch_warnings():
445-
# numpy may show a FutureWarning:
446-
# elementwise comparison failed; returning scalar instead,
447-
# but in the future will perform elementwise comparison
448-
# before returning NotImplemented. We fall back to the correct
449-
# behavior today, so that should be fine to ignore.
450-
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
451-
with np.errstate(all="ignore"):
452-
method = getattr(self._data, f"__{op.__name__}__")
453-
result = method(other)
454-
455-
if result is NotImplemented:
456-
result = invalid_comparison(self._data, other, op)
457-
458-
# nans propagate
459-
if mask is None:
460-
mask = self._mask.copy()
461-
else:
462-
mask = self._mask | mask
463-
464-
return BooleanArray(result, mask)
465-
466414
def sum(self, *, skipna=True, min_count=0, axis: int | None = 0, **kwargs):
467415
nv.validate_sum((), kwargs)
468416
return super()._reduce("sum", skipna=skipna, min_count=min_count, axis=axis)

pandas/core/arrays/masked.py

+48
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
TypeVar,
88
overload,
99
)
10+
import warnings
1011

1112
import numpy as np
1213

@@ -40,6 +41,7 @@
4041
is_dtype_equal,
4142
is_float_dtype,
4243
is_integer_dtype,
44+
is_list_like,
4345
is_object_dtype,
4446
is_scalar,
4547
is_string_dtype,
@@ -66,6 +68,7 @@
6668
from pandas.core.arraylike import OpsMixin
6769
from pandas.core.arrays import ExtensionArray
6870
from pandas.core.indexers import check_array_indexer
71+
from pandas.core.ops import invalid_comparison
6972

7073
if TYPE_CHECKING:
7174
from pandas import Series
@@ -482,6 +485,51 @@ def _hasna(self) -> bool:
482485
# error: Incompatible return value type (got "bool_", expected "bool")
483486
return self._mask.any() # type: ignore[return-value]
484487

488+
def _cmp_method(self, other, op) -> BooleanArray:
489+
from pandas.core.arrays import BooleanArray
490+
491+
mask = None
492+
493+
if isinstance(other, BaseMaskedArray):
494+
other, mask = other._data, other._mask
495+
496+
elif is_list_like(other):
497+
other = np.asarray(other)
498+
if other.ndim > 1:
499+
raise NotImplementedError("can only perform ops with 1-d structures")
500+
if len(self) != len(other):
501+
raise ValueError("Lengths must match to compare")
502+
503+
if other is libmissing.NA:
504+
# numpy does not handle pd.NA well as "other" scalar (it returns
505+
# a scalar False instead of an array)
506+
# This may be fixed by NA.__array_ufunc__. Revisit this check
507+
# once that's implemented.
508+
result = np.zeros(self._data.shape, dtype="bool")
509+
mask = np.ones(self._data.shape, dtype="bool")
510+
else:
511+
with warnings.catch_warnings():
512+
# numpy may show a FutureWarning:
513+
# elementwise comparison failed; returning scalar instead,
514+
# but in the future will perform elementwise comparison
515+
# before returning NotImplemented. We fall back to the correct
516+
# behavior today, so that should be fine to ignore.
517+
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
518+
with np.errstate(all="ignore"):
519+
method = getattr(self._data, f"__{op.__name__}__")
520+
result = method(other)
521+
522+
if result is NotImplemented:
523+
result = invalid_comparison(self._data, other, op)
524+
525+
# nans propagate
526+
if mask is None:
527+
mask = self._mask.copy()
528+
else:
529+
mask = self._mask | mask
530+
531+
return BooleanArray(result, mask, copy=False)
532+
485533
def isna(self) -> np.ndarray:
486534
return self._mask.copy()
487535

0 commit comments

Comments
 (0)