Skip to content

Commit 790d646

Browse files
committed
Merge pull request #7607 from sinhrks/millireso
CLN: Simplify Period Construction / Resolution
2 parents cba5afd + 071c37e commit 790d646

File tree

9 files changed

+73
-63
lines changed

9 files changed

+73
-63
lines changed
Binary file not shown.
Binary file not shown.

pandas/io/tests/generate_legacy_pickles.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def create_data():
5858
from pandas import (Series,TimeSeries,DataFrame,Panel,
5959
SparseSeries,SparseTimeSeries,SparseDataFrame,SparsePanel,
6060
Index,MultiIndex,PeriodIndex,
61-
date_range,bdate_range,Timestamp)
61+
date_range,period_range,bdate_range,Timestamp)
6262
nan = np.nan
6363

6464
data = {
@@ -70,7 +70,9 @@ def create_data():
7070
}
7171

7272
index = dict(int = Index(np.arange(10)),
73-
date = date_range('20130101',periods=10))
73+
date = date_range('20130101',periods=10),
74+
period = period_range('2013-01-01', freq='M', periods=10))
75+
7476
mi = dict(reg2 = MultiIndex.from_tuples(tuple(zip(*[['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
7577
['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']])),
7678
names=['first', 'second']))

pandas/tseries/frequencies.py

+31-24
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,40 @@ class FreqGroup(object):
3030

3131
class Resolution(object):
3232

33-
RESO_US = 0
34-
RESO_SEC = 1
35-
RESO_MIN = 2
36-
RESO_HR = 3
37-
RESO_DAY = 4
33+
RESO_US = tslib.US_RESO
34+
RESO_MS = tslib.MS_RESO
35+
RESO_SEC = tslib.S_RESO
36+
RESO_MIN = tslib.T_RESO
37+
RESO_HR = tslib.H_RESO
38+
RESO_DAY = tslib.D_RESO
39+
40+
_reso_str_map = {
41+
RESO_US: 'microsecond',
42+
RESO_MS: 'millisecond',
43+
RESO_SEC: 'second',
44+
RESO_MIN: 'minute',
45+
RESO_HR: 'hour',
46+
RESO_DAY: 'day'}
47+
48+
_reso_period_map = {
49+
'year': 'A',
50+
'quarter': 'Q',
51+
'month': 'M',
52+
'day': 'D',
53+
'hour': 'H',
54+
'minute': 'T',
55+
'second': 'S',
56+
'millisecond': 'L',
57+
'microsecond': 'U',
58+
'nanosecond': 'N'}
3859

3960
@classmethod
4061
def get_str(cls, reso):
41-
return {cls.RESO_US: 'microsecond',
42-
cls.RESO_SEC: 'second',
43-
cls.RESO_MIN: 'minute',
44-
cls.RESO_HR: 'hour',
45-
cls.RESO_DAY: 'day'}.get(reso, 'day')
62+
return cls._reso_str_map.get(reso, 'day')
4663

64+
@classmethod
65+
def get_freq(cls, resostr):
66+
return cls._reso_period_map[resostr]
4767

4868
def get_reso_string(reso):
4969
return Resolution.get_str(reso)
@@ -571,22 +591,9 @@ def _period_alias_dictionary():
571591

572592
return alias_dict
573593

574-
_reso_period_map = {
575-
"year": "A",
576-
"quarter": "Q",
577-
"month": "M",
578-
"day": "D",
579-
"hour": "H",
580-
"minute": "T",
581-
"second": "S",
582-
"millisecond": "L",
583-
"microsecond": "U",
584-
"nanosecond": "N",
585-
}
586-
587594

588595
def _infer_period_group(freqstr):
589-
return _period_group(_reso_period_map[freqstr])
596+
return _period_group(Resolution._reso_period_map[freqstr])
590597

591598

592599
def _period_group(freqstr):

pandas/tseries/period.py

+7-31
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,14 @@ def __init__(self, value=None, freq=None, ordinal=None,
111111
elif isinstance(value, compat.string_types) or com.is_integer(value):
112112
if com.is_integer(value):
113113
value = str(value)
114+
value = value.upper()
114115

115-
dt, freq = _get_date_and_freq(value, freq)
116+
dt, _, reso = parse_time_string(value, freq)
117+
if freq is None:
118+
try:
119+
freq = _freq_mod.Resolution.get_freq(reso)
120+
except KeyError:
121+
raise ValueError("Invalid frequency or could not infer: %s" % reso)
116122

117123
elif isinstance(value, datetime):
118124
dt = value
@@ -451,36 +457,6 @@ def strftime(self, fmt):
451457
return tslib.period_format(self.ordinal, base, fmt)
452458

453459

454-
def _get_date_and_freq(value, freq):
455-
value = value.upper()
456-
dt, _, reso = parse_time_string(value, freq)
457-
458-
if freq is None:
459-
if reso == 'year':
460-
freq = 'A'
461-
elif reso == 'quarter':
462-
freq = 'Q'
463-
elif reso == 'month':
464-
freq = 'M'
465-
elif reso == 'day':
466-
freq = 'D'
467-
elif reso == 'hour':
468-
freq = 'H'
469-
elif reso == 'minute':
470-
freq = 'T'
471-
elif reso == 'second':
472-
freq = 'S'
473-
elif reso == 'microsecond':
474-
if dt.microsecond % 1000 == 0:
475-
freq = 'L'
476-
else:
477-
freq = 'U'
478-
else:
479-
raise ValueError("Invalid frequency or could not infer: %s" % reso)
480-
481-
return dt, freq
482-
483-
484460
def _get_ordinals(data, freq):
485461
f = lambda x: Period(x, freq=freq).ordinal
486462
if isinstance(data[0], Period):

pandas/tseries/tests/test_period.py

+8
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ def test_period_constructor(self):
227227
i2 = Period(datetime(2007, 1, 1), freq='M')
228228
self.assertEqual(i1, i2)
229229

230+
i1 = Period('2007-01-01 09:00:00.001')
231+
expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1000), freq='L')
232+
self.assertEqual(i1, expected)
233+
234+
i1 = Period('2007-01-01 09:00:00.00101')
235+
expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1010), freq='U')
236+
self.assertEqual(i1, expected)
237+
230238
self.assertRaises(ValueError, Period, ordinal=200701)
231239

