Skip to content

Commit 8a4a428

Browse files
authored
CLN: de-duplicate dispatch_to_series calls (#34386)
1 parent 0859bac commit 8a4a428

File tree

1 file changed

+21
-39
lines changed

1 file changed

+21
-39
lines changed

pandas/core/ops/__init__.py

+21-39
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,7 @@
7878
}
7979

8080

81-
COMPARISON_BINOPS: Set[str] = {
82-
"eq",
83-
"ne",
84-
"lt",
85-
"gt",
86-
"le",
87-
"ge",
88-
}
81+
COMPARISON_BINOPS: Set[str] = {"eq", "ne", "lt", "gt", "le", "ge"}
8982

9083
# -----------------------------------------------------------------------------
9184
# Ops Wrapping Utilities
@@ -246,17 +239,17 @@ def fill_binop(left, right, fill_value):
246239
# Dispatch logic
247240

248241

249-
def dispatch_to_series(left, right, func, axis=None):
242+
def dispatch_to_series(left, right, func, axis: Optional[int] = None):
250243
"""
251244
Evaluate the frame operation func(left, right) by evaluating
252245
column-by-column, dispatching to the Series implementation.
253246
254247
Parameters
255248
----------
256249
left : DataFrame
257-
right : scalar or DataFrame
250+
right : scalar, Series, or DataFrame
258251
func : arithmetic or comparison operator
259-
axis : {None, 0, 1, "index", "columns"}
252+
axis : {None, 0, 1}
260253
261254
Returns
262255
-------
@@ -266,7 +259,8 @@ def dispatch_to_series(left, right, func, axis=None):
266259
array_op = get_array_op(func)
267260

268261
right = lib.item_from_zerodim(right)
269-
if lib.is_scalar(right) or np.ndim(right) == 0:
262+
if not is_list_like(right):
263+
# i.e. scalar, faster than checking np.ndim(right) == 0
270264
bm = left._mgr.apply(array_op, right=right)
271265
return type(left)(bm)
272266

@@ -284,15 +278,9 @@ def dispatch_to_series(left, right, func, axis=None):
284278
# axis=1 means we want to operate row-by-row
285279
assert right.index.equals(left.columns)
286280

287-
if right.dtype == "timedelta64[ns]":
288-
# ensure we treat NaT values as the correct dtype
289-
# Note: we do not do this unconditionally as it may be lossy or
290-
# expensive for EA dtypes.
291-
right = np.asarray(right)
292-
else:
293-
right = right._values
294-
# maybe_align_as_frame ensures we do not have an ndarray here
295-
assert not isinstance(right, np.ndarray)
281+
right = right._values
282+
# maybe_align_as_frame ensures we do not have an ndarray here
283+
assert not isinstance(right, np.ndarray)
296284

297285
arrays = [array_op(l, r) for l, r in zip(left._iter_column_arrays(), right)]
298286

@@ -315,7 +303,7 @@ def dispatch_to_series(left, right, func, axis=None):
315303
# Series
316304

317305

318-
def _align_method_SERIES(left, right, align_asobject=False):
306+
def _align_method_SERIES(left: "Series", right, align_asobject: bool = False):
319307
""" align lhs and rhs Series """
320308
# ToDo: Different from _align_method_FRAME, list, tuple and ndarray
321309
# are not coerced here
@@ -596,7 +584,11 @@ def _maybe_align_series_as_frame(frame: "DataFrame", series: "Series", axis: int
596584
rvalues = series._values
597585
if not isinstance(rvalues, np.ndarray):
598586
# TODO(EA2D): no need to special-case with 2D EAs
599-
return series
587+
if rvalues.dtype == "datetime64[ns]" or rvalues.dtype == "timedelta64[ns]":
588+
# We can losslessly+cheaply cast to ndarray
589+
rvalues = np.asarray(rvalues)
590+
else:
591+
return series
600592

601593
if axis == 0:
602594
rvalues = rvalues.reshape(-1, 1)
@@ -633,6 +625,8 @@ def f(self, other, axis=default_axis, level=None, fill_value=None):
633625
# through the DataFrame path
634626
raise NotImplementedError(f"fill_value {fill_value} not supported.")
635627

628+
axis = self._get_axis_number(axis) if axis is not None else 1
629+
636630
# TODO: why are we passing flex=True instead of flex=not special?
637631
# 15 tests fail if we pass flex=not special instead
638632
self, other = _align_method_FRAME(self, other, axis, flex=True, level=level)
@@ -642,7 +636,6 @@ def f(self, other, axis=default_axis, level=None, fill_value=None):
642636
new_data = self._combine_frame(other, na_op, fill_value)
643637

644638
elif isinstance(other, ABCSeries):
645-
axis = self._get_axis_number(axis) if axis is not None else 1
646639
new_data = dispatch_to_series(self, other, op, axis=axis)
647640
else:
648641
# in this case we always have `np.ndim(other) == 0`
@@ -670,20 +663,11 @@ def _flex_comp_method_FRAME(cls: Type["DataFrame"], op, special: bool):
670663

671664
@Appender(doc)
672665
def f(self, other, axis=default_axis, level=None):
666+
axis = self._get_axis_number(axis) if axis is not None else 1
673667

674668
self, other = _align_method_FRAME(self, other, axis, flex=True, level=level)
675669

676-
if isinstance(other, ABCDataFrame):
677-
# Another DataFrame
678-
new_data = dispatch_to_series(self, other, op)
679-
680-
elif isinstance(other, ABCSeries):
681-
axis = self._get_axis_number(axis) if axis is not None else 1
682-
new_data = dispatch_to_series(self, other, op, axis=axis)
683-
else:
684-
# in this case we always have `np.ndim(other) == 0`
685-
new_data = dispatch_to_series(self, other, op)
686-
670+
new_data = dispatch_to_series(self, other, op, axis=axis)
687671
return self._construct_result(new_data)
688672

689673
f.__name__ = op_name
@@ -697,12 +681,10 @@ def _comp_method_FRAME(cls: Type["DataFrame"], op, special: bool):
697681

698682
@Appender(f"Wrapper for comparison method {op_name}")
699683
def f(self, other):
684+
axis = 1 # only relevant for Series other case
700685

701-
self, other = _align_method_FRAME(
702-
self, other, axis=None, level=None, flex=False
703-
)
686+
self, other = _align_method_FRAME(self, other, axis, level=None, flex=False)
704687

705-
axis = 1 # only relevant for Series other case
706688
# See GH#4537 for discussion of scalar op behavior
707689
new_data = dispatch_to_series(self, other, op, axis=axis)
708690
return self._construct_result(new_data)

0 commit comments

Comments
 (0)