Skip to content

Commit 022e630

Browse files
committed
ENH: nanosecond tweak per #2170. period spanning in upsampling
1 parent 74fdbaa commit 022e630

File tree

6 files changed

+61
-25
lines changed

6 files changed

+61
-25
lines changed

RELEASE.rst

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ pandas 0.9.1
3434
- Add `where` and `mask` functions to DataFrame (#2109, #2151)
3535
- Add `at_time` and `between_time` functions to DataFrame (#2149)
3636

37+
**API Changes**
38+
39+
- Upsampling period index "spans" intervals. Example: annual periods
40+
upsampled to monthly will span all months in each year
41+
- Period.end_time will yield timestamp at last nanosecond in the interval
42+
(#2124, #2125, #1764)
43+
3744
**Improvements to existing features**
3845

3946
- Time rule inference for week-of-month (e.g. WOM-2FRI) rules (#2140)

pandas/core/generic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def between_time(self, start_time, end_time, include_start=True,
200200
raise TypeError('Index must be DatetimeIndex')
201201

202202
def resample(self, rule, how=None, axis=0, fill_method=None,
203-
closed='right', label='right', convention=None,
203+
closed='right', label='right', convention='start',
204204
kind=None, loffset=None, limit=None, base=0):
205205
"""
206206
Convenience method for frequency conversion and resampling of regular

pandas/tseries/period.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,12 @@ def asfreq(self, freq, how='E'):
181181

182182
@property
183183
def start_time(self):
184-
return self.to_timestamp('s', how='S')
184+
return self.to_timestamp(how='S')
185185

186186
@property
187187
def end_time(self):
188-
return self.to_timestamp('s', how='E')
188+
ordinal = (self + 1).start_time.value - 1
189+
return Timestamp(ordinal)
189190

190191
def to_timestamp(self, freq=None, how='start'):
191192
"""

pandas/tseries/resample.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,8 @@ def _resample_periods(self, obj):
219219
return obj.reindex(new_index)
220220
else:
221221
start = axlabels[0].asfreq(self.freq, how=self.convention)
222-
end = axlabels[-1].asfreq(self.freq, how=self.convention)
222+
end = axlabels[-1].asfreq(self.freq, how='end')
223+
223224
new_index = period_range(start, end, freq=self.freq)
224225

225226
# Start vs. end of period

pandas/tseries/tests/test_period.py

+18-10
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,18 @@ def test_to_timestamp(self):
224224

225225
from_lst = ['A', 'Q', 'M', 'W', 'B',
226226
'D', 'H', 'Min', 'S']
227+
228+
def _ex(p):
229+
return Timestamp((p + 1).start_time.value - 1)
230+
227231
for i, fcode in enumerate(from_lst):
228232
p = Period('1982', freq=fcode)
229233
result = p.to_timestamp().to_period(fcode)
230234
self.assertEquals(result, p)
231235

232236
self.assertEquals(p.start_time, p.to_timestamp(how='S'))
233237

234-
self.assertEquals(p.end_time, p.to_timestamp('s', how='E'))
238+
self.assertEquals(p.end_time, _ex(p))
235239

236240
# Frequency other than daily
237241

@@ -272,30 +276,34 @@ def test_start_time(self):
272276

273277
def test_end_time(self):
274278
p = Period('2012', freq='A')
275-
xp = datetime(2012, 12, 31, 23, 59, 59)
279+
280+
def _ex(*args):
281+
return Timestamp(Timestamp(datetime(*args)).value - 1)
282+
283+
xp = _ex(2013, 1, 1)
276284
self.assertEquals(xp, p.end_time)
277285

278286
p = Period('2012', freq='Q')
279-
xp = datetime(2012, 3, 31, 23, 59, 59)
287+
xp = _ex(2012, 4, 1)
280288
self.assertEquals(xp, p.end_time)
281289

282290
p = Period('2012', freq='M')
283-
xp = datetime(2012, 1, 31, 23, 59, 59)
291+
xp = _ex(2012, 2, 1)
284292
self.assertEquals(xp, p.end_time)
285293

286-
xp = datetime(2012, 1, 1, 23, 59, 59)
294+
xp = _ex(2012, 1, 2)
287295
p = Period('2012', freq='D')
288296
self.assertEquals(p.end_time, xp)
289297

290-
xp = datetime(2012, 1, 1, 0, 59, 59)
298+
xp = _ex(2012, 1, 1, 1)
291299
p = Period('2012', freq='H')
292300
self.assertEquals(p.end_time, xp)
293301

294-
self.assertEquals(Period('2012', freq='B').end_time,
295-
datetime(2011, 12, 30, 23, 59, 59))
302+
xp = _ex(2012, 1, 2)
303+
self.assertEquals(Period('2012', freq='B').end_time, xp)
296304

297-
self.assertEquals(Period('2012', freq='W').end_time,
298-
datetime(2012, 1, 1, 23, 59, 59))
305+
xp = _ex(2012, 1, 2)
306+
self.assertEquals(Period('2012', freq='W').end_time, xp)
299307

300308

301309
def test_properties_annually(self):

pandas/tseries/tests/test_resample.py

+30-11
Original file line numberDiff line numberDiff line change
@@ -451,24 +451,26 @@ def test_resample_anchored_intraday(self):
451451
df = DataFrame(rng.month, index=rng)
452452

453453
result = df.resample('M')
454-
expected = df.resample('M', kind='period').to_timestamp()
454+
expected = df.resample('M', kind='period').to_timestamp(how='end')
455455
tm.assert_frame_equal(result, expected)
456456

457457
result = df.resample('M', closed='left')
458-
exp = df.tshift(1, freq='D').resample('M', kind='period').to_timestamp()
458+
exp = df.tshift(1, freq='D').resample('M', kind='period')
459+
exp = exp.to_timestamp(how='end')
460+
459461
tm.assert_frame_equal(result, exp)
460462

461463
rng = date_range('1/1/2012', '4/1/2013', freq='10min')
462464
df = DataFrame(rng.month, index=rng)
463465

464466
result = df.resample('Q')
465-
expected = df.resample('Q', kind='period').to_timestamp()
467+
expected = df.resample('Q', kind='period').to_timestamp(how='end')
466468
tm.assert_frame_equal(result, expected)
467469

468470
result = df.resample('Q', closed='left')
469471
expected = df.tshift(1, freq='D').resample('Q', kind='period',
470472
closed='left')
471-
expected = expected.to_timestamp()
473+
expected = expected.to_timestamp(how='end')
472474
tm.assert_frame_equal(result, expected)
473475

474476
ts = _simple_ts('2012-04-29 23:00', '2012-04-30 5:00', freq='h')
@@ -622,7 +624,8 @@ def test_upsample_with_limit(self):
622624
rng = period_range('1/1/2000', periods=5, freq='A')
623625
ts = Series(np.random.randn(len(rng)), rng)
624626

625-
result = ts.resample('M', fill_method='ffill', limit=2)
627+
result = ts.resample('M', fill_method='ffill', limit=2,
628+
convention='end')
626629
expected = ts.asfreq('M').reindex(result.index, method='ffill',
627630
limit=2)
628631
assert_series_equal(result, expected)
@@ -646,6 +649,17 @@ def test_annual_upsample(self):
646649
exp = df['a'].resample('D', fill_method='ffill')
647650
assert_series_equal(rdf['a'], exp)
648651

652+
653+
rng = period_range('2000', '2003', freq='A-DEC')
654+
ts = Series([1, 2, 3, 4], index=rng)
655+
656+
result = ts.resample('M', fill_method='ffill')
657+
ex_index = period_range('2000-01', '2003-12', freq='M')
658+
659+
expected = ts.asfreq('M', how='start').reindex(ex_index,
660+
method='ffill')
661+
assert_series_equal(result, expected)
662+
649663
def test_quarterly_upsample(self):
650664
targets = ['D', 'B', 'M']
651665

@@ -696,8 +710,9 @@ def test_resample_to_quarterly(self):
696710
ts = _simple_pts('1990', '1992', freq='A-%s' % month)
697711
quar_ts = ts.resample('Q-%s' % month, fill_method='ffill')
698712

699-
stamps = ts.to_timestamp('D', how='end')
700-
qdates = period_range(stamps.index[0], stamps.index[-1],
713+
stamps = ts.to_timestamp('D', how='start')
714+
qdates = period_range(ts.index[0].asfreq('D', 'start'),
715+
ts.index[-1].asfreq('D', 'end'),
701716
freq='Q-%s' % month)
702717

703718
expected = stamps.reindex(qdates.to_timestamp('D', 'e'),
@@ -711,9 +726,13 @@ def test_resample_to_quarterly(self):
711726

712727
for how in ['start', 'end']:
713728
result = ts.resample('Q-MAR', convention=how, fill_method='ffill')
714-
expected = ts.asfreq('Q-MAR', how=how).to_timestamp('D')
715-
expected = expected.resample('Q-MAR', fill_method='ffill')
716-
assert_series_equal(result, expected.to_period('Q-MAR'))
729+
expected = ts.asfreq('Q-MAR', how=how)
730+
expected = expected.reindex(result.index, method='ffill')
731+
732+
# .to_timestamp('D')
733+
# expected = expected.resample('Q-MAR', fill_method='ffill')
734+
735+
assert_series_equal(result, expected)
717736

718737
def test_resample_fill_missing(self):
719738
rng = PeriodIndex([2000, 2005, 2007, 2009], freq='A')
@@ -752,7 +771,7 @@ def test_upsample_daily_business_daily(self):
752771

753772
ts = _simple_pts('1/1/2000', '2/1/2000')
754773
result = ts.resample('H', convention='s')
755-
exp_rng = period_range('1/1/2000', '2/1/2000', freq='H')
774+
exp_rng = period_range('1/1/2000', '2/1/2000 23:00', freq='H')
756775
expected = ts.asfreq('H', how='s').reindex(exp_rng)
757776
assert_series_equal(result, expected)
758777

0 commit comments

Comments
 (0)