Skip to content

Commit 76b06f0

Browse files
committed
BUG: frequencies.get_freq_code raises an error against offset with n != 1
1 parent 37fa925 commit 76b06f0

File tree

5 files changed

+263
-21
lines changed

5 files changed

+263
-21
lines changed

pandas/tseries/base.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,7 @@ def resolution(self):
318318
"""
319319
Returns day, hour, minute, second, millisecond or microsecond
320320
"""
321-
from pandas.tseries.frequencies import get_reso_string
322-
return get_reso_string(self._resolution)
321+
return Resolution.get_str(self._resolution)
323322

324323
def _convert_scalar_indexer(self, key, kind=None):
325324
"""

pandas/tseries/frequencies.py

+119-13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class FreqGroup(object):
3232

3333
class Resolution(object):
3434

35+
# defined in period.pyx
36+
# note that these are different from freq codes
3537
RESO_US = period.US_RESO
3638
RESO_MS = period.MS_RESO
3739
RESO_SEC = period.S_RESO
@@ -65,30 +67,104 @@ class Resolution(object):
6567

6668
@classmethod
6769
def get_str(cls, reso):
70+
"""
71+
Return resolution str against resolution code.
72+
73+
Example
74+
-------
75+
>>> Resolution.get_str(Resolution.RESO_SEC)
76+
'second'
77+
"""
6878
return cls._reso_str_map.get(reso, 'day')
6979

7080
@classmethod
7181
def get_reso(cls, resostr):
82+
"""
83+
Return resolution str against resolution code.
84+
85+
Example
86+
-------
87+
>>> Resolution.get_reso('second')
88+
2
89+
90+
>>> Resolution.get_reso('second') == Resolution.RESO_SEC
91+
True
92+
"""
7293
return cls._str_reso_map.get(resostr, cls.RESO_DAY)
7394

95+
@classmethod
96+
def get_freq_group(cls, resostr):
97+
"""
98+
Return frequency str against resolution str.
99+
100+
Example
101+
-------
102+
>>> f.Resolution.get_freq_group('day')
103+
4000
104+
"""
105+
return get_freq_group(cls.get_freq(resostr))
106+
74107
@classmethod
75108
def get_freq(cls, resostr):
109+
"""
110+
Return frequency str against resolution str.
111+
112+
Example
113+
-------
114+
>>> f.Resolution.get_freq('day')
115+
'D'
116+
"""
76117
return cls._reso_freq_map[resostr]
77118

78119
@classmethod
79120
def get_str_from_freq(cls, freq):
121+
"""
122+
Return resolution str against frequency str.
123+
124+
Example
125+
-------
126+
>>> Resolution.get_str_from_freq('H')
127+
'hour'
128+
"""
80129
return cls._freq_reso_map.get(freq, 'day')
81130

82131
@classmethod
83132
def get_reso_from_freq(cls, freq):
84-
return cls.get_reso(cls.get_str_from_freq(freq))
133+
"""
134+
Return resolution code against frequency str.
85135
136+
Example
137+
-------
138+
>>> Resolution.get_reso_from_freq('H')
139+
4
86140
87-
def get_reso_string(reso):
88-
return Resolution.get_str(reso)
141+
>>> Resolution.get_reso_from_freq('H') == Resolution.RESO_HR
142+
True
143+
"""
144+
return cls.get_reso(cls.get_str_from_freq(freq))
89145

90146

91147
def get_to_timestamp_base(base):
148+
"""
149+
Return frequency code group used for base of to_timestamp against
150+
frequency code.
151+
152+
Example
153+
-------
154+
# Return day freq code against longer freq than day
155+
>>> get_to_timestamp_base(get_freq_code('D')[0])
156+
6000
157+
>>> get_to_timestamp_base(get_freq_code('W')[0])
158+
6000
159+
>>> get_to_timestamp_base(get_freq_code('M')[0])
160+
6000
161+
162+
# Return second freq code against hour between second
163+
>>> get_to_timestamp_base(get_freq_code('H')[0])
164+
9000
165+
>>> get_to_timestamp_base(get_freq_code('S')[0])
166+
9000
167+
"""
92168
if base < FreqGroup.FR_BUS:
93169
return FreqGroup.FR_DAY
94170
if FreqGroup.FR_HR <= base <= FreqGroup.FR_SEC:
@@ -97,13 +173,36 @@ def get_to_timestamp_base(base):
97173

98174

99175
def get_freq_group(freq):
176+
"""
177+
Return frequency code group of given frequency str.
178+
179+
Example
180+
-------
181+
>>> get_freq_group('W-MON')
182+
4000
183+
184+
>>> get_freq_group('W-FRI')
185+
4000
186+
"""
100187
if isinstance(freq, compat.string_types):
101188
base, mult = get_freq_code(freq)
102189
freq = base
103190
return (freq // 1000) * 1000
104191

105192

106193
def get_freq(freq):
194+
"""
195+
Return frequency code of given frequency str.
196+
If input is not string, return input as it is.
197+
198+
Example
199+
-------
200+
>>> get_freq('A')
201+
1000
202+
203+
>>> get_freq('3A')
204+
1000
205+
"""
107206
if isinstance(freq, compat.string_types):
108207
base, mult = get_freq_code(freq)
109208
freq = base
@@ -112,15 +211,29 @@ def get_freq(freq):
112211

113212
def get_freq_code(freqstr):
114213
"""
214+
Return freq str or tuple to freq code and stride (mult)
115215
116216
Parameters
117217
----------
218+
freqstr : str or tuple
118219
119220
Returns
120221
-------
222+
return : tuple of base frequency code and stride (mult)
223+
224+
Example
225+
-------
226+
>>> get_freq_code('3D')
227+
(6000, 3)
228+
229+
>>> get_freq_code('D')
230+
(6000, 1)
231+
232+
>>> get_freq_code(('D', 3))
233+
(6000, 3)
121234
"""
122235
if isinstance(freqstr, DateOffset):
123-
freqstr = (get_offset_name(freqstr), freqstr.n)
236+
freqstr = (freqstr.rule_code, freqstr.n)
124237

125238
if isinstance(freqstr, tuple):
126239
if (com.is_integer(freqstr[0]) and
@@ -386,6 +499,7 @@ def get_base_alias(freqstr):
386499
"""
387500
return _base_and_stride(freqstr)[0]
388501

