diff --git a/pandas/tseries/base.py b/pandas/tseries/base.py index 71ff0f6c9c56c..15f69b38febce 100644 --- a/pandas/tseries/base.py +++ b/pandas/tseries/base.py @@ -318,8 +318,7 @@ def resolution(self): """ Returns day, hour, minute, second, millisecond or microsecond """ - from pandas.tseries.frequencies import get_reso_string - return get_reso_string(self._resolution) + return Resolution.get_str(self._resolution) def _convert_scalar_indexer(self, key, kind=None): """ diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 4af8c68110978..9fb06d2854b11 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -32,6 +32,8 @@ class FreqGroup(object): class Resolution(object): + # defined in period.pyx + # note that these are different from freq codes RESO_US = period.US_RESO RESO_MS = period.MS_RESO RESO_SEC = period.S_RESO @@ -65,30 +67,104 @@ class Resolution(object): @classmethod def get_str(cls, reso): + """ + Return resolution str against resolution code. + + Example + ------- + >>> Resolution.get_str(Resolution.RESO_SEC) + 'second' + """ return cls._reso_str_map.get(reso, 'day') @classmethod def get_reso(cls, resostr): + """ + Return resolution str against resolution code. + + Example + ------- + >>> Resolution.get_reso('second') + 2 + + >>> Resolution.get_reso('second') == Resolution.RESO_SEC + True + """ return cls._str_reso_map.get(resostr, cls.RESO_DAY) + @classmethod + def get_freq_group(cls, resostr): + """ + Return frequency str against resolution str. + + Example + ------- + >>> f.Resolution.get_freq_group('day') + 4000 + """ + return get_freq_group(cls.get_freq(resostr)) + @classmethod def get_freq(cls, resostr): + """ + Return frequency str against resolution str. + + Example + ------- + >>> f.Resolution.get_freq('day') + 'D' + """ return cls._reso_freq_map[resostr] @classmethod def get_str_from_freq(cls, freq): + """ + Return resolution str against frequency str. + + Example + ------- + >>> Resolution.get_str_from_freq('H') + 'hour' + """ return cls._freq_reso_map.get(freq, 'day') @classmethod def get_reso_from_freq(cls, freq): - return cls.get_reso(cls.get_str_from_freq(freq)) + """ + Return resolution code against frequency str. + Example + ------- + >>> Resolution.get_reso_from_freq('H') + 4 -def get_reso_string(reso): - return Resolution.get_str(reso) + >>> Resolution.get_reso_from_freq('H') == Resolution.RESO_HR + True + """ + return cls.get_reso(cls.get_str_from_freq(freq)) def get_to_timestamp_base(base): + """ + Return frequency code group used for base of to_timestamp against + frequency code. + + Example + ------- + # Return day freq code against longer freq than day + >>> get_to_timestamp_base(get_freq_code('D')[0]) + 6000 + >>> get_to_timestamp_base(get_freq_code('W')[0]) + 6000 + >>> get_to_timestamp_base(get_freq_code('M')[0]) + 6000 + + # Return second freq code against hour between second + >>> get_to_timestamp_base(get_freq_code('H')[0]) + 9000 + >>> get_to_timestamp_base(get_freq_code('S')[0]) + 9000 + """ if base < FreqGroup.FR_BUS: return FreqGroup.FR_DAY if FreqGroup.FR_HR <= base <= FreqGroup.FR_SEC: @@ -97,6 +173,17 @@ def get_to_timestamp_base(base): def get_freq_group(freq): + """ + Return frequency code group of given frequency str. + + Example + ------- + >>> get_freq_group('W-MON') + 4000 + + >>> get_freq_group('W-FRI') + 4000 + """ if isinstance(freq, compat.string_types): base, mult = get_freq_code(freq) freq = base @@ -104,6 +191,18 @@ def get_freq_group(freq): def get_freq(freq): + """ + Return frequency code of given frequency str. + If input is not string, return input as it is. + + Example + ------- + >>> get_freq('A') + 1000 + + >>> get_freq('3A') + 1000 + """ if isinstance(freq, compat.string_types): base, mult = get_freq_code(freq) freq = base @@ -112,15 +211,29 @@ def get_freq(freq): def get_freq_code(freqstr): """ + Return freq str or tuple to freq code and stride (mult) Parameters ---------- + freqstr : str or tuple Returns ------- + return : tuple of base frequency code and stride (mult) + + Example + ------- + >>> get_freq_code('3D') + (6000, 3) + + >>> get_freq_code('D') + (6000, 1) + + >>> get_freq_code(('D', 3)) + (6000, 3) """ if isinstance(freqstr, DateOffset): - freqstr = (get_offset_name(freqstr), freqstr.n) + freqstr = (freqstr.rule_code, freqstr.n) if isinstance(freqstr, tuple): if (com.is_integer(freqstr[0]) and @@ -386,6 +499,7 @@ def get_base_alias(freqstr): """ return _base_and_stride(freqstr)[0] + _dont_uppercase = set(('MS', 'ms')) @@ -637,14 +751,6 @@ def _period_alias_dictionary(): return alias_dict -def _infer_period_group(freqstr): - return _period_group(Resolution._reso_freq_map[freqstr]) - - -def _period_group(freqstr): - base, mult = get_freq_code(freqstr) - return base // 1000 * 1000 - _period_alias_dict = _period_alias_dictionary() @@ -671,7 +777,7 @@ def _period_str_to_code(freqstr): def infer_freq(index, warn=True): """ Infer the most likely frequency given the input index. If the frequency is - uncertain, a warning will be printed. + uncertain, a warning will be printed. Parameters ---------- diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py index 95bbc5016237c..7606bd0bd86b8 100644 --- a/pandas/tseries/period.py +++ b/pandas/tseries/period.py @@ -528,8 +528,8 @@ def get_value(self, series, key): except (KeyError, IndexError): try: asdt, parsed, reso = parse_time_string(key, self.freq) - grp = frequencies._infer_period_group(reso) - freqn = frequencies._period_group(self.freq) + grp = frequencies.Resolution.get_freq_group(reso) + freqn = frequencies.get_freq_group(self.freq) vals = self.values @@ -655,8 +655,8 @@ def _get_string_slice(self, key): key, parsed, reso = parse_time_string(key, self.freq) - grp = frequencies._infer_period_group(reso) - freqn = frequencies._period_group(self.freq) + grp = frequencies.Resolution.get_freq_group(reso) + freqn = frequencies.get_freq_group(self.freq) if reso in ['day', 'hour', 'minute', 'second'] and not grp < freqn: raise KeyError(key) diff --git a/pandas/tseries/tests/test_frequencies.py b/pandas/tseries/tests/test_frequencies.py index 823c762c692e5..29152551f5ddf 100644 --- a/pandas/tseries/tests/test_frequencies.py +++ b/pandas/tseries/tests/test_frequencies.py @@ -132,6 +132,117 @@ def test_anchored_shortcuts(): expected = frequencies.to_offset('Q-DEC') assert(result == expected) +class TestFrequencyCode(tm.TestCase): + + def test_freq_code(self): + self.assertEqual(frequencies.get_freq('A'), 1000) + self.assertEqual(frequencies.get_freq('3A'), 1000) + self.assertEqual(frequencies.get_freq('-1A'), 1000) + + self.assertEqual(frequencies.get_freq('W'), 4000) + self.assertEqual(frequencies.get_freq('W-MON'), 4001) + self.assertEqual(frequencies.get_freq('W-FRI'), 4005) + + for freqstr, code in compat.iteritems(frequencies._period_code_map): + result = frequencies.get_freq(freqstr) + self.assertEqual(result, code) + + result = frequencies.get_freq_group(freqstr) + self.assertEqual(result, code // 1000 * 1000) + + result = frequencies.get_freq_group(code) + self.assertEqual(result, code // 1000 * 1000) + + def test_get_to_timestamp_base(self): + tsb = frequencies.get_to_timestamp_base + + self.assertEqual(tsb(frequencies.get_freq_code('D')[0]), + frequencies.get_freq_code('D')[0]) + self.assertEqual(tsb(frequencies.get_freq_code('W')[0]), + frequencies.get_freq_code('D')[0]) + self.assertEqual(tsb(frequencies.get_freq_code('M')[0]), + frequencies.get_freq_code('D')[0]) + + self.assertEqual(tsb(frequencies.get_freq_code('S')[0]), + frequencies.get_freq_code('S')[0]) + self.assertEqual(tsb(frequencies.get_freq_code('T')[0]), + frequencies.get_freq_code('S')[0]) + self.assertEqual(tsb(frequencies.get_freq_code('H')[0]), + frequencies.get_freq_code('S')[0]) + + + def test_freq_to_reso(self): + Reso = frequencies.Resolution + + self.assertEqual(Reso.get_str_from_freq('A'), 'year') + self.assertEqual(Reso.get_str_from_freq('Q'), 'quarter') + self.assertEqual(Reso.get_str_from_freq('M'), 'month') + self.assertEqual(Reso.get_str_from_freq('D'), 'day') + self.assertEqual(Reso.get_str_from_freq('H'), 'hour') + self.assertEqual(Reso.get_str_from_freq('T'), 'minute') + self.assertEqual(Reso.get_str_from_freq('S'), 'second') + self.assertEqual(Reso.get_str_from_freq('L'), 'millisecond') + self.assertEqual(Reso.get_str_from_freq('U'), 'microsecond') + self.assertEqual(Reso.get_str_from_freq('N'), 'nanosecond') + + for freq in ['A', 'Q', 'M', 'D', 'H', 'T', 'S', 'L', 'U', 'N']: + # check roundtrip + result = Reso.get_freq(Reso.get_str_from_freq(freq)) + self.assertEqual(freq, result) + + for freq in ['D', 'H', 'T', 'S', 'L', 'U']: + result = Reso.get_freq(Reso.get_str(Reso.get_reso_from_freq(freq))) + self.assertEqual(freq, result) + + def test_get_freq_code(self): + # freqstr + self.assertEqual(frequencies.get_freq_code('A'), + (frequencies.get_freq('A'), 1)) + self.assertEqual(frequencies.get_freq_code('3D'), + (frequencies.get_freq('D'), 3)) + self.assertEqual(frequencies.get_freq_code('-2M'), + (frequencies.get_freq('M'), -2)) + + # tuple + self.assertEqual(frequencies.get_freq_code(('D', 1)), + (frequencies.get_freq('D'), 1)) + self.assertEqual(frequencies.get_freq_code(('A', 3)), + (frequencies.get_freq('A'), 3)) + self.assertEqual(frequencies.get_freq_code(('M', -2)), + (frequencies.get_freq('M'), -2)) + # numeric tuple + self.assertEqual(frequencies.get_freq_code((1000, 1)), (1000, 1)) + + # offsets + self.assertEqual(frequencies.get_freq_code(offsets.Day()), + (frequencies.get_freq('D'), 1)) + self.assertEqual(frequencies.get_freq_code(offsets.Day(3)), + (frequencies.get_freq('D'), 3)) + self.assertEqual(frequencies.get_freq_code(offsets.Day(-2)), + (frequencies.get_freq('D'), -2)) + + self.assertEqual(frequencies.get_freq_code(offsets.MonthEnd()), + (frequencies.get_freq('M'), 1)) + self.assertEqual(frequencies.get_freq_code(offsets.MonthEnd(3)), + (frequencies.get_freq('M'), 3)) + self.assertEqual(frequencies.get_freq_code(offsets.MonthEnd(-2)), + (frequencies.get_freq('M'), -2)) + + self.assertEqual(frequencies.get_freq_code(offsets.Week()), + (frequencies.get_freq('W'), 1)) + self.assertEqual(frequencies.get_freq_code(offsets.Week(3)), + (frequencies.get_freq('W'), 3)) + self.assertEqual(frequencies.get_freq_code(offsets.Week(-2)), + (frequencies.get_freq('W'), -2)) + + # monday is weekday=0 + self.assertEqual(frequencies.get_freq_code(offsets.Week(weekday=1)), + (frequencies.get_freq('W-TUE'), 1)) + self.assertEqual(frequencies.get_freq_code(offsets.Week(3, weekday=0)), + (frequencies.get_freq('W-MON'), 3)) + self.assertEqual(frequencies.get_freq_code(offsets.Week(-2, weekday=4)), + (frequencies.get_freq('W-FRI'), -2)) + _dti = DatetimeIndex @@ -333,7 +444,6 @@ def test_infer_freq_tz_transition(self): for date_pair in date_pairs: for freq in freqs: idx = date_range(date_pair[0], date_pair[1], freq=freq, tz=tz) - print(idx) self.assertEqual(idx.inferred_freq, freq) index = date_range("2013-11-03", periods=5, freq="3H").tz_localize("America/Chicago") diff --git a/pandas/tseries/tests/test_offsets.py b/pandas/tseries/tests/test_offsets.py index a051560617604..275fcd4d987ed 100644 --- a/pandas/tseries/tests/test_offsets.py +++ b/pandas/tseries/tests/test_offsets.py @@ -19,7 +19,7 @@ get_offset, get_offset_name, get_standard_freq) from pandas import Series -from pandas.tseries.frequencies import _offset_map +from pandas.tseries.frequencies import _offset_map, get_freq_code, _get_freq_str from pandas.tseries.index import _to_m8, DatetimeIndex, _daterange_cache, date_range from pandas.tseries.tools import parse_time_string, DateParseError import pandas.tseries.offsets as offsets @@ -211,6 +211,27 @@ def test_return_type(self): self.assertTrue(NaT - offset is NaT) self.assertTrue((-offset).apply(NaT) is NaT) + def test_offset_n(self): + for offset_klass in self.offset_types: + offset = self._get_offset(offset_klass) + self.assertEqual(offset.n, 1) + + neg_offset = offset * -1 + self.assertEqual(neg_offset.n, -1) + + mul_offset = offset * 3 + self.assertEqual(mul_offset.n, 3) + + def test_offset_freqstr(self): + for offset_klass in self.offset_types: + offset = self._get_offset(offset_klass) + + freqstr = offset.freqstr + if freqstr not in ('', "", + 'LWOM-SAT', ): + code = get_offset(freqstr) + self.assertEqual(offset.rule_code, code) + def _check_offsetfunc_works(self, offset, funcname, dt, expected, normalize=False): offset_s = self._get_offset(offset, normalize=normalize) @@ -3695,6 +3716,12 @@ def test_rule_code(self): self.assertEqual(alias, get_offset(alias).rule_code) self.assertEqual(alias, (get_offset(alias) * 5).rule_code) + lst = ['M', 'D', 'B', 'H', 'T', 'S', 'L', 'U'] + for k in lst: + code, stride = get_freq_code('3' + k) + self.assertTrue(isinstance(code, int)) + self.assertEqual(stride, 3) + self.assertEqual(k, _get_freq_str(code)) def test_apply_ticks(): result = offsets.Hour(3).apply(offsets.Hour(4))