diff --git a/pandas/core/computation/expressions.py b/pandas/core/computation/expressions.py index 68d08fb476950..0e9077e6d557e 100644 --- a/pandas/core/computation/expressions.py +++ b/pandas/core/computation/expressions.py @@ -223,7 +223,7 @@ def evaluate(op, a, b, use_numexpr: bool = True): use_numexpr : bool, default True Whether to try to use numexpr. """ - op_str = _op_str_mapping.get(op, None) + op_str = _op_str_mapping[op] if op_str is not None: use_numexpr = use_numexpr and _bool_arith_check(op_str, a, b) if use_numexpr: diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 3ca160e39787e..18a3d93bd5c3b 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -280,9 +280,8 @@ def dispatch_to_series(left, right, func, axis=None): bm = left._mgr.operate_blockwise(right._mgr, array_op) return type(left)(bm) - elif isinstance(right, ABCSeries) and axis == "columns": - # We only get here if called via _combine_series_frame, - # in which case we specifically want to operate row-by-row + elif isinstance(right, ABCSeries) and axis == 1: + # axis=1 means we want to operate row-by-row assert right.index.equals(left.columns) if right.dtype == "timedelta64[ns]": @@ -292,6 +291,8 @@ def dispatch_to_series(left, right, func, axis=None): right = np.asarray(right) else: right = right._values + # maybe_align_as_frame ensures we do not have an ndarray here + assert not isinstance(right, np.ndarray) arrays = [array_op(l, r) for l, r in zip(left._iter_column_arrays(), right)] @@ -440,35 +441,6 @@ def flex_wrapper(self, other, level=None, fill_value=None, axis=0): # DataFrame -def _combine_series_frame(left, right, func, axis: int): - """ - Apply binary operator `func` to self, other using alignment and fill - conventions determined by the axis argument. - - Parameters - ---------- - left : DataFrame - right : Series - func : binary operator - axis : {0, 1} - - Returns - ------- - result : DataFrame or Dict[int, Series[]] - """ - # We assume that self.align(other, ...) has already been called - - rvalues = right._values - assert not isinstance(rvalues, np.ndarray) # handled by align_series_as_frame - - if axis == 0: - new_data = dispatch_to_series(left, right, func) - else: - new_data = dispatch_to_series(left, right, func, axis="columns") - - return new_data - - def _align_method_FRAME( left, right, axis, flex: Optional[bool] = False, level: Level = None ): @@ -671,7 +643,7 @@ def f(self, other, axis=default_axis, level=None, fill_value=None): elif isinstance(other, ABCSeries): axis = self._get_axis_number(axis) if axis is not None else 1 - new_data = _combine_series_frame(self, other, op, axis=axis) + new_data = dispatch_to_series(self, other, op, axis=axis) else: # in this case we always have `np.ndim(other) == 0` if fill_value is not None: @@ -707,7 +679,7 @@ def f(self, other, axis=default_axis, level=None): elif isinstance(other, ABCSeries): axis = self._get_axis_number(axis) if axis is not None else 1 - new_data = _combine_series_frame(self, other, op, axis=axis) + new_data = dispatch_to_series(self, other, op, axis=axis) else: # in this case we always have `np.ndim(other) == 0` new_data = dispatch_to_series(self, other, op) @@ -730,7 +702,7 @@ def f(self, other): self, other, axis=None, level=None, flex=False ) - axis = "columns" # only relevant for Series other case + axis = 1 # only relevant for Series other case # See GH#4537 for discussion of scalar op behavior new_data = dispatch_to_series(self, other, op, axis=axis) return self._construct_result(new_data) diff --git a/pandas/core/ops/array_ops.py b/pandas/core/ops/array_ops.py index cb1b2f0c37c6f..3379ee56b6ad0 100644 --- a/pandas/core/ops/array_ops.py +++ b/pandas/core/ops/array_ops.py @@ -85,13 +85,7 @@ def masked_arith_op(x: np.ndarray, y, op): yrav = y.ravel() mask = notna(xrav) & ymask.ravel() - if yrav.shape != mask.shape: - # FIXME: GH#5284, GH#5035, GH#19448 - # Without specifically raising here we get mismatched - # errors in Py3 (TypeError) vs Py2 (ValueError) - # Note: Only = an issue in DataFrame case - raise ValueError("Cannot broadcast operands together.") - + # See GH#5284, GH#5035, GH#19448 for historical reference if mask.any(): with np.errstate(all="ignore"): result[mask] = op(xrav[mask], yrav[mask]) @@ -378,13 +372,28 @@ def get_array_op(op): # TODO: avoid getting here return op - op_name = op.__name__.strip("_") + op_name = op.__name__.strip("_").lstrip("r") + if op_name == "arith_op": + # Reached via DataFrame._combine_frame + return op + if op_name in {"eq", "ne", "lt", "le", "gt", "ge"}: return partial(comparison_op, op=op) elif op_name in {"and", "or", "xor", "rand", "ror", "rxor"}: return partial(logical_op, op=op) - else: + elif op_name in { + "add", + "sub", + "mul", + "truediv", + "floordiv", + "mod", + "divmod", + "pow", + }: return partial(arithmetic_op, op=op) + else: + raise NotImplementedError(op_name) def maybe_upcast_datetimelike_array(obj: ArrayLike) -> ArrayLike: