def flex_method_SERIES(op): name = op.__name__.strip("_") doc = make_flex_doc(name, "series") @Appender(doc) def flex_wrapper(self, other, level=None, fill_value=None, axis=0): # validate axis if axis is not None: self._get_axis_number(axis) res_name = get_op_result_name(self, other) if isinstance(other, ABCSeries): return self._binop(other, op, level=level, fill_value=fill_value) elif isinstance(other, (np.ndarray, list, tuple)): if len(other) != len(self): raise ValueError("Lengths must be equal") other = self._constructor(other, self.index) result = self._binop(other, op, level=level, fill_value=fill_value) result.name = res_name return result else: if fill_value is not None: self = self.fillna(fill_value) return op(self, other) flex_wrapper.__name__ = name return flex_wrapper
def flex_arith_method_FRAME(op): op_name = op.__name__.strip("_") default_axis = "columns" na_op = get_array_op(op) doc = make_flex_doc(op_name, "dataframe") @Appender(doc) def f(self, other, axis=default_axis, level=None, fill_value=None): if should_reindex_frame_op(self, other, op, axis, default_axis, fill_value, level): return frame_arith_method_with_reindex(self, other, op) if isinstance(other, ABCSeries) and fill_value is not None: # TODO: We could allow this in cases where we end up going # through the DataFrame path raise NotImplementedError( f"fill_value {fill_value} not supported.") axis = self._get_axis_number(axis) if axis is not None else 1 other = maybe_prepare_scalar_for_op(other, self.shape) self, other = align_method_FRAME(self, other, axis, flex=True, level=level) if isinstance(other, ABCDataFrame): # Another DataFrame new_data = self._combine_frame(other, na_op, fill_value) elif isinstance(other, ABCSeries): new_data = self._dispatch_frame_op(other, op, axis=axis) else: # in this case we always have `np.ndim(other) == 0` if fill_value is not None: self = self.fillna(fill_value) new_data = self._dispatch_frame_op(other, op) return self._construct_result(new_data) f.__name__ = op_name return f