Skip to content

Commit a9202fb

Browse files
jbrockmendel1kastner
authored andcommitted
Separate out arithmetic tests for datetimelike indexes (pandas-dev#18049)
1 parent c354271 commit a9202fb

File tree

11 files changed

+1625
-1557
lines changed

11 files changed

+1625
-1557
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# -*- coding: utf-8 -*-
2+
import warnings
3+
from datetime import datetime, timedelta
4+
5+
import pytest
6+
7+
import numpy as np
8+
9+
import pandas as pd
10+
import pandas.util.testing as tm
11+
from pandas.errors import PerformanceWarning
12+
from pandas import (Timestamp, Timedelta, Series,
13+
DatetimeIndex, TimedeltaIndex,
14+
date_range)
15+
16+
17+
class TestDatetimeIndexArithmetic(object):
18+
tz = [None, 'UTC', 'Asia/Tokyo', 'US/Eastern', 'dateutil/Asia/Singapore',
19+
'dateutil/US/Pacific']
20+
21+
def test_add_iadd(self):
22+
for tz in self.tz:
23+
24+
# offset
25+
offsets = [pd.offsets.Hour(2), timedelta(hours=2),
26+
np.timedelta64(2, 'h'), Timedelta(hours=2)]
27+
28+
for delta in offsets:
29+
rng = pd.date_range('2000-01-01', '2000-02-01', tz=tz)
30+
result = rng + delta
31+
expected = pd.date_range('2000-01-01 02:00',
32+
'2000-02-01 02:00', tz=tz)
33+
tm.assert_index_equal(result, expected)
34+
rng += delta
35+
tm.assert_index_equal(rng, expected)
36+
37+
# int
38+
rng = pd.date_range('2000-01-01 09:00', freq='H', periods=10,
39+
tz=tz)
40+
result = rng + 1
41+
expected = pd.date_range('2000-01-01 10:00', freq='H', periods=10,
42+
tz=tz)
43+
tm.assert_index_equal(result, expected)
44+
rng += 1
45+
tm.assert_index_equal(rng, expected)
46+
47+
idx = DatetimeIndex(['2011-01-01', '2011-01-02'])
48+
msg = "cannot add DatetimeIndex and Timestamp"
49+
with tm.assert_raises_regex(TypeError, msg):
50+
idx + Timestamp('2011-01-01')
51+
52+
with tm.assert_raises_regex(TypeError, msg):
53+
Timestamp('2011-01-01') + idx
54+
55+
def test_sub_isub(self):
56+
for tz in self.tz:
57+
58+
# offset
59+
offsets = [pd.offsets.Hour(2), timedelta(hours=2),
60+
np.timedelta64(2, 'h'), Timedelta(hours=2)]
61+
62+
for delta in offsets:
63+
rng = pd.date_range('2000-01-01', '2000-02-01', tz=tz)
64+
expected = pd.date_range('1999-12-31 22:00',
65+
'2000-01-31 22:00', tz=tz)
66+
67+
result = rng - delta
68+
tm.assert_index_equal(result, expected)
69+
rng -= delta
70+
tm.assert_index_equal(rng, expected)
71+
72+
# int
73+
rng = pd.date_range('2000-01-01 09:00', freq='H', periods=10,
74+
tz=tz)
75+
result = rng - 1
76+
expected = pd.date_range('2000-01-01 08:00', freq='H', periods=10,
77+
tz=tz)
78+
tm.assert_index_equal(result, expected)
79+
rng -= 1
80+
tm.assert_index_equal(rng, expected)
81+
82+
@pytest.mark.parametrize('addend', [
83+
datetime(2011, 1, 1),
84+
DatetimeIndex(['2011-01-01', '2011-01-02']),
85+
DatetimeIndex(['2011-01-01', '2011-01-02']).tz_localize('US/Eastern'),
86+
np.datetime64('2011-01-01'),
87+
Timestamp('2011-01-01')])
88+
def test_add_datetimelike_and_dti(self, addend):
89+
# GH#9631
90+
dti = DatetimeIndex(['2011-01-01', '2011-01-02'])
91+
msg = 'cannot add DatetimeIndex and {0}'.format(
92+
type(addend).__name__)
93+
with tm.assert_raises_regex(TypeError, msg):
94+
dti + addend
95+
with tm.assert_raises_regex(TypeError, msg):
96+
addend + dti
97+
98+
@pytest.mark.parametrize('addend', [
99+
datetime(2011, 1, 1),
100+
DatetimeIndex(['2011-01-01', '2011-01-02']),
101+
DatetimeIndex(['2011-01-01', '2011-01-02']).tz_localize('US/Eastern'),
102+
np.datetime64('2011-01-01'),
103+
Timestamp('2011-01-01')])
104+
def test_add_datetimelike_and_dti_tz(self, addend):
105+
# GH#9631
106+
dti_tz = DatetimeIndex(['2011-01-01',
107+
'2011-01-02']).tz_localize('US/Eastern')
108+
msg = 'cannot add DatetimeIndex and {0}'.format(
109+
type(addend).__name__)
110+
with tm.assert_raises_regex(TypeError, msg):
111+
dti_tz + addend
112+
with tm.assert_raises_regex(TypeError, msg):
113+
addend + dti_tz
114+
115+
def test_sub_dti_dti(self):
116+
# previously performed setop (deprecated in 0.16.0), now changed to
117+
# return subtraction -> TimeDeltaIndex (GH ...)
118+
119+
dti = date_range('20130101', periods=3)
120+
dti_tz = date_range('20130101', periods=3).tz_localize('US/Eastern')
121+
dti_tz2 = date_range('20130101', periods=3).tz_localize('UTC')
122+
expected = TimedeltaIndex([0, 0, 0])
123+
124+
result = dti - dti
125+
tm.assert_index_equal(result, expected)
126+
127+
result = dti_tz - dti_tz
128+
tm.assert_index_equal(result, expected)
129+
130+
with pytest.raises(TypeError):
131+
dti_tz - dti
132+
133+
with pytest.raises(TypeError):
134+
dti - dti_tz
135+
136+
with pytest.raises(TypeError):
137+
dti_tz - dti_tz2
138+
139+
# isub
140+
dti -= dti
141+
tm.assert_index_equal(dti, expected)
142+
143+
# different length raises ValueError
144+
dti1 = date_range('20130101', periods=3)
145+
dti2 = date_range('20130101', periods=4)
146+
with pytest.raises(ValueError):
147+
dti1 - dti2
148+
149+
# NaN propagation
150+
dti1 = DatetimeIndex(['2012-01-01', np.nan, '2012-01-03'])
151+
dti2 = DatetimeIndex(['2012-01-02', '2012-01-03', np.nan])
152+
expected = TimedeltaIndex(['1 days', np.nan, np.nan])
153+
result = dti2 - dti1
154+
tm.assert_index_equal(result, expected)
155+
156+
def test_sub_period(self):
157+
# GH 13078
158+
# not supported, check TypeError
159+
p = pd.Period('2011-01-01', freq='D')
160+
161+
for freq in [None, 'D']:
162+
idx = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=freq)
163+
164+
with pytest.raises(TypeError):
165+
idx - p
166+
167+
with pytest.raises(TypeError):
168+
p - idx
169+
170+
def test_ufunc_coercions(self):
171+
idx = date_range('2011-01-01', periods=3, freq='2D', name='x')
172+
173+
delta = np.timedelta64(1, 'D')
174+
for result in [idx + delta, np.add(idx, delta)]:
175+
assert isinstance(result, DatetimeIndex)
176+
exp = date_range('2011-01-02', periods=3, freq='2D', name='x')
177+
tm.assert_index_equal(result, exp)
178+
assert result.freq == '2D'
179+
180+
for result in [idx - delta, np.subtract(idx, delta)]:
181+
assert isinstance(result, DatetimeIndex)
182+
exp = date_range('2010-12-31', periods=3, freq='2D', name='x')
183+
tm.assert_index_equal(result, exp)
184+
assert result.freq == '2D'
185+
186+
delta = np.array([np.timedelta64(1, 'D'), np.timedelta64(2, 'D'),
187+
np.timedelta64(3, 'D')])
188+
for result in [idx + delta, np.add(idx, delta)]:
189+
assert isinstance(result, DatetimeIndex)
190+
exp = DatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-08'],
191+
freq='3D', name='x')
192+
tm.assert_index_equal(result, exp)
193+
assert result.freq == '3D'
194+
195+
for result in [idx - delta, np.subtract(idx, delta)]:
196+
assert isinstance(result, DatetimeIndex)
197+
exp = DatetimeIndex(['2010-12-31', '2011-01-01', '2011-01-02'],
198+
freq='D', name='x')
199+
tm.assert_index_equal(result, exp)
200+
assert result.freq == 'D'
201+
202+
def test_overflow_offset(self):
203+
# xref https://github.com/statsmodels/statsmodels/issues/3374
204+
# ends up multiplying really large numbers which overflow
205+
206+
t = Timestamp('2017-01-13 00:00:00', freq='D')
207+
offset = 20169940 * pd.offsets.Day(1)
208+
209+
def f():
210+
t + offset
211+
pytest.raises(OverflowError, f)
212+
213+
def f():
214+
offset + t
215+
pytest.raises(OverflowError, f)
216+
217+
def f():
218+
t - offset
219+
pytest.raises(OverflowError, f)
220+
221+
222+
# GH 10699
223+
@pytest.mark.parametrize('klass,assert_func', zip([Series, DatetimeIndex],
224+
[tm.assert_series_equal,
225+
tm.assert_index_equal]))
226+
def test_datetime64_with_DateOffset(klass, assert_func):
227+
s = klass(date_range('2000-01-01', '2000-01-31'), name='a')
228+
result = s + pd.DateOffset(years=1)
229+
result2 = pd.DateOffset(years=1) + s
230+
exp = klass(date_range('2001-01-01', '2001-01-31'), name='a')
231+
assert_func(result, exp)
232+
assert_func(result2, exp)
233+
234+
result = s - pd.DateOffset(years=1)
235+
exp = klass(date_range('1999-01-01', '1999-01-31'), name='a')
236+
assert_func(result, exp)
237+
238+
s = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
239+
pd.Timestamp('2000-02-15', tz='US/Central')], name='a')
240+
result = s + pd.offsets.Day()
241+
result2 = pd.offsets.Day() + s
242+
exp = klass([Timestamp('2000-01-16 00:15:00', tz='US/Central'),
243+
Timestamp('2000-02-16', tz='US/Central')], name='a')
244+
assert_func(result, exp)
245+
assert_func(result2, exp)
246+
247+
s = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
248+
pd.Timestamp('2000-02-15', tz='US/Central')], name='a')
249+
result = s + pd.offsets.MonthEnd()
250+
result2 = pd.offsets.MonthEnd() + s
251+
exp = klass([Timestamp('2000-01-31 00:15:00', tz='US/Central'),
252+
Timestamp('2000-02-29', tz='US/Central')], name='a')
253+
assert_func(result, exp)
254+
assert_func(result2, exp)
255+
256+
# array of offsets - valid for Series only
257+
if klass is Series:
258+
with tm.assert_produces_warning(PerformanceWarning):
259+
s = klass([Timestamp('2000-1-1'), Timestamp('2000-2-1')])
260+
result = s + Series([pd.offsets.DateOffset(years=1),
261+
pd.offsets.MonthEnd()])
262+
exp = klass([Timestamp('2001-1-1'), Timestamp('2000-2-29')
263+
])
264+
assert_func(result, exp)
265+
266+
# same offset
267+
result = s + Series([pd.offsets.DateOffset(years=1),
268+
pd.offsets.DateOffset(years=1)])
269+
exp = klass([Timestamp('2001-1-1'), Timestamp('2001-2-1')])
270+
assert_func(result, exp)
271+
272+
s = klass([Timestamp('2000-01-05 00:15:00'),
273+
Timestamp('2000-01-31 00:23:00'),
274+
Timestamp('2000-01-01'),
275+
Timestamp('2000-03-31'),
276+
Timestamp('2000-02-29'),
277+
Timestamp('2000-12-31'),
278+
Timestamp('2000-05-15'),
279+
Timestamp('2001-06-15')])
280+
281+
# DateOffset relativedelta fastpath
282+
relative_kwargs = [('years', 2), ('months', 5), ('days', 3),
283+
('hours', 5), ('minutes', 10), ('seconds', 2),
284+
('microseconds', 5)]
285+
for i, kwd in enumerate(relative_kwargs):
286+
op = pd.DateOffset(**dict([kwd]))
287+
assert_func(klass([x + op for x in s]), s + op)
288+
assert_func(klass([x - op for x in s]), s - op)
289+
op = pd.DateOffset(**dict(relative_kwargs[:i + 1]))
290+
assert_func(klass([x + op for x in s]), s + op)
291+
assert_func(klass([x - op for x in s]), s - op)
292+
293+
# assert these are equal on a piecewise basis
294+
offsets = ['YearBegin', ('YearBegin', {'month': 5}),
295+
'YearEnd', ('YearEnd', {'month': 5}),
296+
'MonthBegin', 'MonthEnd',
297+
'SemiMonthEnd', 'SemiMonthBegin',
298+
'Week', ('Week', {'weekday': 3}),
299+
'BusinessDay', 'BDay', 'QuarterEnd', 'QuarterBegin',
300+
'CustomBusinessDay', 'CDay', 'CBMonthEnd',
301+
'CBMonthBegin', 'BMonthBegin', 'BMonthEnd',
302+
'BusinessHour', 'BYearBegin', 'BYearEnd',
303+
'BQuarterBegin', ('LastWeekOfMonth', {'weekday': 2}),
304+
('FY5253Quarter', {'qtr_with_extra_week': 1,
305+
'startingMonth': 1,
306+
'weekday': 2,
307+
'variation': 'nearest'}),
308+
('FY5253', {'weekday': 0,
309+
'startingMonth': 2,
310+
'variation':
311+
'nearest'}),
312+
('WeekOfMonth', {'weekday': 2,
313+
'week': 2}),
314+
'Easter', ('DateOffset', {'day': 4}),
315+
('DateOffset', {'month': 5})]
316+
317+
with warnings.catch_warnings(record=True):
318+
for normalize in (True, False):
319+
for do in offsets:
320+
if isinstance(do, tuple):
321+
do, kwargs = do
322+
else:
323+
do = do
324+
kwargs = {}
325+
326+
for n in [0, 5]:
327+
if (do in ['WeekOfMonth', 'LastWeekOfMonth',
328+
'FY5253Quarter', 'FY5253'] and n == 0):
329+
continue
330+
op = getattr(pd.offsets, do)(n,
331+
normalize=normalize,
332+
**kwargs)
333+
assert_func(klass([x + op for x in s]), s + op)
334+
assert_func(klass([x - op for x in s]), s - op)
335+
assert_func(klass([op + x for x in s]), op + s)

0 commit comments

Comments
 (0)