diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index 5b1335c1a834e..e2ee3deb5396e 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -12,8 +12,8 @@ from .generic import (ABCCategorical, ABCPeriodIndex, ABCDatetimeIndex, ABCSeries, ABCSparseArray, ABCSparseSeries, ABCCategoricalIndex, - ABCIndexClass) -from .inference import is_string_like + ABCIndexClass, ABCDateOffset) +from .inference import is_string_like, is_list_like from .inference import * # noqa @@ -266,6 +266,37 @@ def is_datetimetz(arr): is_datetime64tz_dtype(arr)) +def is_offsetlike(arr_or_obj): + """ + Check if obj or all elements of list-like is DateOffset + + Parameters + ---------- + arr_or_obj : object + + Returns + ------- + boolean : Whether the object is a DateOffset or listlike of DatetOffsets + + Examples + -------- + >>> is_offsetlike(pd.DateOffset(days=1)) + True + >>> is_offsetlike('offset') + False + >>> is_offsetlike([pd.offsets.Minute(4), pd.offsets.MonthEnd()]) + True + >>> is_offsetlike(np.array([pd.DateOffset(months=3), pd.Timestamp.now()])) + False + """ + if isinstance(arr_or_obj, ABCDateOffset): + return True + elif (is_list_like(arr_or_obj) and len(arr_or_obj) and + is_object_dtype(arr_or_obj)): + return all(isinstance(x, ABCDateOffset) for x in arr_or_obj) + return False + + def is_period(arr): """ Check whether an array-like is a periodical index. diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 2fb0cbb14c225..ca6d888625873 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -30,7 +30,7 @@ is_object_dtype, is_timedelta64_dtype, is_datetime64_dtype, is_datetime64tz_dtype, is_bool_dtype, is_datetimetz, - is_list_like, + is_list_like, is_offsetlike, is_scalar, _ensure_object) from pandas.core.dtypes.cast import maybe_upcast_putmask, find_common_type @@ -38,8 +38,7 @@ ABCSeries, ABCDataFrame, ABCIndex, - ABCPeriodIndex, - ABCDateOffset) + ABCPeriodIndex) # ----------------------------------------------------------------------------- # Functions that add arithmetic methods to objects, given arithmetic factory @@ -363,7 +362,7 @@ def __init__(self, left, right, name, na_op): rvalues = self._convert_to_array(right, name=name, other=lvalues) # left - self.is_offset_lhs = self._is_offset(left) + self.is_offset_lhs = is_offsetlike(left) self.is_timedelta_lhs = is_timedelta64_dtype(lvalues) self.is_datetime64_lhs = is_datetime64_dtype(lvalues) self.is_datetime64tz_lhs = is_datetime64tz_dtype(lvalues) @@ -373,7 +372,7 @@ def __init__(self, left, right, name, na_op): self.is_floating_lhs = left.dtype.kind == 'f' # right - self.is_offset_rhs = self._is_offset(right) + self.is_offset_rhs = is_offsetlike(right) self.is_datetime64_rhs = is_datetime64_dtype(rvalues) self.is_datetime64tz_rhs = is_datetime64tz_dtype(rvalues) self.is_datetime_rhs = (self.is_datetime64_rhs or @@ -515,7 +514,7 @@ def _convert_to_array(self, values, name=None, other=None): values = np.empty(values.shape, dtype=other.dtype) values[:] = iNaT return values - elif self._is_offset(values): + elif is_offsetlike(values): return values else: raise TypeError("incompatible type [{dtype}] for a " @@ -618,15 +617,6 @@ def f(x): return lvalues, rvalues - def _is_offset(self, arr_or_obj): - """ check if obj or all elements of list-like is DateOffset """ - if isinstance(arr_or_obj, ABCDateOffset): - return True - elif (is_list_like(arr_or_obj) and len(arr_or_obj) and - is_object_dtype(arr_or_obj)): - return all(isinstance(x, ABCDateOffset) for x in arr_or_obj) - return False - def _align_method_SERIES(left, right, align_asobject=False): """ align lhs and rhs Series """ diff --git a/pandas/tests/dtypes/test_common.py b/pandas/tests/dtypes/test_common.py index 2146704fea95f..bfec229d32b22 100644 --- a/pandas/tests/dtypes/test_common.py +++ b/pandas/tests/dtypes/test_common.py @@ -537,6 +537,19 @@ def test_is_complex_dtype(): assert com.is_complex_dtype(np.array([1 + 1j, 5])) +def test_is_offsetlike(): + assert com.is_offsetlike(np.array([pd.DateOffset(month=3), + pd.offsets.Nano()])) + assert com.is_offsetlike(pd.offsets.MonthEnd()) + assert com.is_offsetlike(pd.Index([pd.DateOffset(second=1)])) + + assert not com.is_offsetlike(pd.Timedelta(1)) + assert not com.is_offsetlike(np.array([1 + 1j, 5])) + + # mixed case + assert not com.is_offsetlike(np.array([pd.DateOffset(), pd.Timestamp(0)])) + + @pytest.mark.parametrize('input_param,result', [ (int, np.dtype(int)), ('int32', np.dtype('int32')),