232240
self.assertRaises(ValueError, Period, '2007-1-1', freq='X')

pandas/tseries/tests/test_tslib.py

+11
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,17 @@ def test_addition_subtraction_preserve_frequency(self):
476476
self.assertEqual((timestamp_instance + timedelta64_instance).freq, original_freq)
477477
self.assertEqual((timestamp_instance - timedelta64_instance).freq, original_freq)
478478

479+
def test_resolution(self):
480+
481+
for freq, expected in zip(['A', 'Q', 'M', 'D', 'H', 'T', 'S', 'L', 'U'],
482+
[tslib.D_RESO, tslib.D_RESO, tslib.D_RESO, tslib.D_RESO,
483+
tslib.H_RESO, tslib.T_RESO,tslib.S_RESO, tslib.MS_RESO, tslib.US_RESO]):
484+
for tz in [None, 'Asia/Tokyo', 'US/Eastern']:
485+
idx = date_range(start='2013-04-01', periods=30, freq=freq, tz=tz)
486+
result = tslib.resolution(idx.asi8, idx.tz)
487+
self.assertEqual(result, expected)
488+
489+
479490
if __name__ == '__main__':
480491
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
481492
exit=False)

pandas/tseries/tools.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -508,8 +508,11 @@ def dateutil_parse(timestr, default,
508508
if reso is None:
509509
raise ValueError("Cannot parse date.")
510510

511-
if reso == 'microsecond' and repl['microsecond'] == 0:
512-
reso = 'second'
511+
if reso == 'microsecond':
512+
if repl['microsecond'] == 0:
513+
reso = 'second'
514+
elif repl['microsecond'] % 1000 == 0:
515+
reso = 'millisecond'
513516

514517
ret = default.replace(**repl)
515518
if res.weekday is not None and not res.day:

pandas/tslib.pyx

+7-4
Original file line numberDiff line numberDiff line change
@@ -3372,13 +3372,16 @@ cpdef resolution(ndarray[int64_t] stamps, tz=None):
33723372
return reso
33733373

33743374
US_RESO = 0
3375-
S_RESO = 1
3376-
T_RESO = 2
3377-
H_RESO = 3
3378-
D_RESO = 4
3375+
MS_RESO = 1
3376+
S_RESO = 2
3377+
T_RESO = 3
3378+
H_RESO = 4
3379+
D_RESO = 5
33793380

33803381
cdef inline int _reso_stamp(pandas_datetimestruct *dts):
33813382
if dts.us != 0:
3383+
if dts.us % 1000 == 0:
3384+
return MS_RESO
33823385
return US_RESO
33833386
elif dts.sec != 0:
33843387
return S_RESO

0 commit comments

Comments
 (0)