From bc950776e34e01c34be2073f21022d3301bcf0cc Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 29 Aug 2018 10:40:23 -0700 Subject: [PATCH 1/2] CLN: use dispatch_to_series where possible --- pandas/core/frame.py | 29 ++++++++++++++++------------- pandas/core/ops.py | 7 +++++++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 74f760f382c76..a9da9c2587318 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4806,21 +4806,29 @@ def _arith_op(left, right): return ops.dispatch_to_series(this, other, _arith_op) else: result = _arith_op(this.values, other.values) - - return self._constructor(result, index=new_index, columns=new_columns, - copy=False) + return self._constructor(result, + index=new_index, columns=new_columns, + copy=False) def _combine_match_index(self, other, func, level=None): left, right = self.align(other, join='outer', axis=0, level=level, copy=False) - new_data = func(left.values.T, right.values).T - return self._constructor(new_data, - index=left.index, columns=self.columns, - copy=False) + assert left.index.equals(right.index) + + if left._is_mixed_type or right._is_mixed_type: + # Operate column-wise to avoid expensive copy/cast + return ops.dispatch_to_series(left, right, func) + else: + # Fastpath; operate directly on data + new_data = func(left.values.T, right.values).T + return self._constructor(new_data, + index=left.index, columns=self.columns, + copy=False) def _combine_match_columns(self, other, func, level=None, try_cast=True): left, right = self.align(other, join='outer', axis=1, level=level, copy=False) + assert left.columns.equals(right.index) new_data = left._data.eval(func=func, other=right, axes=[left.columns, self.index], @@ -4829,12 +4837,7 @@ def _combine_match_columns(self, other, func, level=None, try_cast=True): def _combine_const(self, other, func, errors='raise', try_cast=True): if lib.is_scalar(other) or np.ndim(other) == 0: - new_data = {i: func(self.iloc[:, i], other) - for i, col in enumerate(self.columns)} - - result = self._constructor(new_data, index=self.index, copy=False) - result.columns = self.columns - return result + return ops.dispatch_to_series(self, other, func) new_data = self._data.eval(func=func, other=other, errors=errors, diff --git a/pandas/core/ops.py b/pandas/core/ops.py index b25809bf074f7..77f691a6f7fb7 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -1638,9 +1638,16 @@ def dispatch_to_series(left, right, func): """ # Note: we use iloc to access columns for compat with cases # with non-unique columns. + right = lib.item_from_zerodim(right) if lib.is_scalar(right): new_data = {i: func(left.iloc[:, i], right) for i in range(len(left.columns))} + + elif (isinstance(right, ABCSeries) and right.index.equals(left.index) and + len(right) != len(left.columns)): + new_data = {i: func(left.iloc[:, i], right) + for i in range(len(left.columns))} + elif isinstance(right, ABCDataFrame): assert right._indexed_same(left) new_data = {i: func(left.iloc[:, i], right.iloc[:, i]) From 0b5f86a8b078e31dfa5f9cc15da7c802b54881c6 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 29 Aug 2018 10:43:44 -0700 Subject: [PATCH 2/2] flake8 fixup --- pandas/core/frame.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index a9da9c2587318..8d99cd80cf3c1 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4822,8 +4822,8 @@ def _combine_match_index(self, other, func, level=None): # Fastpath; operate directly on data new_data = func(left.values.T, right.values).T return self._constructor(new_data, - index=left.index, columns=self.columns, - copy=False) + index=left.index, columns=self.columns, + copy=False) def _combine_match_columns(self, other, func, level=None, try_cast=True): left, right = self.align(other, join='outer', axis=1, level=level,