From bf45ca4fc69671fea3e10570b662cb9391141c3a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 26 Sep 2019 08:47:45 -0700 Subject: [PATCH 1/3] CLN: streamline Series _construct_result calls --- pandas/core/ops/__init__.py | 32 ++++++++++++++------------------ pandas/core/series.py | 5 +---- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 06c0e9722c045..176770c8f1d8c 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -566,13 +566,22 @@ def _align_method_SERIES(left, right, align_asobject=False): return left, right -def _construct_result(left, result, index, name, dtype=None): +def _construct_result(left, result, index, name): """ If the raw op result has a non-None name (e.g. it is an Index object) and the name argument is None, then passing name to the constructor will not be enough; we still need to override the name attribute. """ - out = left._constructor(result, index=index, dtype=dtype) + if isinstance(result, tuple): + # produced by divmod or rdivmod + return ( + _construct_result(left, result[0], index=index, name=name), + _construct_result(left, result[1], index=index, name=name), + ) + + # We do not pass dtype to ensure that the Series constructor + # does inference in the case where `result` has object-dtype. + out = left._constructor(result, index=index) out = out.__finalize__(left) # Set the result's name after __finalize__ is called because __finalize__ @@ -581,15 +590,6 @@ def _construct_result(left, result, index, name, dtype=None): return out -def _construct_divmod_result(left, result, index, name, dtype=None): - """divmod returns a tuple of like indexed series instead of a single series. - """ - return ( - _construct_result(left, result[0], index=index, name=name, dtype=dtype), - _construct_result(left, result[1], index=index, name=name, dtype=dtype), - ) - - def _arith_method_SERIES(cls, op, special): """ Wrapper function for Series arithmetic operations, to avoid @@ -598,9 +598,6 @@ def _arith_method_SERIES(cls, op, special): str_rep = _get_opstr(op) op_name = _get_op_name(op, special) eval_kwargs = _gen_eval_kwargs(op_name) - construct_result = ( - _construct_divmod_result if op in [divmod, rdivmod] else _construct_result - ) def wrapper(left, right): if isinstance(right, ABCDataFrame): @@ -612,9 +609,7 @@ def wrapper(left, right): lvalues = extract_array(left, extract_numpy=True) result = arithmetic_op(lvalues, right, op, str_rep, eval_kwargs) - # We do not pass dtype to ensure that the Series constructor - # does inference in the case where `result` has object-dtype. - return construct_result(left, result, index=left.index, name=res_name) + return _construct_result(left, result, index=left.index, name=res_name) wrapper.__name__ = op_name return wrapper @@ -683,6 +678,7 @@ def flex_wrapper(self, other, level=None, fill_value=None, axis=0): # validate axis if axis is not None: self._get_axis_number(axis) + if isinstance(other, ABCSeries): return self._binop(other, op, level=level, fill_value=fill_value) elif isinstance(other, (np.ndarray, list, tuple)): @@ -694,7 +690,7 @@ def flex_wrapper(self, other, level=None, fill_value=None, axis=0): if fill_value is not None: self = self.fillna(fill_value) - return self._constructor(op(self, other), self.index).__finalize__(self) + return op(self, other) flex_wrapper.__name__ = name return flex_wrapper diff --git a/pandas/core/series.py b/pandas/core/series.py index c87e371354f63..276f829d287ab 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2738,10 +2738,7 @@ def _binop(self, other, func, level=None, fill_value=None): result = func(this_vals, other_vals) name = ops.get_op_result_name(self, other) - if func.__name__ in ["divmod", "rdivmod"]: - ret = ops._construct_divmod_result(self, result, new_index, name) - else: - ret = ops._construct_result(self, result, new_index, name) + ret = ops._construct_result(self, result, new_index, name) return ret def combine(self, other, func, fill_value=None): From fa2e88cd45b6ba0a76f21f4c6cff1d1c9c3d3581 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 26 Sep 2019 09:04:54 -0700 Subject: [PATCH 2/3] docstring+types --- pandas/core/ops/__init__.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 176770c8f1d8c..b19144600f9ad 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -566,11 +566,26 @@ def _align_method_SERIES(left, right, align_asobject=False): return left, right -def _construct_result(left, result, index, name): +def _construct_result( + left: ABCSeries, + result: Union[np.ndarray, ABCExtensionArray], + index: ABCIndexClass, + name, +): """ - If the raw op result has a non-None name (e.g. it is an Index object) and - the name argument is None, then passing name to the constructor will - not be enough; we still need to override the name attribute. + Construct an appropriately-labelled Series from the result of an op. + + Parameters + ---------- + left : Series + result : ndarray or ExtensionArray + index : Index + name : object + + Returns + ------- + Series + In the case of __divmod__ or __rdivmod__, a 2-tuple of Series. """ if isinstance(result, tuple): # produced by divmod or rdivmod From a937792da0749b788675f98e55c72acd2fc68b32 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 26 Sep 2019 09:34:12 -0700 Subject: [PATCH 3/3] Rebase+restore missing import --- pandas/core/ops/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 6bb4f54a8f59e..79272c5643281 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -5,7 +5,7 @@ """ import datetime import operator -from typing import Tuple +from typing import Tuple, Union import numpy as np @@ -13,7 +13,12 @@ from pandas.util._decorators import Appender from pandas.core.dtypes.common import is_list_like, is_timedelta64_dtype -from pandas.core.dtypes.generic import ABCDataFrame, ABCIndexClass, ABCSeries +from pandas.core.dtypes.generic import ( + ABCDataFrame, + ABCExtensionArray, + ABCIndexClass, + ABCSeries, +) from pandas.core.dtypes.missing import isna from pandas.core.construction import extract_array