1053 lines
41 KiB
Python
1053 lines
41 KiB
Python
# -*- coding: utf-8 -*-
|
|
import warnings
|
|
from datetime import datetime, timedelta
|
|
import operator
|
|
|
|
import pytest
|
|
|
|
import numpy as np
|
|
|
|
import pandas as pd
|
|
from pandas.compat.numpy import np_datetime64_compat
|
|
import pandas.util.testing as tm
|
|
from pandas.errors import PerformanceWarning, NullFrequencyError
|
|
from pandas import (Timestamp, Timedelta, Series,
|
|
DatetimeIndex, TimedeltaIndex,
|
|
date_range)
|
|
from pandas.core import ops
|
|
from pandas._libs import tslib
|
|
from pandas._libs.tslibs.offsets import shift_months
|
|
|
|
|
|
@pytest.fixture(params=[None, 'UTC', 'Asia/Tokyo',
|
|
'US/Eastern', 'dateutil/Asia/Singapore',
|
|
'dateutil/US/Pacific'])
|
|
def tz(request):
|
|
return request.param
|
|
|
|
|
|
@pytest.fixture(params=[pd.offsets.Hour(2), timedelta(hours=2),
|
|
np.timedelta64(2, 'h'), Timedelta(hours=2)],
|
|
ids=str)
|
|
def delta(request):
|
|
# Several ways of representing two hours
|
|
return request.param
|
|
|
|
|
|
@pytest.fixture(
|
|
params=[
|
|
datetime(2011, 1, 1),
|
|
DatetimeIndex(['2011-01-01', '2011-01-02']),
|
|
DatetimeIndex(['2011-01-01', '2011-01-02']).tz_localize('US/Eastern'),
|
|
np.datetime64('2011-01-01'),
|
|
Timestamp('2011-01-01')],
|
|
ids=lambda x: type(x).__name__)
|
|
def addend(request):
|
|
return request.param
|
|
|
|
|
|
class TestDatetimeIndexComparisons(object):
|
|
@pytest.mark.parametrize('other', [datetime(2016, 1, 1),
|
|
Timestamp('2016-01-01'),
|
|
np.datetime64('2016-01-01')])
|
|
def test_dti_cmp_datetimelike(self, other, tz):
|
|
dti = pd.date_range('2016-01-01', periods=2, tz=tz)
|
|
if tz is not None:
|
|
if isinstance(other, np.datetime64):
|
|
# no tzaware version available
|
|
return
|
|
elif isinstance(other, Timestamp):
|
|
other = other.tz_localize(dti.tzinfo)
|
|
else:
|
|
other = tslib._localize_pydatetime(other, dti.tzinfo)
|
|
|
|
result = dti == other
|
|
expected = np.array([True, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = dti > other
|
|
expected = np.array([False, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = dti >= other
|
|
expected = np.array([True, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = dti < other
|
|
expected = np.array([False, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = dti <= other
|
|
expected = np.array([True, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
def dti_cmp_non_datetime(self, tz):
|
|
# GH#19301 by convention datetime.date is not considered comparable
|
|
# to Timestamp or DatetimeIndex. This may change in the future.
|
|
dti = pd.date_range('2016-01-01', periods=2, tz=tz)
|
|
|
|
other = datetime(2016, 1, 1).date()
|
|
assert not (dti == other).any()
|
|
assert (dti != other).all()
|
|
with pytest.raises(TypeError):
|
|
dti < other
|
|
with pytest.raises(TypeError):
|
|
dti <= other
|
|
with pytest.raises(TypeError):
|
|
dti > other
|
|
with pytest.raises(TypeError):
|
|
dti >= other
|
|
|
|
@pytest.mark.parametrize('other', [None, np.nan, pd.NaT])
|
|
def test_dti_eq_null_scalar(self, other, tz):
|
|
# GH#19301
|
|
dti = pd.date_range('2016-01-01', periods=2, tz=tz)
|
|
assert not (dti == other).any()
|
|
|
|
@pytest.mark.parametrize('other', [None, np.nan, pd.NaT])
|
|
def test_dti_ne_null_scalar(self, other, tz):
|
|
# GH#19301
|
|
dti = pd.date_range('2016-01-01', periods=2, tz=tz)
|
|
assert (dti != other).all()
|
|
|
|
@pytest.mark.parametrize('other', [None, np.nan])
|
|
def test_dti_cmp_null_scalar_inequality(self, tz, other):
|
|
# GH#19301
|
|
dti = pd.date_range('2016-01-01', periods=2, tz=tz)
|
|
|
|
with pytest.raises(TypeError):
|
|
dti < other
|
|
with pytest.raises(TypeError):
|
|
dti <= other
|
|
with pytest.raises(TypeError):
|
|
dti > other
|
|
with pytest.raises(TypeError):
|
|
dti >= other
|
|
|
|
def test_dti_cmp_nat(self):
|
|
left = pd.DatetimeIndex([pd.Timestamp('2011-01-01'), pd.NaT,
|
|
pd.Timestamp('2011-01-03')])
|
|
right = pd.DatetimeIndex([pd.NaT, pd.NaT, pd.Timestamp('2011-01-03')])
|
|
|
|
for lhs, rhs in [(left, right),
|
|
(left.astype(object), right.astype(object))]:
|
|
result = rhs == lhs
|
|
expected = np.array([False, False, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = lhs != rhs
|
|
expected = np.array([True, True, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
expected = np.array([False, False, False])
|
|
tm.assert_numpy_array_equal(lhs == pd.NaT, expected)
|
|
tm.assert_numpy_array_equal(pd.NaT == rhs, expected)
|
|
|
|
expected = np.array([True, True, True])
|
|
tm.assert_numpy_array_equal(lhs != pd.NaT, expected)
|
|
tm.assert_numpy_array_equal(pd.NaT != lhs, expected)
|
|
|
|
expected = np.array([False, False, False])
|
|
tm.assert_numpy_array_equal(lhs < pd.NaT, expected)
|
|
tm.assert_numpy_array_equal(pd.NaT > lhs, expected)
|
|
|
|
def test_dti_cmp_nat_behaves_like_float_cmp_nan(self):
|
|
fidx1 = pd.Index([1.0, np.nan, 3.0, np.nan, 5.0, 7.0])
|
|
fidx2 = pd.Index([2.0, 3.0, np.nan, np.nan, 6.0, 7.0])
|
|
|
|
didx1 = pd.DatetimeIndex(['2014-01-01', pd.NaT, '2014-03-01', pd.NaT,
|
|
'2014-05-01', '2014-07-01'])
|
|
didx2 = pd.DatetimeIndex(['2014-02-01', '2014-03-01', pd.NaT, pd.NaT,
|
|
'2014-06-01', '2014-07-01'])
|
|
darr = np.array([np_datetime64_compat('2014-02-01 00:00Z'),
|
|
np_datetime64_compat('2014-03-01 00:00Z'),
|
|
np_datetime64_compat('nat'), np.datetime64('nat'),
|
|
np_datetime64_compat('2014-06-01 00:00Z'),
|
|
np_datetime64_compat('2014-07-01 00:00Z')])
|
|
|
|
cases = [(fidx1, fidx2), (didx1, didx2), (didx1, darr)]
|
|
|
|
# Check pd.NaT is handles as the same as np.nan
|
|
with tm.assert_produces_warning(None):
|
|
for idx1, idx2 in cases:
|
|
|
|
result = idx1 < idx2
|
|
expected = np.array([True, False, False, False, True, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx2 > idx1
|
|
expected = np.array([True, False, False, False, True, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 <= idx2
|
|
expected = np.array([True, False, False, False, True, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx2 >= idx1
|
|
expected = np.array([True, False, False, False, True, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 == idx2
|
|
expected = np.array([False, False, False, False, False, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 != idx2
|
|
expected = np.array([True, True, True, True, True, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
with tm.assert_produces_warning(None):
|
|
for idx1, val in [(fidx1, np.nan), (didx1, pd.NaT)]:
|
|
result = idx1 < val
|
|
expected = np.array([False, False, False, False, False, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
result = idx1 > val
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 <= val
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
result = idx1 >= val
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 == val
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 != val
|
|
expected = np.array([True, True, True, True, True, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
# Check pd.NaT is handles as the same as np.nan
|
|
with tm.assert_produces_warning(None):
|
|
for idx1, val in [(fidx1, 3), (didx1, datetime(2014, 3, 1))]:
|
|
result = idx1 < val
|
|
expected = np.array([True, False, False, False, False, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
result = idx1 > val
|
|
expected = np.array([False, False, False, False, True, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 <= val
|
|
expected = np.array([True, False, True, False, False, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
result = idx1 >= val
|
|
expected = np.array([False, False, True, False, True, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 == val
|
|
expected = np.array([False, False, True, False, False, False])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = idx1 != val
|
|
expected = np.array([True, True, False, True, True, True])
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
@pytest.mark.parametrize('op', [operator.eq, operator.ne,
|
|
operator.gt, operator.ge,
|
|
operator.lt, operator.le])
|
|
def test_comparison_tzawareness_compat(self, op):
|
|
# GH#18162
|
|
dr = pd.date_range('2016-01-01', periods=6)
|
|
dz = dr.tz_localize('US/Pacific')
|
|
|
|
with pytest.raises(TypeError):
|
|
op(dr, dz)
|
|
with pytest.raises(TypeError):
|
|
op(dr, list(dz))
|
|
with pytest.raises(TypeError):
|
|
op(dz, dr)
|
|
with pytest.raises(TypeError):
|
|
op(dz, list(dr))
|
|
|
|
# Check that there isn't a problem aware-aware and naive-naive do not
|
|
# raise
|
|
assert (dr == dr).all()
|
|
assert (dr == list(dr)).all()
|
|
assert (dz == dz).all()
|
|
assert (dz == list(dz)).all()
|
|
|
|
# Check comparisons against scalar Timestamps
|
|
ts = pd.Timestamp('2000-03-14 01:59')
|
|
ts_tz = pd.Timestamp('2000-03-14 01:59', tz='Europe/Amsterdam')
|
|
|
|
assert (dr > ts).all()
|
|
with pytest.raises(TypeError):
|
|
op(dr, ts_tz)
|
|
|
|
assert (dz > ts_tz).all()
|
|
with pytest.raises(TypeError):
|
|
op(dz, ts)
|
|
|
|
@pytest.mark.parametrize('op', [operator.eq, operator.ne,
|
|
operator.gt, operator.ge,
|
|
operator.lt, operator.le])
|
|
def test_nat_comparison_tzawareness(self, op):
|
|
# GH#19276
|
|
# tzaware DatetimeIndex should not raise when compared to NaT
|
|
dti = pd.DatetimeIndex(['2014-01-01', pd.NaT, '2014-03-01', pd.NaT,
|
|
'2014-05-01', '2014-07-01'])
|
|
expected = np.array([op == operator.ne] * len(dti))
|
|
result = op(dti, pd.NaT)
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
result = op(dti.tz_localize('US/Pacific'), pd.NaT)
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
def test_dti_cmp_int_raises(self):
|
|
rng = date_range('1/1/2000', periods=10)
|
|
|
|
# raise TypeError for now
|
|
with pytest.raises(TypeError):
|
|
rng < rng[3].value
|
|
|
|
def test_dti_cmp_list(self):
|
|
rng = date_range('1/1/2000', periods=10)
|
|
|
|
result = rng == list(rng)
|
|
expected = rng == rng
|
|
tm.assert_numpy_array_equal(result, expected)
|
|
|
|
|
|
class TestDatetimeIndexArithmetic(object):
|
|
|
|
# -------------------------------------------------------------
|
|
# Invalid Operations
|
|
|
|
@pytest.mark.parametrize('other', [3.14, np.array([2.0, 3.0])])
|
|
@pytest.mark.parametrize('op', [operator.add, ops.radd,
|
|
operator.sub, ops.rsub])
|
|
def test_dti_add_sub_float(self, op, other):
|
|
dti = DatetimeIndex(['2011-01-01', '2011-01-02'], freq='D')
|
|
with pytest.raises(TypeError):
|
|
op(dti, other)
|
|
|
|
def test_dti_add_timestamp_raises(self):
|
|
idx = DatetimeIndex(['2011-01-01', '2011-01-02'])
|
|
msg = "cannot add DatetimeIndex and Timestamp"
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
idx + Timestamp('2011-01-01')
|
|
|
|
def test_dti_radd_timestamp_raises(self):
|
|
idx = DatetimeIndex(['2011-01-01', '2011-01-02'])
|
|
msg = "cannot add DatetimeIndex and Timestamp"
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
Timestamp('2011-01-01') + idx
|
|
|
|
# -------------------------------------------------------------
|
|
# Binary operations DatetimeIndex and int
|
|
|
|
def test_dti_add_int(self, tz, one):
|
|
# Variants of `one` for #19012
|
|
rng = pd.date_range('2000-01-01 09:00', freq='H',
|
|
periods=10, tz=tz)
|
|
result = rng + one
|
|
expected = pd.date_range('2000-01-01 10:00', freq='H',
|
|
periods=10, tz=tz)
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_iadd_int(self, tz, one):
|
|
rng = pd.date_range('2000-01-01 09:00', freq='H',
|
|
periods=10, tz=tz)
|
|
expected = pd.date_range('2000-01-01 10:00', freq='H',
|
|
periods=10, tz=tz)
|
|
rng += one
|
|
tm.assert_index_equal(rng, expected)
|
|
|
|
def test_dti_sub_int(self, tz, one):
|
|
rng = pd.date_range('2000-01-01 09:00', freq='H',
|
|
periods=10, tz=tz)
|
|
result = rng - one
|
|
expected = pd.date_range('2000-01-01 08:00', freq='H',
|
|
periods=10, tz=tz)
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_isub_int(self, tz, one):
|
|
rng = pd.date_range('2000-01-01 09:00', freq='H',
|
|
periods=10, tz=tz)
|
|
expected = pd.date_range('2000-01-01 08:00', freq='H',
|
|
periods=10, tz=tz)
|
|
rng -= one
|
|
tm.assert_index_equal(rng, expected)
|
|
|
|
# -------------------------------------------------------------
|
|
# DatetimeIndex.shift is used in integer addition
|
|
|
|
def test_dti_shift_tzaware(self, tz):
|
|
# GH#9903
|
|
idx = pd.DatetimeIndex([], name='xxx', tz=tz)
|
|
tm.assert_index_equal(idx.shift(0, freq='H'), idx)
|
|
tm.assert_index_equal(idx.shift(3, freq='H'), idx)
|
|
|
|
idx = pd.DatetimeIndex(['2011-01-01 10:00', '2011-01-01 11:00'
|
|
'2011-01-01 12:00'], name='xxx', tz=tz)
|
|
tm.assert_index_equal(idx.shift(0, freq='H'), idx)
|
|
exp = pd.DatetimeIndex(['2011-01-01 13:00', '2011-01-01 14:00'
|
|
'2011-01-01 15:00'], name='xxx', tz=tz)
|
|
tm.assert_index_equal(idx.shift(3, freq='H'), exp)
|
|
exp = pd.DatetimeIndex(['2011-01-01 07:00', '2011-01-01 08:00'
|
|
'2011-01-01 09:00'], name='xxx', tz=tz)
|
|
tm.assert_index_equal(idx.shift(-3, freq='H'), exp)
|
|
|
|
def test_dti_shift_freqs(self):
|
|
# test shift for DatetimeIndex and non DatetimeIndex
|
|
# GH#8083
|
|
drange = pd.date_range('20130101', periods=5)
|
|
result = drange.shift(1)
|
|
expected = pd.DatetimeIndex(['2013-01-02', '2013-01-03', '2013-01-04',
|
|
'2013-01-05',
|
|
'2013-01-06'], freq='D')
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
result = drange.shift(-1)
|
|
expected = pd.DatetimeIndex(['2012-12-31', '2013-01-01', '2013-01-02',
|
|
'2013-01-03', '2013-01-04'],
|
|
freq='D')
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
result = drange.shift(3, freq='2D')
|
|
expected = pd.DatetimeIndex(['2013-01-07', '2013-01-08', '2013-01-09',
|
|
'2013-01-10',
|
|
'2013-01-11'], freq='D')
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_shift_int(self):
|
|
rng = date_range('1/1/2000', periods=20)
|
|
|
|
result = rng + 5
|
|
expected = rng.shift(5)
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
result = rng - 5
|
|
expected = rng.shift(-5)
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_shift_no_freq(self):
|
|
# GH#19147
|
|
dti = pd.DatetimeIndex(['2011-01-01 10:00', '2011-01-01'], freq=None)
|
|
with pytest.raises(NullFrequencyError):
|
|
dti.shift(2)
|
|
|
|
@pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
|
|
def test_dti_shift_localized(self, tzstr):
|
|
dr = date_range('2011/1/1', '2012/1/1', freq='W-FRI')
|
|
dr_tz = dr.tz_localize(tzstr)
|
|
|
|
result = dr_tz.shift(1, '10T')
|
|
assert result.tz == dr_tz.tz
|
|
|
|
# -------------------------------------------------------------
|
|
# Binary operations DatetimeIndex and timedelta-like
|
|
|
|
def test_dti_add_timedeltalike(self, tz, delta):
|
|
rng = pd.date_range('2000-01-01', '2000-02-01', tz=tz)
|
|
result = rng + delta
|
|
expected = pd.date_range('2000-01-01 02:00',
|
|
'2000-02-01 02:00', tz=tz)
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_iadd_timedeltalike(self, tz, delta):
|
|
rng = pd.date_range('2000-01-01', '2000-02-01', tz=tz)
|
|
expected = pd.date_range('2000-01-01 02:00',
|
|
'2000-02-01 02:00', tz=tz)
|
|
rng += delta
|
|
tm.assert_index_equal(rng, expected)
|
|
|
|
def test_dti_sub_timedeltalike(self, tz, delta):
|
|
rng = pd.date_range('2000-01-01', '2000-02-01', tz=tz)
|
|
expected = pd.date_range('1999-12-31 22:00',
|
|
'2000-01-31 22:00', tz=tz)
|
|
result = rng - delta
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_isub_timedeltalike(self, tz, delta):
|
|
rng = pd.date_range('2000-01-01', '2000-02-01', tz=tz)
|
|
expected = pd.date_range('1999-12-31 22:00',
|
|
'2000-01-31 22:00', tz=tz)
|
|
rng -= delta
|
|
tm.assert_index_equal(rng, expected)
|
|
|
|
# -------------------------------------------------------------
|
|
# Binary operations DatetimeIndex and TimedeltaIndex/array
|
|
def test_dti_add_tdi(self, tz):
|
|
# GH 17558
|
|
dti = DatetimeIndex([Timestamp('2017-01-01', tz=tz)] * 10)
|
|
tdi = pd.timedelta_range('0 days', periods=10)
|
|
expected = pd.date_range('2017-01-01', periods=10, tz=tz)
|
|
|
|
# add with TimdeltaIndex
|
|
result = dti + tdi
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
result = tdi + dti
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
# add with timedelta64 array
|
|
result = dti + tdi.values
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
result = tdi.values + dti
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_iadd_tdi(self, tz):
|
|
# GH 17558
|
|
dti = DatetimeIndex([Timestamp('2017-01-01', tz=tz)] * 10)
|
|
tdi = pd.timedelta_range('0 days', periods=10)
|
|
expected = pd.date_range('2017-01-01', periods=10, tz=tz)
|
|
|
|
# iadd with TimdeltaIndex
|
|
result = DatetimeIndex([Timestamp('2017-01-01', tz=tz)] * 10)
|
|
result += tdi
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
result = pd.timedelta_range('0 days', periods=10)
|
|
result += dti
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
# iadd with timedelta64 array
|
|
result = DatetimeIndex([Timestamp('2017-01-01', tz=tz)] * 10)
|
|
result += tdi.values
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
result = pd.timedelta_range('0 days', periods=10)
|
|
result += dti
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_sub_tdi(self, tz):
|
|
# GH 17558
|
|
dti = DatetimeIndex([Timestamp('2017-01-01', tz=tz)] * 10)
|
|
tdi = pd.timedelta_range('0 days', periods=10)
|
|
expected = pd.date_range('2017-01-01', periods=10, tz=tz, freq='-1D')
|
|
|
|
# sub with TimedeltaIndex
|
|
result = dti - tdi
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
msg = 'cannot subtract .*TimedeltaIndex'
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
tdi - dti
|
|
|
|
# sub with timedelta64 array
|
|
result = dti - tdi.values
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
msg = 'cannot perform __neg__ with this index type:'
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
tdi.values - dti
|
|
|
|
def test_dti_isub_tdi(self, tz):
|
|
# GH 17558
|
|
dti = DatetimeIndex([Timestamp('2017-01-01', tz=tz)] * 10)
|
|
tdi = pd.timedelta_range('0 days', periods=10)
|
|
expected = pd.date_range('2017-01-01', periods=10, tz=tz, freq='-1D')
|
|
|
|
# isub with TimedeltaIndex
|
|
result = DatetimeIndex([Timestamp('2017-01-01', tz=tz)] * 10)
|
|
result -= tdi
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
msg = 'cannot subtract .*TimedeltaIndex'
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
tdi -= dti
|
|
|
|
# isub with timedelta64 array
|
|
result = DatetimeIndex([Timestamp('2017-01-01', tz=tz)] * 10)
|
|
result -= tdi.values
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
msg = '|'.join(['cannot perform __neg__ with this index type:',
|
|
'ufunc subtract cannot use operands with types'])
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
tdi.values -= dti
|
|
|
|
# -------------------------------------------------------------
|
|
# Binary Operations DatetimeIndex and datetime-like
|
|
# TODO: A couple other tests belong in this section. Move them in
|
|
# A PR where there isn't already a giant diff.
|
|
|
|
def test_add_datetimelike_and_dti(self, addend):
|
|
# GH#9631
|
|
dti = DatetimeIndex(['2011-01-01', '2011-01-02'])
|
|
msg = 'cannot add DatetimeIndex and {0}'.format(
|
|
type(addend).__name__)
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
dti + addend
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
addend + dti
|
|
|
|
def test_add_datetimelike_and_dti_tz(self, addend):
|
|
# GH#9631
|
|
dti_tz = DatetimeIndex(['2011-01-01',
|
|
'2011-01-02']).tz_localize('US/Eastern')
|
|
msg = 'cannot add DatetimeIndex and {0}'.format(
|
|
type(addend).__name__)
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
dti_tz + addend
|
|
with tm.assert_raises_regex(TypeError, msg):
|
|
addend + dti_tz
|
|
|
|
# -------------------------------------------------------------
|
|
# __add__/__sub__ with ndarray[datetime64] and ndarray[timedelta64]
|
|
|
|
def test_dti_add_dt64_array_raises(self, tz):
|
|
dti = pd.date_range('2016-01-01', periods=3, tz=tz)
|
|
dtarr = dti.values
|
|
|
|
with pytest.raises(TypeError):
|
|
dti + dtarr
|
|
with pytest.raises(TypeError):
|
|
dtarr + dti
|
|
|
|
def test_dti_sub_dt64_array_naive(self):
|
|
dti = pd.date_range('2016-01-01', periods=3, tz=None)
|
|
dtarr = dti.values
|
|
|
|
expected = dti - dti
|
|
result = dti - dtarr
|
|
tm.assert_index_equal(result, expected)
|
|
result = dtarr - dti
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_sub_dt64_array_aware_raises(self, tz):
|
|
if tz is None:
|
|
return
|
|
dti = pd.date_range('2016-01-01', periods=3, tz=tz)
|
|
dtarr = dti.values
|
|
|
|
with pytest.raises(TypeError):
|
|
dti - dtarr
|
|
with pytest.raises(TypeError):
|
|
dtarr - dti
|
|
|
|
def test_dti_add_td64_array(self, tz):
|
|
dti = pd.date_range('2016-01-01', periods=3, tz=tz)
|
|
tdi = dti - dti.shift(1)
|
|
tdarr = tdi.values
|
|
|
|
expected = dti + tdi
|
|
result = dti + tdarr
|
|
tm.assert_index_equal(result, expected)
|
|
result = tdarr + dti
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
def test_dti_sub_td64_array(self, tz):
|
|
dti = pd.date_range('2016-01-01', periods=3, tz=tz)
|
|
tdi = dti - dti.shift(1)
|
|
tdarr = tdi.values
|
|
|
|
expected = dti - tdi
|
|
result = dti - tdarr
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
with pytest.raises(TypeError):
|
|
tdarr - dti
|
|
|
|
# -------------------------------------------------------------
|
|
|
|
def test_sub_dti_dti(self):
|
|
# previously performed setop (deprecated in 0.16.0), now changed to
|
|
# return subtraction -> TimeDeltaIndex (GH ...)
|
|
|
|
dti = date_range('20130101', periods=3)
|
|
dti_tz = date_range('20130101', periods=3).tz_localize('US/Eastern')
|
|
dti_tz2 = date_range('20130101', periods=3).tz_localize('UTC')
|
|
expected = TimedeltaIndex([0, 0, 0])
|
|
|
|
result = dti - dti
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
result = dti_tz - dti_tz
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
with pytest.raises(TypeError):
|
|
dti_tz - dti
|
|
|
|
with pytest.raises(TypeError):
|
|
dti - dti_tz
|
|
|
|
with pytest.raises(TypeError):
|
|
dti_tz - dti_tz2
|
|
|
|
# isub
|
|
dti -= dti
|
|
tm.assert_index_equal(dti, expected)
|
|
|
|
# different length raises ValueError
|
|
dti1 = date_range('20130101', periods=3)
|
|
dti2 = date_range('20130101', periods=4)
|
|
with pytest.raises(ValueError):
|
|
dti1 - dti2
|
|
|
|
# NaN propagation
|
|
dti1 = DatetimeIndex(['2012-01-01', np.nan, '2012-01-03'])
|
|
dti2 = DatetimeIndex(['2012-01-02', '2012-01-03', np.nan])
|
|
expected = TimedeltaIndex(['1 days', np.nan, np.nan])
|
|
result = dti2 - dti1
|
|
tm.assert_index_equal(result, expected)
|
|
|
|
@pytest.mark.parametrize('freq', [None, 'D'])
|
|
def test_sub_period(self, freq):
|
|
# GH#13078
|
|
# not supported, check TypeError
|
|
p = pd.Period('2011-01-01', freq='D')
|
|
|
|
idx = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=freq)
|
|
|
|
with pytest.raises(TypeError):
|
|
idx - p
|
|
|
|
with pytest.raises(TypeError):
|
|
p - idx
|
|
|
|
def test_ufunc_coercions(self):
|
|
idx = date_range('2011-01-01', periods=3, freq='2D', name='x')
|
|
|
|
delta = np.timedelta64(1, 'D')
|
|
for result in [idx + delta, np.add(idx, delta)]:
|
|
assert isinstance(result, DatetimeIndex)
|
|
exp = date_range('2011-01-02', periods=3, freq='2D', name='x')
|
|
tm.assert_index_equal(result, exp)
|
|
assert result.freq == '2D'
|
|
|
|
for result in [idx - delta, np.subtract(idx, delta)]:
|
|
assert isinstance(result, DatetimeIndex)
|
|
exp = date_range('2010-12-31', periods=3, freq='2D', name='x')
|
|
tm.assert_index_equal(result, exp)
|
|
assert result.freq == '2D'
|
|
|
|
delta = np.array([np.timedelta64(1, 'D'), np.timedelta64(2, 'D'),
|
|
np.timedelta64(3, 'D')])
|
|
for result in [idx + delta, np.add(idx, delta)]:
|
|
assert isinstance(result, DatetimeIndex)
|
|
exp = DatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-08'],
|
|
freq='3D', name='x')
|
|
tm.assert_index_equal(result, exp)
|
|
assert result.freq == '3D'
|
|
|
|
for result in [idx - delta, np.subtract(idx, delta)]:
|
|
assert isinstance(result, DatetimeIndex)
|
|
exp = DatetimeIndex(['2010-12-31', '2011-01-01', '2011-01-02'],
|
|
freq='D', name='x')
|
|
tm.assert_index_equal(result, exp)
|
|
assert result.freq == 'D'
|
|
|
|
def test_datetimeindex_sub_timestamp_overflow(self):
|
|
dtimax = pd.to_datetime(['now', pd.Timestamp.max])
|
|
dtimin = pd.to_datetime(['now', pd.Timestamp.min])
|
|
|
|
tsneg = Timestamp('1950-01-01')
|
|
ts_neg_variants = [tsneg,
|
|
tsneg.to_pydatetime(),
|
|
tsneg.to_datetime64().astype('datetime64[ns]'),
|
|
tsneg.to_datetime64().astype('datetime64[D]')]
|
|
|
|
tspos = Timestamp('1980-01-01')
|
|
ts_pos_variants = [tspos,
|
|
tspos.to_pydatetime(),
|
|
tspos.to_datetime64().astype('datetime64[ns]'),
|
|
tspos.to_datetime64().astype('datetime64[D]')]
|
|
|
|
for variant in ts_neg_variants:
|
|
with pytest.raises(OverflowError):
|
|
dtimax - variant
|
|
|
|
expected = pd.Timestamp.max.value - tspos.value
|
|
for variant in ts_pos_variants:
|
|
res = dtimax - variant
|
|
assert res[1].value == expected
|
|
|
|
expected = pd.Timestamp.min.value - tsneg.value
|
|
for variant in ts_neg_variants:
|
|
res = dtimin - variant
|
|
assert res[1].value == expected
|
|
|
|
for variant in ts_pos_variants:
|
|
with pytest.raises(OverflowError):
|
|
dtimin - variant
|
|
|
|
@pytest.mark.parametrize('names', [('foo', None, None),
|
|
('baz', 'bar', None),
|
|
('bar', 'bar', 'bar')])
|
|
@pytest.mark.parametrize('tz', [None, 'America/Chicago'])
|
|
def test_dti_add_series(self, tz, names):
|
|
# GH#13905
|
|
index = DatetimeIndex(['2016-06-28 05:30', '2016-06-28 05:31'],
|
|
tz=tz, name=names[0])
|
|
ser = Series([Timedelta(seconds=5)] * 2,
|
|
index=index, name=names[1])
|
|
expected = Series(index + Timedelta(seconds=5),
|
|
index=index, name=names[2])
|
|
|
|
# passing name arg isn't enough when names[2] is None
|
|
expected.name = names[2]
|
|
assert expected.dtype == index.dtype
|
|
result = ser + index
|
|
tm.assert_series_equal(result, expected)
|
|
result2 = index + ser
|
|
tm.assert_series_equal(result2, expected)
|
|
|
|
expected = index + Timedelta(seconds=5)
|
|
result3 = ser.values + index
|
|
tm.assert_index_equal(result3, expected)
|
|
result4 = index + ser.values
|
|
tm.assert_index_equal(result4, expected)
|
|
|
|
def test_dti_add_offset_array(self, tz):
|
|
# GH#18849
|
|
dti = pd.date_range('2017-01-01', periods=2, tz=tz)
|
|
other = np.array([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)])
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res = dti + other
|
|
expected = DatetimeIndex([dti[n] + other[n] for n in range(len(dti))],
|
|
name=dti.name, freq='infer')
|
|
tm.assert_index_equal(res, expected)
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res2 = other + dti
|
|
tm.assert_index_equal(res2, expected)
|
|
|
|
@pytest.mark.parametrize('names', [(None, None, None),
|
|
('foo', 'bar', None),
|
|
('foo', 'foo', 'foo')])
|
|
def test_dti_add_offset_index(self, tz, names):
|
|
# GH#18849, GH#19744
|
|
dti = pd.date_range('2017-01-01', periods=2, tz=tz, name=names[0])
|
|
other = pd.Index([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)],
|
|
name=names[1])
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res = dti + other
|
|
expected = DatetimeIndex([dti[n] + other[n] for n in range(len(dti))],
|
|
name=names[2], freq='infer')
|
|
tm.assert_index_equal(res, expected)
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res2 = other + dti
|
|
tm.assert_index_equal(res2, expected)
|
|
|
|
def test_dti_sub_offset_array(self, tz):
|
|
# GH#18824
|
|
dti = pd.date_range('2017-01-01', periods=2, tz=tz)
|
|
other = np.array([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)])
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res = dti - other
|
|
expected = DatetimeIndex([dti[n] - other[n] for n in range(len(dti))],
|
|
name=dti.name, freq='infer')
|
|
tm.assert_index_equal(res, expected)
|
|
|
|
@pytest.mark.parametrize('names', [(None, None, None),
|
|
('foo', 'bar', None),
|
|
('foo', 'foo', 'foo')])
|
|
def test_dti_sub_offset_index(self, tz, names):
|
|
# GH#18824, GH#19744
|
|
dti = pd.date_range('2017-01-01', periods=2, tz=tz, name=names[0])
|
|
other = pd.Index([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)],
|
|
name=names[1])
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res = dti - other
|
|
expected = DatetimeIndex([dti[n] - other[n] for n in range(len(dti))],
|
|
name=names[2], freq='infer')
|
|
tm.assert_index_equal(res, expected)
|
|
|
|
@pytest.mark.parametrize('names', [(None, None, None),
|
|
('foo', 'bar', None),
|
|
('foo', 'foo', 'foo')])
|
|
def test_dti_with_offset_series(self, tz, names):
|
|
# GH#18849
|
|
dti = pd.date_range('2017-01-01', periods=2, tz=tz, name=names[0])
|
|
other = Series([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)],
|
|
name=names[1])
|
|
|
|
expected_add = Series([dti[n] + other[n] for n in range(len(dti))],
|
|
name=names[2])
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res = dti + other
|
|
tm.assert_series_equal(res, expected_add)
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res2 = other + dti
|
|
tm.assert_series_equal(res2, expected_add)
|
|
|
|
expected_sub = Series([dti[n] - other[n] for n in range(len(dti))],
|
|
name=names[2])
|
|
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
res3 = dti - other
|
|
tm.assert_series_equal(res3, expected_sub)
|
|
|
|
def test_dti_add_offset_tzaware(self, tz_aware_fixture):
|
|
timezone = tz_aware_fixture
|
|
if timezone == 'US/Pacific':
|
|
dates = date_range('2012-11-01', periods=3, tz=timezone)
|
|
offset = dates + pd.offsets.Hour(5)
|
|
assert dates[0] + pd.offsets.Hour(5) == offset[0]
|
|
|
|
dates = date_range('2010-11-01 00:00',
|
|
periods=3, tz=timezone, freq='H')
|
|
expected = DatetimeIndex(['2010-11-01 05:00', '2010-11-01 06:00',
|
|
'2010-11-01 07:00'], freq='H', tz=timezone)
|
|
|
|
offset = dates + pd.offsets.Hour(5)
|
|
tm.assert_index_equal(offset, expected)
|
|
offset = dates + np.timedelta64(5, 'h')
|
|
tm.assert_index_equal(offset, expected)
|
|
offset = dates + timedelta(hours=5)
|
|
tm.assert_index_equal(offset, expected)
|
|
|
|
|
|
@pytest.mark.parametrize('klass,assert_func', [
|
|
(Series, tm.assert_series_equal),
|
|
(DatetimeIndex, tm.assert_index_equal)])
|
|
def test_dt64_with_offset_array(klass, assert_func):
|
|
# GH#10699
|
|
# array of offsets
|
|
box = Series if klass is Series else pd.Index
|
|
with tm.assert_produces_warning(PerformanceWarning):
|
|
s = klass([Timestamp('2000-1-1'), Timestamp('2000-2-1')])
|
|
result = s + box([pd.offsets.DateOffset(years=1),
|
|
pd.offsets.MonthEnd()])
|
|
exp = klass([Timestamp('2001-1-1'), Timestamp('2000-2-29')])
|
|
assert_func(result, exp)
|
|
|
|
# same offset
|
|
result = s + box([pd.offsets.DateOffset(years=1),
|
|
pd.offsets.DateOffset(years=1)])
|
|
exp = klass([Timestamp('2001-1-1'), Timestamp('2001-2-1')])
|
|
assert_func(result, exp)
|
|
|
|
|
|
@pytest.mark.parametrize('klass,assert_func', [
|
|
(Series, tm.assert_series_equal),
|
|
(DatetimeIndex, tm.assert_index_equal)])
|
|
def test_dt64_with_DateOffsets_relativedelta(klass, assert_func):
|
|
# GH#10699
|
|
vec = klass([Timestamp('2000-01-05 00:15:00'),
|
|
Timestamp('2000-01-31 00:23:00'),
|
|
Timestamp('2000-01-01'),
|
|
Timestamp('2000-03-31'),
|
|
Timestamp('2000-02-29'),
|
|
Timestamp('2000-12-31'),
|
|
Timestamp('2000-05-15'),
|
|
Timestamp('2001-06-15')])
|
|
|
|
# DateOffset relativedelta fastpath
|
|
relative_kwargs = [('years', 2), ('months', 5), ('days', 3),
|
|
('hours', 5), ('minutes', 10), ('seconds', 2),
|
|
('microseconds', 5)]
|
|
for i, kwd in enumerate(relative_kwargs):
|
|
op = pd.DateOffset(**dict([kwd]))
|
|
assert_func(klass([x + op for x in vec]), vec + op)
|
|
assert_func(klass([x - op for x in vec]), vec - op)
|
|
op = pd.DateOffset(**dict(relative_kwargs[:i + 1]))
|
|
assert_func(klass([x + op for x in vec]), vec + op)
|
|
assert_func(klass([x - op for x in vec]), vec - op)
|
|
|
|
|
|
@pytest.mark.parametrize('cls_and_kwargs', [
|
|
'YearBegin', ('YearBegin', {'month': 5}),
|
|
'YearEnd', ('YearEnd', {'month': 5}),
|
|
'MonthBegin', 'MonthEnd',
|
|
'SemiMonthEnd', 'SemiMonthBegin',
|
|
'Week', ('Week', {'weekday': 3}),
|
|
'BusinessDay', 'BDay', 'QuarterEnd', 'QuarterBegin',
|
|
'CustomBusinessDay', 'CDay', 'CBMonthEnd',
|
|
'CBMonthBegin', 'BMonthBegin', 'BMonthEnd',
|
|
'BusinessHour', 'BYearBegin', 'BYearEnd',
|
|
'BQuarterBegin', ('LastWeekOfMonth', {'weekday': 2}),
|
|
('FY5253Quarter', {'qtr_with_extra_week': 1,
|
|
'startingMonth': 1,
|
|
'weekday': 2,
|
|
'variation': 'nearest'}),
|
|
('FY5253', {'weekday': 0, 'startingMonth': 2, 'variation': 'nearest'}),
|
|
('WeekOfMonth', {'weekday': 2, 'week': 2}),
|
|
'Easter', ('DateOffset', {'day': 4}),
|
|
('DateOffset', {'month': 5})])
|
|
@pytest.mark.parametrize('normalize', [True, False])
|
|
@pytest.mark.parametrize('klass,assert_func', [
|
|
(Series, tm.assert_series_equal),
|
|
(DatetimeIndex, tm.assert_index_equal)])
|
|
def test_dt64_with_DateOffsets(klass, assert_func, normalize, cls_and_kwargs):
|
|
# GH#10699
|
|
# assert these are equal on a piecewise basis
|
|
vec = klass([Timestamp('2000-01-05 00:15:00'),
|
|
Timestamp('2000-01-31 00:23:00'),
|
|
Timestamp('2000-01-01'),
|
|
Timestamp('2000-03-31'),
|
|
Timestamp('2000-02-29'),
|
|
Timestamp('2000-12-31'),
|
|
Timestamp('2000-05-15'),
|
|
Timestamp('2001-06-15')])
|
|
|
|
if isinstance(cls_and_kwargs, tuple):
|
|
# If cls_name param is a tuple, then 2nd entry is kwargs for
|
|
# the offset constructor
|
|
cls_name, kwargs = cls_and_kwargs
|
|
else:
|
|
cls_name = cls_and_kwargs
|
|
kwargs = {}
|
|
|
|
offset_cls = getattr(pd.offsets, cls_name)
|
|
|
|
with warnings.catch_warnings(record=True):
|
|
for n in [0, 5]:
|
|
if (cls_name in ['WeekOfMonth', 'LastWeekOfMonth',
|
|
'FY5253Quarter', 'FY5253'] and n == 0):
|
|
# passing n = 0 is invalid for these offset classes
|
|
continue
|
|
|
|
offset = offset_cls(n, normalize=normalize, **kwargs)
|
|
assert_func(klass([x + offset for x in vec]), vec + offset)
|
|
assert_func(klass([x - offset for x in vec]), vec - offset)
|
|
assert_func(klass([offset + x for x in vec]), offset + vec)
|
|
|
|
|
|
# GH 10699
|
|
@pytest.mark.parametrize('klass,assert_func', zip([Series, DatetimeIndex],
|
|
[tm.assert_series_equal,
|
|
tm.assert_index_equal]))
|
|
def test_datetime64_with_DateOffset(klass, assert_func):
|
|
s = klass(date_range('2000-01-01', '2000-01-31'), name='a')
|
|
result = s + pd.DateOffset(years=1)
|
|
result2 = pd.DateOffset(years=1) + s
|
|
exp = klass(date_range('2001-01-01', '2001-01-31'), name='a')
|
|
assert_func(result, exp)
|
|
assert_func(result2, exp)
|
|
|
|
result = s - pd.DateOffset(years=1)
|
|
exp = klass(date_range('1999-01-01', '1999-01-31'), name='a')
|
|
assert_func(result, exp)
|
|
|
|
s = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
|
|
pd.Timestamp('2000-02-15', tz='US/Central')], name='a')
|
|
result = s + pd.offsets.Day()
|
|
result2 = pd.offsets.Day() + s
|
|
exp = klass([Timestamp('2000-01-16 00:15:00', tz='US/Central'),
|
|
Timestamp('2000-02-16', tz='US/Central')], name='a')
|
|
assert_func(result, exp)
|
|
assert_func(result2, exp)
|
|
|
|
s = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
|
|
pd.Timestamp('2000-02-15', tz='US/Central')], name='a')
|
|
result = s + pd.offsets.MonthEnd()
|
|
result2 = pd.offsets.MonthEnd() + s
|
|
exp = klass([Timestamp('2000-01-31 00:15:00', tz='US/Central'),
|
|
Timestamp('2000-02-29', tz='US/Central')], name='a')
|
|
assert_func(result, exp)
|
|
assert_func(result2, exp)
|
|
|
|
|
|
@pytest.mark.parametrize('years', [-1, 0, 1])
|
|
@pytest.mark.parametrize('months', [-2, 0, 2])
|
|
def test_shift_months(years, months):
|
|
s = DatetimeIndex([Timestamp('2000-01-05 00:15:00'),
|
|
Timestamp('2000-01-31 00:23:00'),
|
|
Timestamp('2000-01-01'),
|
|
Timestamp('2000-02-29'),
|
|
Timestamp('2000-12-31')])
|
|
actual = DatetimeIndex(shift_months(s.asi8, years * 12 + months))
|
|
|
|
raw = [x + pd.offsets.DateOffset(years=years, months=months)
|
|
for x in s]
|
|
expected = DatetimeIndex(raw)
|
|
tm.assert_index_equal(actual, expected)
|