def _sub_period_array(self, other): """ Subtract a Period Array/Index from self. This is only valid if self is itself a Period Array/Index, raises otherwise. Both objects must have the same frequency. Parameters ---------- other : PeriodIndex or PeriodArray Returns ------- result : np.ndarray[object] Array of DateOffset objects; nulls represented by NaT. """ if self.freq != other.freq: msg = DIFFERENT_FREQ.format(cls=type(self).__name__, own_freq=self.freqstr, other_freq=other.freqstr) raise IncompatibleFrequency(msg) new_values = algos.checked_add_with_arr(self.asi8, -other.asi8, arr_mask=self._isnan, b_mask=other._isnan) new_values = np.array([self.freq.base * x for x in new_values]) if self._hasnans or other._hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = NaT return new_values
def _sub_period_array(self, other): """ Subtract one PeriodIndex from another. This is only valid if they have the same frequency. Parameters ---------- other : PeriodIndex Returns ------- result : np.ndarray[object] Array of DateOffset objects; nulls represented by NaT """ if not is_period_dtype(self): raise TypeError("cannot subtract {dtype}-dtype to {cls}" .format(dtype=other.dtype, cls=type(self).__name__)) if not len(self) == len(other): raise ValueError("cannot subtract indices of unequal length") if self.freq != other.freq: msg = _DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr) raise IncompatibleFrequency(msg) new_values = checked_add_with_arr(self.asi8, -other.asi8, arr_mask=self._isnan, b_mask=other._isnan) new_values = np.array([self.freq * x for x in new_values]) if self.hasnans or other.hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = NaT return new_values
def _sub_period_array(self, other): """ Subtract a Period Array/Index from self. This is only valid if self is itself a Period Array/Index, raises otherwise. Both objects must have the same frequency. Parameters ---------- other : PeriodIndex or PeriodArray Returns ------- result : np.ndarray[object] Array of DateOffset objects; nulls represented by NaT. """ self._require_matching_freq(other) new_values = algos.checked_add_with_arr( self.asi8, -other.asi8, arr_mask=self._isnan, b_mask=other._isnan ) new_values = np.array([self.freq.base * x for x in new_values]) if self._hasna or other._hasna: mask = self._isnan | other._isnan new_values[mask] = NaT return new_values
def _addsub_int_array( self, other: np.ndarray, op: Callable[[Any, Any], Any], ) -> "PeriodArray": """ Add or subtract array of integers; equivalent to applying `_time_shift` pointwise. Parameters ---------- other : np.ndarray[integer-dtype] op : {operator.add, operator.sub} Returns ------- result : PeriodArray """ assert op in [operator.add, operator.sub] if op is operator.sub: other = -other res_values = algos.checked_add_with_arr(self.asi8, other, arr_mask=self._isnan) res_values = res_values.view("i8") res_values[self._isnan] = iNaT return type(self)(res_values, freq=self.freq)
def _sub_datelike(self, other): # subtract a datetime from myself, yielding a ndarray[timedelta64[ns]] if isinstance(other, (DatetimeArrayMixin, np.ndarray)): if isinstance(other, np.ndarray): # if other is an ndarray, we assume it is datetime64-dtype other = type(self)(other) if not self._has_same_tz(other): # require tz compat raise TypeError("{cls} subtraction must have the same " "timezones or no timezones" .format(cls=type(self).__name__)) result = self._sub_datelike_dti(other) elif isinstance(other, (datetime, np.datetime64)): assert other is not NaT other = Timestamp(other) if other is NaT: return self - NaT # require tz compat elif not self._has_same_tz(other): raise TypeError("Timestamp subtraction must have the same " "timezones or no timezones") else: i8 = self.asi8 result = checked_add_with_arr(i8, -other.value, arr_mask=self._isnan) result = self._maybe_mask_results(result, fill_value=iNaT) else: raise TypeError("cannot subtract {cls} and {typ}" .format(cls=type(self).__name__, typ=type(other).__name__)) return result.view('timedelta64[ns]')
def _add_delta_td(self, other): # add a delta of a timedeltalike # return the i8 result view inc = tslib._delta_to_nanoseconds(other) new_values = checked_add_with_arr(self.asi8, inc, arr_mask=self._isnan).view("i8") if self.hasnans: new_values[self._isnan] = tslib.iNaT return new_values.view("i8")
def _add_delta_td(self, other): # add a delta of a timedeltalike # return the i8 result view inc = delta_to_nanoseconds(other) new_values = checked_add_with_arr(self.asi8, inc, arr_mask=self._isnan).view('i8') if self.hasnans: new_values[self._isnan] = iNaT return new_values.view('i8')
def _add_timedeltalike_scalar(self, other): """ Add a delta of a timedeltalike return the i8 result view """ inc = delta_to_nanoseconds(other) new_values = checked_add_with_arr(self.asi8, inc, arr_mask=self._isnan).view('i8') new_values = self._maybe_mask_results(new_values) return new_values.view('i8')
def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike from pandas import Timestamp, DatetimeIndex if other is NaT: result = self._nat_new(box=False) else: other = Timestamp(other) i8 = self.asi8 result = checked_add_with_arr(i8, other.value) result = self._maybe_mask_results(result, fill_value=iNaT) return DatetimeIndex(result, name=self.name, copy=False)
def _add_delta_td(self, other): """ Add a delta of a timedeltalike return the i8 result view """ inc = delta_to_nanoseconds(other) new_values = checked_add_with_arr(self.asi8, inc, arr_mask=self._isnan).view('i8') if self.hasnans: new_values[self._isnan] = iNaT return new_values.view('i8')
def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike from pandas import Timestamp, DatetimeIndex if other is tslib.NaT: result = self._nat_new(box=False) else: other = Timestamp(other) i8 = self.asi8 result = checked_add_with_arr(i8, other.value) result = self._maybe_mask_results(result, fill_value=tslib.iNaT) return DatetimeIndex(result, name=self.name, copy=False)
def _addsub_int_array(self, other: Union[ABCPeriodArray, ABCSeries, ABCPeriodIndex, np.ndarray], op: Callable[[Any], Any]) -> ABCPeriodArray: assert op in [operator.add, operator.sub] if op is operator.sub: other = -other res_values = algos.checked_add_with_arr(self.asi8, other, arr_mask=self._isnan) res_values = res_values.view('i8') res_values[self._isnan] = iNaT return type(self)(res_values, freq=self.freq)
def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike from pandas import Timestamp, DatetimeIndex if other is NaT: # GH#19124 pd.NaT is treated like a timedelta return self._nat_new() else: other = Timestamp(other) i8 = self.asi8 result = checked_add_with_arr(i8, other.value, arr_mask=self._isnan) result = self._maybe_mask_results(result, fill_value=iNaT) return DatetimeIndex(result, name=self.name, copy=False)
def _sub_datelike_dti(self, other): """subtraction of two DatetimeIndexes""" if not len(self) == len(other): raise ValueError("cannot add indices of unequal length") self_i8 = self.asi8 other_i8 = other.asi8 new_values = checked_add_with_arr(self_i8, -other_i8, arr_mask=self._isnan) if self.hasnans or other.hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = iNaT return new_values.view('timedelta64[ns]')
def _addsub_int_array( self, other: Union[ExtensionArray, np.ndarray, ABCIndexClass], op: Callable[[Any], Any] ) -> ABCPeriodArray: assert op in [operator.add, operator.sub] if op is operator.sub: other = -other res_values = algos.checked_add_with_arr(self.asi8, other, arr_mask=self._isnan) res_values = res_values.view('i8') res_values[self._isnan] = iNaT return type(self)(res_values, freq=self.freq)
def _add_delta_tdi(self, other): # add a delta of a TimedeltaIndex # return the i8 result view # delta operation if not len(self) == len(other): raise ValueError("cannot add indices of unequal length") self_i8 = self.asi8 other_i8 = other.asi8 new_values = checked_add_with_arr(self_i8, other_i8, arr_mask=self._isnan, b_mask=other._isnan) if self.hasnans or other.hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = tslib.iNaT return new_values.view(self.dtype)
def _addsub_int_array( self, other, # type: Union[Index, ExtensionArray, np.ndarray[int]] op # type: Callable[Any, Any] ): # type: (...) -> PeriodArray assert op in [operator.add, operator.sub] if op is operator.sub: other = -other res_values = algos.checked_add_with_arr(self.asi8, other, arr_mask=self._isnan) res_values = res_values.view('i8') res_values[self._isnan] = iNaT return type(self)(res_values, freq=self.freq)
def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike from pandas import Timestamp, DatetimeIndex if isinstance(other, (DatetimeIndex, np.ndarray)): # if other is an ndarray, we assume it is datetime64-dtype # defer to implementation in DatetimeIndex other = DatetimeIndex(other) return other + self else: assert other is not NaT other = Timestamp(other) i8 = self.asi8 result = checked_add_with_arr(i8, other.value, arr_mask=self._isnan) result = self._maybe_mask_results(result, fill_value=iNaT) return DatetimeIndex(result)
def _add_timedeltalike_scalar(self, other): """ Add a delta of a timedeltalike return the i8 result view """ if isna(other): # i.e np.timedelta64("NaT"), not recognized by delta_to_nanoseconds new_values = np.empty(len(self), dtype='i8') new_values[:] = iNaT return new_values inc = delta_to_nanoseconds(other) new_values = checked_add_with_arr(self.asi8, inc, arr_mask=self._isnan).view('i8') new_values = self._maybe_mask_results(new_values) return new_values.view('i8')
def _add_datetimelike_scalar(self, other): # adding a timedeltaindex to a datetimelike from pandas.core.arrays import DatetimeArrayMixin assert other is not NaT other = Timestamp(other) if other is NaT: # In this case we specifically interpret NaT as a datetime, not # the timedelta interpretation we would get by returning self + NaT result = self.asi8.view('m8[ms]') + NaT.to_datetime64() return DatetimeArrayMixin(result) i8 = self.asi8 result = checked_add_with_arr(i8, other.value, arr_mask=self._isnan) result = self._maybe_mask_results(result) return DatetimeArrayMixin(result, tz=other.tz, freq=self.freq)
def _add_datetimelike_scalar(self, other): # adding a timedeltaindex to a datetimelike from pandas.core.arrays import DatetimeArrayMixin assert other is not NaT other = Timestamp(other) if other is NaT: # In this case we specifically interpret NaT as a datetime, not # the timedelta interpretation we would get by returning self + NaT result = self.asi8.view('m8[ms]') + NaT.to_datetime64() return DatetimeArrayMixin(result) i8 = self.asi8 result = checked_add_with_arr(i8, other.value, arr_mask=self._isnan) result = self._maybe_mask_results(result) return DatetimeArrayMixin(result, tz=other.tz)
def _add_delta_tdi(self, other): # add a delta of a TimedeltaIndex # return the i8 result view # delta operation if not len(self) == len(other): raise ValueError("cannot add indices of unequal length") self_i8 = self.asi8 other_i8 = other.asi8 new_values = checked_add_with_arr(self_i8, other_i8, arr_mask=self._isnan, b_mask=other._isnan) if self.hasnans or other.hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = iNaT return new_values.view(self.dtype)
def _add_datetimelike_scalar(self, other) -> DatetimeArray: # adding a timedeltaindex to a datetimelike from pandas.core.arrays import DatetimeArray assert other is not NaT other = Timestamp(other) if other is NaT: # In this case we specifically interpret NaT as a datetime, not # the timedelta interpretation we would get by returning self + NaT result = self.asi8.view("m8[ms]") + NaT.to_datetime64() return DatetimeArray(result) i8 = self.asi8 result = checked_add_with_arr(i8, other.value, arr_mask=self._isnan) result = self._maybe_mask_results(result) dtype = DatetimeTZDtype(tz=other.tz) if other.tz else DT64NS_DTYPE return DatetimeArray(result, dtype=dtype, freq=self.freq)
def _addsub_int_array( self, other, # type: Union[ExtensionArray, np.ndarray[int]] op # type: Callable[Any, Any] ): # type: (...) -> PeriodArray # TODO: ABCIndexClass is a valid type for other but had to be excluded # due to length of Py2 compatability comment; add back in once migrated # to Py3 syntax assert op in [operator.add, operator.sub] if op is operator.sub: other = -other res_values = algos.checked_add_with_arr(self.asi8, other, arr_mask=self._isnan) res_values = res_values.view('i8') res_values[self._isnan] = iNaT return type(self)(res_values, freq=self.freq)
def _add_timedelta_arraylike( self, other: TimedeltaArray | npt.NDArray[np.timedelta64] ) -> PeriodArray: """ Parameters ---------- other : TimedeltaArray or ndarray[timedelta64] Returns ------- PeriodArray """ freq = self.freq if not isinstance(freq, Tick): # We cannot add timedelta-like to non-tick PeriodArray raise TypeError( f"Cannot add or subtract timedelta64[ns] dtype from {self.dtype}" ) dtype = np.dtype(f"m8[{freq._td64_unit}]") try: delta = astype_overflowsafe(np.asarray(other), dtype=dtype, copy=False, round_ok=False) except ValueError as err: # e.g. if we have minutes freq and try to add 30s # "Cannot losslessly convert units" raise IncompatibleFrequency( "Cannot add/subtract timedelta-like from PeriodArray that is " "not an integer multiple of the PeriodArray's freq.") from err b_mask = np.isnat(delta) res_values = algos.checked_add_with_arr(self.asi8, delta.view("i8"), arr_mask=self._isnan, b_mask=b_mask) np.putmask(res_values, self._isnan | b_mask, iNaT) return type(self)(res_values, freq=self.freq)
def _addsub_int_array(self, other, op): """ Add or subtract array-like of integers equivalent to applying `shift` pointwise. Parameters ---------- other : Index, ExtensionArray, np.ndarray integer-dtype op : {operator.add, operator.sub} Returns ------- result : same class as self """ assert op in [operator.add, operator.sub] if is_period_dtype(self): # easy case for PeriodIndex if op is operator.sub: other = -other res_values = checked_add_with_arr(self.asi8, other, arr_mask=self._isnan) res_values = res_values.view('i8') res_values[self._isnan] = iNaT return self._from_ordinals(res_values, freq=self.freq) elif self.freq is None: # GH#19123 raise NullFrequencyError("Cannot shift with no freq") elif isinstance(self.freq, Tick): # easy case where we can convert to timedelta64 operation td = Timedelta(self.freq) return op(self, td * other) # We should only get here with DatetimeIndex; dispatch # to _addsub_offset_array assert not is_timedelta64_dtype(self) return op(self, np.array(other) * self.freq)
def _add_delta_tdi(self, other): """ Add a delta of a TimedeltaIndex return the i8 result view """ if len(self) != len(other): raise ValueError("cannot add indices of unequal length") if isinstance(other, np.ndarray): # ndarray[timedelta64]; wrap in TimedeltaIndex for op from pandas import TimedeltaIndex other = TimedeltaIndex(other) self_i8 = self.asi8 other_i8 = other.asi8 new_values = checked_add_with_arr(self_i8, other_i8, arr_mask=self._isnan, b_mask=other._isnan) if self.hasnans or other.hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = iNaT return new_values.view('i8')
def _add_delta_tdi(self, other): """ Add a delta of a TimedeltaIndex return the i8 result view """ if len(self) != len(other): raise ValueError("cannot add indices of unequal length") if isinstance(other, np.ndarray): # ndarray[timedelta64]; wrap in TimedeltaIndex for op from pandas import TimedeltaIndex other = TimedeltaIndex(other) self_i8 = self.asi8 other_i8 = other.asi8 new_values = checked_add_with_arr(self_i8, other_i8, arr_mask=self._isnan, b_mask=other._isnan) if self._hasnans or other._hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = iNaT return new_values.view('i8')
def _sub_period_array(self, other): """ Subtract a Period Array/Index from self. This is only valid if self is itself a Period Array/Index, raises otherwise. Both objects must have the same frequency. Parameters ---------- other : PeriodIndex or PeriodArray Returns ------- result : np.ndarray[object] Array of DateOffset objects; nulls represented by NaT """ if not is_period_dtype(self): raise TypeError("cannot subtract {dtype}-dtype from {cls}".format( dtype=other.dtype, cls=type(self).__name__)) if len(self) != len(other): raise ValueError("cannot subtract arrays/indices of " "unequal length") if self.freq != other.freq: msg = DIFFERENT_FREQ.format(cls=type(self).__name__, own_freq=self.freqstr, other_freq=other.freqstr) raise IncompatibleFrequency(msg) new_values = checked_add_with_arr(self.asi8, -other.asi8, arr_mask=self._isnan, b_mask=other._isnan) new_values = np.array([self.freq.base * x for x in new_values]) if self._hasnans or other._hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = NaT return new_values
def _sub_period_array(self, other): """ Subtract a Period Array/Index from self. This is only valid if self is itself a Period Array/Index, raises otherwise. Both objects must have the same frequency. Parameters ---------- other : PeriodIndex or PeriodArray Returns ------- result : np.ndarray[object] Array of DateOffset objects; nulls represented by NaT """ if not is_period_dtype(self): raise TypeError("cannot subtract {dtype}-dtype from {cls}" .format(dtype=other.dtype, cls=type(self).__name__)) if len(self) != len(other): raise ValueError("cannot subtract arrays/indices of " "unequal length") if self.freq != other.freq: msg = DIFFERENT_FREQ.format(cls=type(self).__name__, own_freq=self.freqstr, other_freq=other.freqstr) raise IncompatibleFrequency(msg) new_values = checked_add_with_arr(self.asi8, -other.asi8, arr_mask=self._isnan, b_mask=other._isnan) new_values = np.array([self.freq.base * x for x in new_values]) if self.hasnans or other.hasnans: mask = (self._isnan) | (other._isnan) new_values[mask] = NaT return new_values
def time_add_overflow_arr_rev(self): checked_add_with_arr(self.arr, self.arr_rev)
def test_int64_add_overflow(): # see gh-14068 msg = "Overflow in int64 addition" m = np.iinfo(np.int64).max n = np.iinfo(np.int64).min with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, m]), m) with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, m]), np.array([m, m])) with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([n, n]), n) with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([n, n]), np.array([n, n])) with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, n]), np.array([n, n])) with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, m]), np.array([m, m]), arr_mask=np.array([False, True])) with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, m]), np.array([m, m]), b_mask=np.array([False, True])) with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, m]), np.array([m, m]), arr_mask=np.array([False, True]), b_mask=np.array([False, True])) with tm.assertRaisesRegexp(OverflowError, msg): with tm.assert_produces_warning(RuntimeWarning): algos.checked_add_with_arr(np.array([m, m]), np.array([np.nan, m])) # Check that the nan boolean arrays override whether or not # the addition overflows. We don't check the result but just # the fact that an OverflowError is not raised. with tm.assertRaises(AssertionError): with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, m]), np.array([m, m]), arr_mask=np.array([True, True])) with tm.assertRaises(AssertionError): with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, m]), np.array([m, m]), b_mask=np.array([True, True])) with tm.assertRaises(AssertionError): with tm.assertRaisesRegexp(OverflowError, msg): algos.checked_add_with_arr(np.array([m, m]), np.array([m, m]), arr_mask=np.array([True, False]), b_mask=np.array([False, True]))
def time_add_overflow_scalar(self, scalar): checked_add_with_arr(self.arr, scalar)
def time_add_overflow_both_arg_nan(self): checked_add_with_arr(self.arr, self.arr_mixed, arr_mask=self.arr_nan_1, b_mask=self.arr_nan_2)