502+
389503
_dont_uppercase = set(('MS', 'ms'))
390504

391505

@@ -637,14 +751,6 @@ def _period_alias_dictionary():
637751
return alias_dict
638752

639753

640-
def _infer_period_group(freqstr):
641-
return _period_group(Resolution._reso_freq_map[freqstr])
642-
643-
644-
def _period_group(freqstr):
645-
base, mult = get_freq_code(freqstr)
646-
return base // 1000 * 1000
647-
648754
_period_alias_dict = _period_alias_dictionary()
649755

650756

@@ -671,7 +777,7 @@ def _period_str_to_code(freqstr):
671777
def infer_freq(index, warn=True):
672778
"""
673779
Infer the most likely frequency given the input index. If the frequency is
674-
uncertain, a warning will be printed.
780+
uncertain, a warning will be printed.
675781
676782
Parameters
677783
----------

pandas/tseries/period.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -528,8 +528,8 @@ def get_value(self, series, key):
528528
except (KeyError, IndexError):
529529
try:
530530
asdt, parsed, reso = parse_time_string(key, self.freq)
531-
grp = frequencies._infer_period_group(reso)
532-
freqn = frequencies._period_group(self.freq)
531+
grp = frequencies.Resolution.get_freq_group(reso)
532+
freqn = frequencies.get_freq_group(self.freq)
533533

534534
vals = self.values
535535

@@ -655,8 +655,8 @@ def _get_string_slice(self, key):
655655

656656
key, parsed, reso = parse_time_string(key, self.freq)
657657

658-
grp = frequencies._infer_period_group(reso)
659-
freqn = frequencies._period_group(self.freq)
658+
grp = frequencies.Resolution.get_freq_group(reso)
659+
freqn = frequencies.get_freq_group(self.freq)
660660
if reso in ['day', 'hour', 'minute', 'second'] and not grp < freqn:
661661
raise KeyError(key)
662662

pandas/tseries/tests/test_frequencies.py

+111-1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,117 @@ def test_anchored_shortcuts():
132132
expected = frequencies.to_offset('Q-DEC')
133133
assert(result == expected)
134134

135+
class TestFrequencyCode(tm.TestCase):
136+
137+
def test_freq_code(self):
138+
self.assertEqual(frequencies.get_freq('A'), 1000)
139+
self.assertEqual(frequencies.get_freq('3A'), 1000)
140+
self.assertEqual(frequencies.get_freq('-1A'), 1000)
141+
142+
self.assertEqual(frequencies.get_freq('W'), 4000)
143+
self.assertEqual(frequencies.get_freq('W-MON'), 4001)
144+
self.assertEqual(frequencies.get_freq('W-FRI'), 4005)
145+
146+
for freqstr, code in compat.iteritems(frequencies._period_code_map):
147+
result = frequencies.get_freq(freqstr)
148+
self.assertEqual(result, code)
149+
150+
result = frequencies.get_freq_group(freqstr)
151+
self.assertEqual(result, code // 1000 * 1000)
152+
153+
result = frequencies.get_freq_group(code)
154+
self.assertEqual(result, code // 1000 * 1000)
155+
156+
def test_get_to_timestamp_base(self):
157+
tsb = frequencies.get_to_timestamp_base
158+
159+
self.assertEqual(tsb(frequencies.get_freq_code('D')[0]),
160+
frequencies.get_freq_code('D')[0])
161+
self.assertEqual(tsb(frequencies.get_freq_code('W')[0]),
162+
frequencies.get_freq_code('D')[0])
163+
self.assertEqual(tsb(frequencies.get_freq_code('M')[0]),
164+
frequencies.get_freq_code('D')[0])
165+
166+
self.assertEqual(tsb(frequencies.get_freq_code('S')[0]),
167+
frequencies.get_freq_code('S')[0])
168+
self.assertEqual(tsb(frequencies.get_freq_code('T')[0]),
169+
frequencies.get_freq_code('S')[0])
170+
self.assertEqual(tsb(frequencies.get_freq_code('H')[0]),
171+
frequencies.get_freq_code('S')[0])
172+
173+
174+
def test_freq_to_reso(self):
175+
Reso = frequencies.Resolution
176+
177+
self.assertEqual(Reso.get_str_from_freq('A'), 'year')
178+
self.assertEqual(Reso.get_str_from_freq('Q'), 'quarter')
179+
self.assertEqual(Reso.get_str_from_freq('M'), 'month')
180+
self.assertEqual(Reso.get_str_from_freq('D'), 'day')
181+
self.assertEqual(Reso.get_str_from_freq('H'), 'hour')
182+
self.assertEqual(Reso.get_str_from_freq('T'), 'minute')
183+
self.assertEqual(Reso.get_str_from_freq('S'), 'second')
184+
self.assertEqual(Reso.get_str_from_freq('L'), 'millisecond')
185+
self.assertEqual(Reso.get_str_from_freq('U'), 'microsecond')
186+
self.assertEqual(Reso.get_str_from_freq('N'), 'nanosecond')
187+
188+
for freq in ['A', 'Q', 'M', 'D', 'H', 'T', 'S', 'L', 'U', 'N']:
189+
# check roundtrip
190+
result = Reso.get_freq(Reso.get_str_from_freq(freq))
191+
self.assertEqual(freq, result)
192+
193+
for freq in ['D', 'H', 'T', 'S', 'L', 'U']:
194+
result = Reso.get_freq(Reso.get_str(Reso.get_reso_from_freq(freq)))
195+
self.assertEqual(freq, result)
196+
197+
def test_get_freq_code(self):
198+
# freqstr
199+
self.assertEqual(frequencies.get_freq_code('A'),
200+
(frequencies.get_freq('A'), 1))
201+
self.assertEqual(frequencies.get_freq_code('3D'),
202+
(frequencies.get_freq('D'), 3))
203+
self.assertEqual(frequencies.get_freq_code('-2M'),
204+
(frequencies.get_freq('M'), -2))
205+
206+
# tuple
207+
self.assertEqual(frequencies.get_freq_code(('D', 1)),
208+
(frequencies.get_freq('D'), 1))
209+
self.assertEqual(frequencies.get_freq_code(('A', 3)),
210+
(frequencies.get_freq('A'), 3))
211+
self.assertEqual(frequencies.get_freq_code(('M', -2)),
212+
(frequencies.get_freq('M'), -2))
213+
# numeric tuple
214+
self.assertEqual(frequencies.get_freq_code((1000, 1)), (1000, 1))
215+
216+
# offsets
217+
self.assertEqual(frequencies.get_freq_code(offsets.Day()),
218+
(frequencies.get_freq('D'), 1))
219+
self.assertEqual(frequencies.get_freq_code(offsets.Day(3)),
220+
(frequencies.get_freq('D'), 3))
221+
self.assertEqual(frequencies.get_freq_code(offsets.Day(-2)),
222+
(frequencies.get_freq('D'), -2))
223+
224+
self.assertEqual(frequencies.get_freq_code(offsets.MonthEnd()),
225+
(frequencies.get_freq('M'), 1))
226+
self.assertEqual(frequencies.get_freq_code(offsets.MonthEnd(3)),
227+
(frequencies.get_freq('M'), 3))
228+
self.assertEqual(frequencies.get_freq_code(offsets.MonthEnd(-2)),
229+
(frequencies.get_freq('M'), -2))
230+
231+
self.assertEqual(frequencies.get_freq_code(offsets.Week()),
232+
(frequencies.get_freq('W'), 1))
233+
self.assertEqual(frequencies.get_freq_code(offsets.Week(3)),
234+
(frequencies.get_freq('W'), 3))
235+
self.assertEqual(frequencies.get_freq_code(offsets.Week(-2)),
236+
(frequencies.get_freq('W'), -2))
237+
238+
# monday is weekday=0
239+
self.assertEqual(frequencies.get_freq_code(offsets.Week(weekday=1)),
240+
(frequencies.get_freq('W-TUE'), 1))
241+
self.assertEqual(frequencies.get_freq_code(offsets.Week(3, weekday=0)),
242+
(frequencies.get_freq('W-MON'), 3))
243+
self.assertEqual(frequencies.get_freq_code(offsets.Week(-2, weekday=4)),
244+
(frequencies.get_freq('W-FRI'), -2))
245+
135246

136247
_dti = DatetimeIndex
137248

@@ -333,7 +444,6 @@ def test_infer_freq_tz_transition(self):
333444
for date_pair in date_pairs:
334445
for freq in freqs:
335446
idx = date_range(date_pair[0], date_pair[1], freq=freq, tz=tz)
336-
print(idx)
337447
self.assertEqual(idx.inferred_freq, freq)
338448

339449
index = date_range("2013-11-03", periods=5, freq="3H").tz_localize("America/Chicago")

0 commit comments

Comments
 (0)