From b77d09d0df8cef5d88e3da75e7c57f881872d0ce Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 2 Oct 2017 08:56:24 -0700 Subject: [PATCH 01/32] Move tseries.frequencies funcs up to tslibs.frequencies --- pandas/_libs/period.pyx | 13 +- pandas/_libs/tslib.pyx | 24 +-- pandas/_libs/tslibs/frequencies.pxd | 6 + pandas/_libs/tslibs/frequencies.pyx | 264 +++++++++++++++++++++++++++- pandas/tseries/frequencies.py | 240 +------------------------ 5 files changed, 284 insertions(+), 263 deletions(-) diff --git a/pandas/_libs/period.pyx b/pandas/_libs/period.pyx index 49353f7b0491c..ce8c7e8578d8f 100644 --- a/pandas/_libs/period.pyx +++ b/pandas/_libs/period.pyx @@ -38,7 +38,10 @@ from tslibs.timezones cimport ( is_utc, is_tzlocal, get_utcoffset, _get_dst_info, maybe_get_tz) from tslib cimport _nat_scalar_rules -from tslibs.frequencies cimport get_freq_code +from tslibs.frequencies cimport ( + get_freq_code, get_base_alias, get_to_timestamp_base, _get_freq_str, + _get_rule_month) +from tslibs.frequencies import _MONTH_NUMBERS from pandas.tseries import offsets from pandas.core.tools.datetimes import parse_time_string @@ -683,7 +686,7 @@ cdef class _Period(object): if isinstance(freq, (int, tuple)): code, stride = get_freq_code(freq) - freq = frequencies._get_freq_str(code, stride) + freq = _get_freq_str(code, stride) freq = frequencies.to_offset(freq) @@ -742,7 +745,7 @@ cdef class _Period(object): raise IncompatibleFrequency(msg.format(self.freqstr)) elif isinstance(other, offsets.DateOffset): freqstr = other.rule_code - base = frequencies.get_base_alias(freqstr) + base = get_base_alias(freqstr) if base == self.freq.rule_code: ordinal = self.ordinal + other.n return Period(ordinal=ordinal, freq=self.freq) @@ -860,7 +863,7 @@ cdef class _Period(object): if freq is None: base, mult = get_freq_code(self.freq) - freq = frequencies.get_to_timestamp_base(base) + freq = get_to_timestamp_base(base) base, mult = get_freq_code(freq) val = self.asfreq(freq, how) @@ -1249,7 +1252,7 @@ def _quarter_to_myear(year, quarter, freq): if quarter <= 0 or quarter > 4: raise ValueError('Quarter must be 1 <= q <= 4') - mnum = tslib._MONTH_NUMBERS[tslib._get_rule_month(freq)] + 1 + mnum = _MONTH_NUMBERS[_get_rule_month(freq)] + 1 month = (mnum + (quarter - 1) * 3) % 12 + 1 if month > mnum: year -= 1 diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index ec12611ae7f02..6fdaf5f0e0deb 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -111,7 +111,8 @@ from tslibs.timezones import ( # noqa _unbox_utcoffsets, _dateutil_gettz ) - +from tslibs.frequencies cimport _get_rule_month +from tslibs.frequencies import _get_rule_month # noqa cdef inline object create_timestamp_from_ts( int64_t value, pandas_datetimestruct dts, @@ -2117,27 +2118,6 @@ _MONTH_NUMBERS = {k: i for i, k in enumerate(_MONTHS)} _MONTH_ALIASES = {(k + 1): v for k, v in enumerate(_MONTHS)} -cpdef object _get_rule_month(object source, object default='DEC'): - """ - Return starting month of given freq, default is December. - - Example - ------- - >>> _get_rule_month('D') - 'DEC' - - >>> _get_rule_month('A-JAN') - 'JAN' - """ - if hasattr(source, 'freqstr'): - source = source.freqstr - source = source.upper() - if '-' not in source: - return default - else: - return source.split('-')[1] - - cpdef array_with_unit_to_datetime(ndarray values, unit, errors='coerce'): """ convert the ndarray according to the unit diff --git a/pandas/_libs/tslibs/frequencies.pxd b/pandas/_libs/tslibs/frequencies.pxd index 974eb4ab45df0..b5bb3cd9dd596 100644 --- a/pandas/_libs/tslibs/frequencies.pxd +++ b/pandas/_libs/tslibs/frequencies.pxd @@ -1,4 +1,10 @@ # -*- coding: utf-8 -*- # cython: profile=False +cpdef object _get_rule_month(object source, object default=*) + cpdef get_freq_code(freqstr) +cpdef get_freq(freq) +cpdef get_base_alias(freqstr) +cpdef get_to_timestamp_base(base) +cpdef _get_freq_str(base, mult=*) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index f7889d76abbc7..4973887e9f4e5 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -6,9 +6,32 @@ cimport cython import numpy as np cimport numpy as np +from numpy cimport int64_t np.import_array() -from util cimport is_integer_object +from util cimport is_integer_object, is_string_object + + +#---------------------------------------------------------------------- +cpdef object _get_rule_month(object source, object default='DEC'): + """ + Return starting month of given freq, default is December. + + Example + ------- + >>> _get_rule_month('D') + 'DEC' + + >>> _get_rule_month('A-JAN') + 'JAN' + """ + if hasattr(source, 'freqstr'): + source = source.freqstr + source = source.upper() + if '-' not in source: + return default + else: + return source.split('-')[1] cpdef get_freq_code(freqstr): @@ -202,3 +225,242 @@ cpdef _period_str_to_code(freqstr): return _period_code_map[freqstr] except KeyError: raise ValueError(_INVALID_FREQ_ERROR.format(freqstr)) + + +cpdef _get_freq_str(base, mult=1): + code = _reverse_period_code_map.get(base) + if mult == 1: + return code + return str(mult) + code + + +cpdef get_base_alias(freqstr): + """ + Returns the base frequency alias, e.g., '5D' -> 'D' + """ + return _base_and_stride(freqstr)[0] + + +class FreqGroup(object): + FR_ANN = 1000 + FR_QTR = 2000 + FR_MTH = 3000 + FR_WK = 4000 + FR_BUS = 5000 + FR_DAY = 6000 + FR_HR = 7000 + FR_MIN = 8000 + FR_SEC = 9000 + FR_MS = 10000 + FR_US = 11000 + FR_NS = 12000 + + +cpdef 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: + return FreqGroup.FR_SEC + return base + + +cpdef 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 is_string_object(freq): + base, mult = get_freq_code(freq) + freq = base + return freq + + +#---------------------------------------------------------------------- +# Frequency comparison + +def is_subperiod(source, target): + """ + Returns True if downsampling is possible between source and target + frequencies + + Parameters + ---------- + source : string + Frequency converting from + target : string + Frequency converting to + + Returns + ------- + is_subperiod : boolean + """ + + if target is None or source is None: + return False + source = _maybe_coerce_freq(source) + target = _maybe_coerce_freq(target) + + if _is_annual(target): + if _is_quarterly(source): + return _quarter_months_conform(_get_rule_month(source), + _get_rule_month(target)) + return source in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] + elif _is_quarterly(target): + return source in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] + elif _is_monthly(target): + return source in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + elif _is_weekly(target): + return source in [target, 'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + elif target == 'B': + return source in ['B', 'H', 'T', 'S', 'L', 'U', 'N'] + elif target == 'C': + return source in ['C', 'H', 'T', 'S', 'L', 'U', 'N'] + elif target == 'D': + return source in ['D', 'H', 'T', 'S', 'L', 'U', 'N'] + elif target == 'H': + return source in ['H', 'T', 'S', 'L', 'U', 'N'] + elif target == 'T': + return source in ['T', 'S', 'L', 'U', 'N'] + elif target == 'S': + return source in ['S', 'L', 'U', 'N'] + elif target == 'L': + return source in ['L', 'U', 'N'] + elif target == 'U': + return source in ['U', 'N'] + elif target == 'N': + return source in ['N'] + + +def is_superperiod(source, target): + """ + Returns True if upsampling is possible between source and target + frequencies + + Parameters + ---------- + source : string + Frequency converting from + target : string + Frequency converting to + + Returns + ------- + is_superperiod : boolean + """ + if target is None or source is None: + return False + source = _maybe_coerce_freq(source) + target = _maybe_coerce_freq(target) + + if _is_annual(source): + if _is_annual(target): + return _get_rule_month(source) == _get_rule_month(target) + + if _is_quarterly(target): + smonth = _get_rule_month(source) + tmonth = _get_rule_month(target) + return _quarter_months_conform(smonth, tmonth) + return target in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] + elif _is_quarterly(source): + return target in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] + elif _is_monthly(source): + return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + elif _is_weekly(source): + return target in [source, 'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + elif source == 'B': + return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + elif source == 'C': + return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + elif source == 'D': + return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + elif source == 'H': + return target in ['H', 'T', 'S', 'L', 'U', 'N'] + elif source == 'T': + return target in ['T', 'S', 'L', 'U', 'N'] + elif source == 'S': + return target in ['S', 'L', 'U', 'N'] + elif source == 'L': + return target in ['L', 'U', 'N'] + elif source == 'U': + return target in ['U', 'N'] + elif source == 'N': + return target in ['N'] + + +def _maybe_coerce_freq(code): + """ we might need to coerce a code to a rule_code + and uppercase it + + Parameters + ---------- + source : string + Frequency converting from + + Returns + ------- + string code + """ + assert code is not None + if getattr(code, '_typ', None) == 'dateoffset': + # i.e. isinstance(code, ABCDateOffset): + code = code.rule_code + return code.upper() + + +_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', + 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] +_MONTH_NUMBERS = {k: i for i, k in enumerate(_MONTHS)} + + +def _quarter_months_conform(source, target): + snum = _MONTH_NUMBERS[source] + tnum = _MONTH_NUMBERS[target] + return snum % 3 == tnum % 3 + + +def _is_annual(rule): + rule = rule.upper() + return rule == 'A' or rule.startswith('A-') + + +def _is_quarterly(rule): + rule = rule.upper() + return rule == 'Q' or rule.startswith('Q-') or rule.startswith('BQ') + + +def _is_monthly(rule): + rule = rule.upper() + return rule == 'M' or rule == 'BM' + + +def _is_weekly(rule): + rule = rule.upper() + return rule == 'W' or rule.startswith('W-') diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 085a3a784557b..2f876bea4ad00 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -23,25 +23,14 @@ from pandas._libs.tslibs.frequencies import ( # noqa get_freq_code, _base_and_stride, _period_str_to_code, _INVALID_FREQ_ERROR, opattern, _lite_rule_alias, _dont_uppercase, - _period_code_map, _reverse_period_code_map) + _period_code_map, _reverse_period_code_map, get_base_alias, + FreqGroup, get_to_timestamp_base, get_freq, _get_freq_str, + _maybe_coerce_freq, is_subperiod, is_superperiod, + _is_annual, _is_quarterly, _is_monthly, _is_weekly, + _quarter_months_conform, _get_rule_month) from pytz import AmbiguousTimeError -class FreqGroup(object): - FR_ANN = 1000 - FR_QTR = 2000 - FR_MTH = 3000 - FR_WK = 4000 - FR_BUS = 5000 - FR_DAY = 6000 - FR_HR = 7000 - FR_MIN = 8000 - FR_SEC = 9000 - FR_MS = 10000 - FR_US = 11000 - FR_NS = 12000 - - RESO_NS = 0 RESO_US = 1 RESO_MS = 2 @@ -229,34 +218,6 @@ def get_stride_from_decimal(cls, value, freq): return cls.get_stride_from_decimal(next_value, next_name) -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: - return FreqGroup.FR_SEC - return base - - def get_freq_group(freq): """ Return frequency code group of given frequency str or offset. @@ -282,32 +243,6 @@ def get_freq_group(freq): return (freq // 1000) * 1000 -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 - return freq - - -def _get_freq_str(base, mult=1): - code = _reverse_period_code_map.get(base) - if mult == 1: - return code - return str(mult) + code - - # --------------------------------------------------------------------- # Offset names ("time rules") and related functions @@ -498,13 +433,6 @@ def to_offset(freq): return delta -def get_base_alias(freqstr): - """ - Returns the base frequency alias, e.g., '5D' -> 'D' - """ - return _base_and_stride(freqstr)[0] - - def get_offset(name): """ Return DateOffset object associated with rule name @@ -878,164 +806,6 @@ def _maybe_add_count(base, count): return base -def _maybe_coerce_freq(code): - """ we might need to coerce a code to a rule_code - and uppercase it - - Parameters - ---------- - source : string - Frequency converting from - - Returns - ------- - string code - """ - - assert code is not None - if isinstance(code, offsets.DateOffset): - code = code.rule_code - return code.upper() - - -def is_subperiod(source, target): - """ - Returns True if downsampling is possible between source and target - frequencies - - Parameters - ---------- - source : string - Frequency converting from - target : string - Frequency converting to - - Returns - ------- - is_subperiod : boolean - """ - - if target is None or source is None: - return False - source = _maybe_coerce_freq(source) - target = _maybe_coerce_freq(target) - - if _is_annual(target): - if _is_quarterly(source): - return _quarter_months_conform(_get_rule_month(source), - _get_rule_month(target)) - return source in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] - elif _is_quarterly(target): - return source in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] - elif _is_monthly(target): - return source in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] - elif _is_weekly(target): - return source in [target, 'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] - elif target == 'B': - return source in ['B', 'H', 'T', 'S', 'L', 'U', 'N'] - elif target == 'C': - return source in ['C', 'H', 'T', 'S', 'L', 'U', 'N'] - elif target == 'D': - return source in ['D', 'H', 'T', 'S', 'L', 'U', 'N'] - elif target == 'H': - return source in ['H', 'T', 'S', 'L', 'U', 'N'] - elif target == 'T': - return source in ['T', 'S', 'L', 'U', 'N'] - elif target == 'S': - return source in ['S', 'L', 'U', 'N'] - elif target == 'L': - return source in ['L', 'U', 'N'] - elif target == 'U': - return source in ['U', 'N'] - elif target == 'N': - return source in ['N'] - - -def is_superperiod(source, target): - """ - Returns True if upsampling is possible between source and target - frequencies - - Parameters - ---------- - source : string - Frequency converting from - target : string - Frequency converting to - - Returns - ------- - is_superperiod : boolean - """ - if target is None or source is None: - return False - source = _maybe_coerce_freq(source) - target = _maybe_coerce_freq(target) - - if _is_annual(source): - if _is_annual(target): - return _get_rule_month(source) == _get_rule_month(target) - - if _is_quarterly(target): - smonth = _get_rule_month(source) - tmonth = _get_rule_month(target) - return _quarter_months_conform(smonth, tmonth) - return target in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] - elif _is_quarterly(source): - return target in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] - elif _is_monthly(source): - return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] - elif _is_weekly(source): - return target in [source, 'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] - elif source == 'B': - return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] - elif source == 'C': - return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] - elif source == 'D': - return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] - elif source == 'H': - return target in ['H', 'T', 'S', 'L', 'U', 'N'] - elif source == 'T': - return target in ['T', 'S', 'L', 'U', 'N'] - elif source == 'S': - return target in ['S', 'L', 'U', 'N'] - elif source == 'L': - return target in ['L', 'U', 'N'] - elif source == 'U': - return target in ['U', 'N'] - elif source == 'N': - return target in ['N'] - - -_get_rule_month = tslib._get_rule_month - - -def _is_annual(rule): - rule = rule.upper() - return rule == 'A' or rule.startswith('A-') - - -def _quarter_months_conform(source, target): - snum = _month_numbers[source] - tnum = _month_numbers[target] - return snum % 3 == tnum % 3 - - -def _is_quarterly(rule): - rule = rule.upper() - return rule == 'Q' or rule.startswith('Q-') or rule.startswith('BQ') - - -def _is_monthly(rule): - rule = rule.upper() - return rule == 'M' or rule == 'BM' - - -def _is_weekly(rule): - rule = rule.upper() - return rule == 'W' or rule.startswith('W-') - - DAYS = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] MONTHS = tslib._MONTHS From b5198f368d9cbb6c4798fea37e1b4dba2fa1c1cb Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 19 Oct 2017 21:32:37 -0700 Subject: [PATCH 02/32] more specific noqa, try to fix incorrect flake warning --- pandas/tseries/frequencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 2ac49873452e7..9fdedf506fdef 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -21,7 +21,7 @@ from pandas._libs import lib, tslib from pandas._libs.tslib import Timedelta -from pandas._libs.tslibs.frequencies import ( # noqa +from pandas._libs.tslibs.frequencies import ( # noqa:F811 get_freq_code, _base_and_stride, _period_str_to_code, _INVALID_FREQ_ERROR, opattern, _lite_rule_alias, _dont_uppercase, _period_code_map, _reverse_period_code_map, get_base_alias, From 3bfbdf1dbf577715463a8ab883500e1d303a4bb2 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 24 Oct 2017 09:34:09 -0700 Subject: [PATCH 03/32] disable flake8 false-positives --- pandas/tseries/frequencies.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 9fdedf506fdef..19c3ecdc8db01 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -19,9 +19,9 @@ from pandas.util._decorators import cache_readonly, deprecate_kwarg import pandas.tseries.offsets as offsets -from pandas._libs import lib, tslib +from pandas._libs import tslib from pandas._libs.tslib import Timedelta -from pandas._libs.tslibs.frequencies import ( # noqa:F811 +from pandas._libs.tslibs.frequencies import ( # noqa:F401 get_freq_code, _base_and_stride, _period_str_to_code, _INVALID_FREQ_ERROR, opattern, _lite_rule_alias, _dont_uppercase, _period_code_map, _reverse_period_code_map, get_base_alias, @@ -138,7 +138,7 @@ def get_freq_group(cls, resostr): """ return get_freq_group(cls.get_freq(resostr)) - @classmethod + @classmethod # noqa:F811 # Disable flake8 false-positive def get_freq(cls, resostr): """ Return frequency str against resolution str. @@ -582,7 +582,7 @@ def is_unique(self): def is_unique_asi8(self): return len(self.deltas_asi8) == 1 - def get_freq(self): + def get_freq(self): # noqa:F811 # Disable flake8 false-positive if not self.is_monotonic or not self.index.is_unique: return None @@ -632,7 +632,7 @@ def fields(self): @cache_readonly def rep_stamp(self): - return lib.Timestamp(self.values[0]) + return tslib.Timestamp(self.values[0]) def month_position_check(self): # TODO: cythonize this, very slow From 74258c098e6190b9e12ebaf686329cdcde783dee Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 1 Nov 2017 09:25:09 -0700 Subject: [PATCH 04/32] flake8 fixup --- pandas/_libs/tslibs/frequencies.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 92adbea76acf7..004f8e74eff9d 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -12,7 +12,7 @@ np.import_array() from util cimport is_integer_object, is_string_object -#---------------------------------------------------------------------- +# ---------------------------------------------------------------------- cpdef object _get_rule_month(object source, object default='DEC'): """ Return starting month of given freq, default is December. @@ -304,7 +304,7 @@ cpdef get_freq(freq): return freq -#---------------------------------------------------------------------- +# ---------------------------------------------------------------------- # Frequency comparison def is_subperiod(source, target): From 823db82ea8d3eecec0d6254f67509836fc428f77 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 1 Nov 2017 09:30:32 -0700 Subject: [PATCH 05/32] consolidate constants near top of file --- pandas/_libs/tslibs/frequencies.pyx | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 004f8e74eff9d..402ccfa506617 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -11,31 +11,13 @@ np.import_array() from util cimport is_integer_object, is_string_object - -# ---------------------------------------------------------------------- -cpdef object _get_rule_month(object source, object default='DEC'): - """ - Return starting month of given freq, default is December. - - Example - ------- - >>> _get_rule_month('D') - 'DEC' - - >>> _get_rule_month('A-JAN') - 'JAN' - """ - if hasattr(source, 'freqstr'): - source = source.freqstr - source = source.upper() - if '-' not in source: - return default - else: - return source.split('-')[1] - # ---------------------------------------------------------------------- # Constants +_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', + 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] +_MONTH_NUMBERS = {k: i for i, k in enumerate(_MONTHS)} + # hack to handle WOM-1MON opattern = re.compile( r'([\-]?\d*|[\-]?\d*\.\d*)\s*([A-Za-z]+([\-][\dA-Za-z\-]+)?)' @@ -436,11 +418,6 @@ def _maybe_coerce_freq(code): return code.upper() -_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', - 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] -_MONTH_NUMBERS = {k: i for i, k in enumerate(_MONTHS)} - - def _quarter_months_conform(source, target): snum = _MONTH_NUMBERS[source] tnum = _MONTH_NUMBERS[target] @@ -465,3 +442,26 @@ def _is_monthly(rule): def _is_weekly(rule): rule = rule.upper() return rule == 'W' or rule.startswith('W-') + + +# ---------------------------------------------------------------------- + +cpdef object _get_rule_month(object source, object default='DEC'): + """ + Return starting month of given freq, default is December. + + Example + ------- + >>> _get_rule_month('D') + 'DEC' + + >>> _get_rule_month('A-JAN') + 'JAN' + """ + if hasattr(source, 'freqstr'): + source = source.freqstr + source = source.upper() + if '-' not in source: + return default + else: + return source.split('-')[1] From 551f0471cd768354b0cb444c67d350b31aabf3b6 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 2 Nov 2017 17:14:43 -0700 Subject: [PATCH 06/32] remove unused variable --- pandas/tseries/frequencies.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 5201721145f8d..3f2877359beb0 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -765,7 +765,6 @@ def _maybe_add_count(base, count): DAYS = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] MONTHS = tslib._MONTHS -_month_numbers = tslib._MONTH_NUMBERS _month_aliases = tslib._MONTH_ALIASES _weekday_rule_aliases = dict((k, v) for k, v in enumerate(DAYS)) From 2a2155eab4bd95f4fe996db786e578f18bd662de Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 10 Nov 2017 07:44:01 -0800 Subject: [PATCH 07/32] update imports per reviewer request --- pandas/_libs/tslib.pyx | 1 - pandas/tests/scalar/test_period_asfreq.py | 2 +- pandas/tests/tseries/test_frequencies.py | 69 ++++++++++++----------- pandas/tests/tseries/test_offsets.py | 6 +- pandas/tseries/frequencies.py | 16 +++--- 5 files changed, 49 insertions(+), 45 deletions(-) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index eed3f49975343..c9e7317eedd0c 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -90,7 +90,6 @@ from tslibs.timezones cimport ( treat_tz_as_dateutil, treat_tz_as_pytz, get_timezone, get_utcoffset, maybe_get_tz, get_dst_info) -from tslibs.frequencies cimport _get_rule_month from tslibs.fields import ( get_date_name_field, get_start_end_field, get_date_field, build_field_sarray) diff --git a/pandas/tests/scalar/test_period_asfreq.py b/pandas/tests/scalar/test_period_asfreq.py index 32cea60c333b7..c7fbe61488fcc 100644 --- a/pandas/tests/scalar/test_period_asfreq.py +++ b/pandas/tests/scalar/test_period_asfreq.py @@ -1,7 +1,7 @@ import pandas as pd from pandas import Period, offsets from pandas.util import testing as tm -from pandas.tseries.frequencies import _period_code_map +from pandas._libs.tslibs.frequencies import _period_code_map class TestFreqConversion(object): diff --git a/pandas/tests/tseries/test_frequencies.py b/pandas/tests/tseries/test_frequencies.py index 745f3ef2ec5cb..450e42f8cccf6 100644 --- a/pandas/tests/tseries/test_frequencies.py +++ b/pandas/tests/tseries/test_frequencies.py @@ -7,6 +7,9 @@ from pandas import (Index, DatetimeIndex, Timestamp, Series, date_range, period_range) +from pandas._libs.tslibs.frequencies import (_period_code_map, _get_rule_month, + _period_str_to_code) + import pandas.tseries.frequencies as frequencies from pandas.core.tools.datetimes import to_datetime @@ -284,84 +287,84 @@ def test_rule_aliases(): def test_get_rule_month(): - result = frequencies._get_rule_month('W') + result = _get_rule_month('W') assert (result == 'DEC') - result = frequencies._get_rule_month(offsets.Week()) + result = _get_rule_month(offsets.Week()) assert (result == 'DEC') - result = frequencies._get_rule_month('D') + result = _get_rule_month('D') assert (result == 'DEC') - result = frequencies._get_rule_month(offsets.Day()) + result = _get_rule_month(offsets.Day()) assert (result == 'DEC') - result = frequencies._get_rule_month('Q') + result = _get_rule_month('Q') assert (result == 'DEC') - result = frequencies._get_rule_month(offsets.QuarterEnd(startingMonth=12)) + result = _get_rule_month(offsets.QuarterEnd(startingMonth=12)) print(result == 'DEC') - result = frequencies._get_rule_month('Q-JAN') + result = _get_rule_month('Q-JAN') assert (result == 'JAN') - result = frequencies._get_rule_month(offsets.QuarterEnd(startingMonth=1)) + result = _get_rule_month(offsets.QuarterEnd(startingMonth=1)) assert (result == 'JAN') - result = frequencies._get_rule_month('A-DEC') + result = _get_rule_month('A-DEC') assert (result == 'DEC') - result = frequencies._get_rule_month('Y-DEC') + result = _get_rule_month('Y-DEC') assert (result == 'DEC') - result = frequencies._get_rule_month(offsets.YearEnd()) + result = _get_rule_month(offsets.YearEnd()) assert (result == 'DEC') - result = frequencies._get_rule_month('A-MAY') + result = _get_rule_month('A-MAY') assert (result == 'MAY') - result = frequencies._get_rule_month('Y-MAY') + result = _get_rule_month('Y-MAY') assert (result == 'MAY') - result = frequencies._get_rule_month(offsets.YearEnd(month=5)) + result = _get_rule_month(offsets.YearEnd(month=5)) assert (result == 'MAY') def test_period_str_to_code(): - assert (frequencies._period_str_to_code('A') == 1000) - assert (frequencies._period_str_to_code('A-DEC') == 1000) - assert (frequencies._period_str_to_code('A-JAN') == 1001) - assert (frequencies._period_str_to_code('Y') == 1000) - assert (frequencies._period_str_to_code('Y-DEC') == 1000) - assert (frequencies._period_str_to_code('Y-JAN') == 1001) + assert (_period_str_to_code('A') == 1000) + assert (_period_str_to_code('A-DEC') == 1000) + assert (_period_str_to_code('A-JAN') == 1001) + assert (_period_str_to_code('Y') == 1000) + assert (_period_str_to_code('Y-DEC') == 1000) + assert (_period_str_to_code('Y-JAN') == 1001) - assert (frequencies._period_str_to_code('Q') == 2000) - assert (frequencies._period_str_to_code('Q-DEC') == 2000) - assert (frequencies._period_str_to_code('Q-FEB') == 2002) + assert (_period_str_to_code('Q') == 2000) + assert (_period_str_to_code('Q-DEC') == 2000) + assert (_period_str_to_code('Q-FEB') == 2002) def _assert_depr(freq, expected, aliases): assert isinstance(aliases, list) - assert (frequencies._period_str_to_code(freq) == expected) + assert (_period_str_to_code(freq) == expected) msg = frequencies._INVALID_FREQ_ERROR for alias in aliases: with tm.assert_raises_regex(ValueError, msg): - frequencies._period_str_to_code(alias) + _period_str_to_code(alias) _assert_depr("M", 3000, ["MTH", "MONTH", "MONTHLY"]) - assert (frequencies._period_str_to_code('W') == 4000) - assert (frequencies._period_str_to_code('W-SUN') == 4000) - assert (frequencies._period_str_to_code('W-FRI') == 4005) + assert (_period_str_to_code('W') == 4000) + assert (_period_str_to_code('W-SUN') == 4000) + assert (_period_str_to_code('W-FRI') == 4005) _assert_depr("B", 5000, ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY"]) _assert_depr("D", 6000, ["DAY", "DLY", "DAILY"]) _assert_depr("H", 7000, ["HR", "HOUR", "HRLY", "HOURLY"]) _assert_depr("T", 8000, ["minute", "MINUTE", "MINUTELY"]) - assert (frequencies._period_str_to_code('Min') == 8000) + assert (_period_str_to_code('Min') == 8000) _assert_depr("S", 9000, ["sec", "SEC", "SECOND", "SECONDLY"]) _assert_depr("L", 10000, ["MILLISECOND", "MILLISECONDLY"]) - assert (frequencies._period_str_to_code('ms') == 10000) + assert (_period_str_to_code('ms') == 10000) _assert_depr("U", 11000, ["MICROSECOND", "MICROSECONDLY"]) - assert (frequencies._period_str_to_code('US') == 11000) + assert (_period_str_to_code('US') == 11000) _assert_depr("N", 12000, ["NANOSECOND", "NANOSECONDLY"]) - assert (frequencies._period_str_to_code('NS') == 12000) + assert (_period_str_to_code('NS') == 12000) class TestFrequencyCode(object): @@ -379,7 +382,7 @@ def test_freq_code(self): assert frequencies.get_freq('W-MON') == 4001 assert frequencies.get_freq('W-FRI') == 4005 - for freqstr, code in compat.iteritems(frequencies._period_code_map): + for freqstr, code in compat.iteritems(_period_code_map): result = frequencies.get_freq(freqstr) assert result == code diff --git a/pandas/tests/tseries/test_offsets.py b/pandas/tests/tseries/test_offsets.py index 4fd3bba01602f..85d03b417db7b 100644 --- a/pandas/tests/tseries/test_offsets.py +++ b/pandas/tests/tseries/test_offsets.py @@ -12,8 +12,10 @@ from pandas.compat.numpy import np_datetime64_compat from pandas.core.series import Series -from pandas.tseries.frequencies import (_offset_map, get_freq_code, - _get_freq_str, _INVALID_FREQ_ERROR, + +from pandas._libs.tslibs.frequencies import get_freq_code, _get_freq_str +from pandas.tseries.frequencies import (_offset_map, + _INVALID_FREQ_ERROR, get_offset, get_standard_freq) from pandas.core.indexes.datetimes import ( _to_m8, DatetimeIndex, _daterange_cache) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 3f2877359beb0..d08cd674d355e 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -21,14 +21,14 @@ from pandas._libs import tslib from pandas._libs.tslib import Timedelta -from pandas._libs.tslibs.frequencies import ( # noqa:F401 - get_freq_code, _base_and_stride, _period_str_to_code, - _INVALID_FREQ_ERROR, opattern, _lite_rule_alias, _dont_uppercase, - _period_code_map, _reverse_period_code_map, get_base_alias, - FreqGroup, get_to_timestamp_base, get_freq, _get_freq_str, - _maybe_coerce_freq, is_subperiod, is_superperiod, - _is_annual, _is_quarterly, _is_monthly, _is_weekly, - _quarter_months_conform, _get_rule_month) +from pandas._libs.tslibs.frequencies import ( + get_freq_code, _base_and_stride, + _INVALID_FREQ_ERROR, opattern, + _lite_rule_alias, _dont_uppercase) +from pandas._libs.tslibs.frequencies import ( # noqa + get_freq, get_base_alias, get_to_timestamp_base, + FreqGroup, + is_subperiod, is_superperiod) from pytz import AmbiguousTimeError From 4192c0db99ab96d925b25290e445d6d58f77b218 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 10 Nov 2017 08:36:18 -0800 Subject: [PATCH 08/32] trim namespace using libfreqs --- pandas/tseries/frequencies.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index d08cd674d355e..34cbbe983c3be 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -21,11 +21,9 @@ from pandas._libs import tslib from pandas._libs.tslib import Timedelta -from pandas._libs.tslibs.frequencies import ( - get_freq_code, _base_and_stride, - _INVALID_FREQ_ERROR, opattern, - _lite_rule_alias, _dont_uppercase) -from pandas._libs.tslibs.frequencies import ( # noqa +import pandas._libs.tslibs.frequencies as libfreqs +from pandas._libs.tslibs.frequencies import get_freq_code +from pandas._libs.tslibs.frequencies import ( # noqa, semi-public API get_freq, get_base_alias, get_to_timestamp_base, FreqGroup, is_subperiod, is_superperiod) @@ -333,7 +331,7 @@ def to_offset(freq): stride = freq[1] if isinstance(stride, compat.string_types): name, stride = stride, name - name, _ = _base_and_stride(name) + name, _ = libfreqs._base_and_stride(name) delta = get_offset(name) * stride elif isinstance(freq, timedelta): @@ -350,13 +348,13 @@ def to_offset(freq): else: delta = delta + offset except Exception: - raise ValueError(_INVALID_FREQ_ERROR.format(freq)) + raise ValueError(libfreqs._INVALID_FREQ_ERROR.format(freq)) else: delta = None stride_sign = None try: - splitted = re.split(opattern, freq) + splitted = re.split(libfreqs.opattern, freq) if splitted[-1] != '' and not splitted[-1].isspace(): # the last element must be blank raise ValueError('last element must be blank') @@ -364,7 +362,7 @@ def to_offset(freq): splitted[2::4]): if sep != '' and not sep.isspace(): raise ValueError('separator must be spaces') - prefix = _lite_rule_alias.get(name) or name + prefix = libfreqs._lite_rule_alias.get(name) or name if stride_sign is None: stride_sign = -1 if stride.startswith('-') else 1 if not stride: @@ -381,10 +379,10 @@ def to_offset(freq): else: delta = delta + offset except Exception: - raise ValueError(_INVALID_FREQ_ERROR.format(freq)) + raise ValueError(libfreqs._INVALID_FREQ_ERROR.format(freq)) if delta is None: - raise ValueError(_INVALID_FREQ_ERROR.format(freq)) + raise ValueError(libfreqs._INVALID_FREQ_ERROR.format(freq)) return delta @@ -397,12 +395,12 @@ def get_offset(name): -------- get_offset('EOM') --> BMonthEnd(1) """ - if name not in _dont_uppercase: + if name not in libfreqs._dont_uppercase: name = name.upper() - name = _lite_rule_alias.get(name, name) - name = _lite_rule_alias.get(name.lower(), name) + name = libfreqs._lite_rule_alias.get(name, name) + name = libfreqs._lite_rule_alias.get(name.lower(), name) else: - name = _lite_rule_alias.get(name, name) + name = libfreqs._lite_rule_alias.get(name, name) if name not in _offset_map: try: @@ -413,7 +411,7 @@ def get_offset(name): offset = klass._from_name(*split[1:]) except (ValueError, TypeError, KeyError): # bad prefix or suffix - raise ValueError(_INVALID_FREQ_ERROR.format(name)) + raise ValueError(libfreqs._INVALID_FREQ_ERROR.format(name)) # cache _offset_map[name] = offset # do not return cache because it's mutable From 21e13dc8fbd00c88a37379a334f8666c2c39876d Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 10 Nov 2017 11:49:04 -0800 Subject: [PATCH 09/32] fix import --- pandas/tests/tseries/test_frequencies.py | 7 ++++--- pandas/tests/tseries/test_offsets.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pandas/tests/tseries/test_frequencies.py b/pandas/tests/tseries/test_frequencies.py index 450e42f8cccf6..7fe29187b23f5 100644 --- a/pandas/tests/tseries/test_frequencies.py +++ b/pandas/tests/tseries/test_frequencies.py @@ -8,7 +8,8 @@ date_range, period_range) from pandas._libs.tslibs.frequencies import (_period_code_map, _get_rule_month, - _period_str_to_code) + _period_str_to_code, + _INVALID_FREQ_ERROR) import pandas.tseries.frequencies as frequencies from pandas.core.tools.datetimes import to_datetime @@ -338,7 +339,7 @@ def _assert_depr(freq, expected, aliases): assert isinstance(aliases, list) assert (_period_str_to_code(freq) == expected) - msg = frequencies._INVALID_FREQ_ERROR + msg = _INVALID_FREQ_ERROR for alias in aliases: with tm.assert_raises_regex(ValueError, msg): _period_str_to_code(alias) @@ -881,7 +882,7 @@ def test_legacy_offset_warnings(self): 'WOM@4THU', 'WOM@1FRI', 'WOM@2FRI', 'WOM@3FRI', 'WOM@4FRI'] - msg = frequencies._INVALID_FREQ_ERROR + msg = _INVALID_FREQ_ERROR for freq in freqs: with tm.assert_raises_regex(ValueError, msg): frequencies.get_offset(freq) diff --git a/pandas/tests/tseries/test_offsets.py b/pandas/tests/tseries/test_offsets.py index 85d03b417db7b..9cb1e37b2f8d5 100644 --- a/pandas/tests/tseries/test_offsets.py +++ b/pandas/tests/tseries/test_offsets.py @@ -13,9 +13,9 @@ from pandas.core.series import Series -from pandas._libs.tslibs.frequencies import get_freq_code, _get_freq_str +from pandas._libs.tslibs.frequencies import (get_freq_code, _get_freq_str, + _INVALID_FREQ_ERROR) from pandas.tseries.frequencies import (_offset_map, - _INVALID_FREQ_ERROR, get_offset, get_standard_freq) from pandas.core.indexes.datetimes import ( _to_m8, DatetimeIndex, _daterange_cache) From 67dbfe211f03783fe5dbf3eb1253f105c79dfe36 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 10 Nov 2017 15:18:00 -0800 Subject: [PATCH 10/32] update imports --- pandas/tests/indexes/datetimes/test_ops.py | 2 +- pandas/tests/indexes/period/test_tools.py | 2 +- pandas/tests/indexes/timedeltas/test_ops.py | 2 +- pandas/tests/scalar/test_period.py | 4 ++-- pandas/tests/scalar/test_period_asfreq.py | 6 +++--- pandas/tests/scalar/test_timestamp.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py index 9e4f8d979ca99..cedafa96a36b6 100644 --- a/pandas/tests/indexes/datetimes/test_ops.py +++ b/pandas/tests/indexes/datetimes/test_ops.py @@ -160,7 +160,7 @@ def test_round(self): tm.assert_index_equal(rng.round(freq='H'), expected_rng) assert elt.round(freq='H') == expected_elt - msg = pd.tseries.frequencies._INVALID_FREQ_ERROR + msg = pd._libs.tslibs.frequencies._INVALID_FREQ_ERROR with tm.assert_raises_regex(ValueError, msg): rng.round(freq='foo') with tm.assert_raises_regex(ValueError, msg): diff --git a/pandas/tests/indexes/period/test_tools.py b/pandas/tests/indexes/period/test_tools.py index 074678164e6f9..0e65b4c300d47 100644 --- a/pandas/tests/indexes/period/test_tools.py +++ b/pandas/tests/indexes/period/test_tools.py @@ -367,7 +367,7 @@ def test_to_period_monthish(self): prng = rng.to_period() assert prng.freq == 'M' - msg = pd.tseries.frequencies._INVALID_FREQ_ERROR + msg = pd._libs.tslibs.frequencies._INVALID_FREQ_ERROR with tm.assert_raises_regex(ValueError, msg): date_range('01-Jan-2012', periods=8, freq='EOM') diff --git a/pandas/tests/indexes/timedeltas/test_ops.py b/pandas/tests/indexes/timedeltas/test_ops.py index 67238665a2e8a..6732fc2aa1ca1 100644 --- a/pandas/tests/indexes/timedeltas/test_ops.py +++ b/pandas/tests/indexes/timedeltas/test_ops.py @@ -114,7 +114,7 @@ def test_round(self): tm.assert_index_equal(td.round(freq='H'), expected_rng) assert elt.round(freq='H') == expected_elt - msg = pd.tseries.frequencies._INVALID_FREQ_ERROR + msg = pd._libs.tslibs.frequencies._INVALID_FREQ_ERROR with tm.assert_raises_regex(ValueError, msg): td.round(freq='foo') with tm.assert_raises_regex(ValueError, msg): diff --git a/pandas/tests/scalar/test_period.py b/pandas/tests/scalar/test_period.py index 28d85c52604d9..d2962c94a2821 100644 --- a/pandas/tests/scalar/test_period.py +++ b/pandas/tests/scalar/test_period.py @@ -513,7 +513,7 @@ def test_period_deprecated_freq(self): "U": ["MICROSECOND", "MICROSECONDLY", "microsecond"], "N": ["NANOSECOND", "NANOSECONDLY", "nanosecond"]} - msg = pd.tseries.frequencies._INVALID_FREQ_ERROR + msg = pd._libs.tslibs.frequencies._INVALID_FREQ_ERROR for exp, freqs in iteritems(cases): for freq in freqs: with tm.assert_raises_regex(ValueError, msg): @@ -757,7 +757,7 @@ def test_properties_weekly_legacy(self): exp = Period(freq='W', year=2012, month=2, day=1) assert exp.days_in_month == 29 - msg = pd.tseries.frequencies._INVALID_FREQ_ERROR + msg = pd._libs.tslibs.frequencies._INVALID_FREQ_ERROR with tm.assert_raises_regex(ValueError, msg): Period(freq='WK', year=2007, month=1, day=7) diff --git a/pandas/tests/scalar/test_period_asfreq.py b/pandas/tests/scalar/test_period_asfreq.py index c7fbe61488fcc..295209930d435 100644 --- a/pandas/tests/scalar/test_period_asfreq.py +++ b/pandas/tests/scalar/test_period_asfreq.py @@ -293,13 +293,13 @@ def test_conv_weekly(self): assert ival_W.asfreq('W') == ival_W - msg = pd.tseries.frequencies._INVALID_FREQ_ERROR + msg = pd._libs.tslibs.frequencies._INVALID_FREQ_ERROR with tm.assert_raises_regex(ValueError, msg): ival_W.asfreq('WK') def test_conv_weekly_legacy(self): # frequency conversion tests: from Weekly Frequency - msg = pd.tseries.frequencies._INVALID_FREQ_ERROR + msg = pd._libs.tslibs.frequencies._INVALID_FREQ_ERROR with tm.assert_raises_regex(ValueError, msg): Period(freq='WK', year=2007, month=1, day=1) @@ -706,7 +706,7 @@ def test_asfreq_MS(self): assert initial.asfreq(freq="M", how="S") == Period('2013-01', 'M') - msg = pd.tseries.frequencies._INVALID_FREQ_ERROR + msg = pd._libs.tslibs._INVALID_FREQ_ERROR with tm.assert_raises_regex(ValueError, msg): initial.asfreq(freq="MS", how="S") diff --git a/pandas/tests/scalar/test_timestamp.py b/pandas/tests/scalar/test_timestamp.py index 4cd9a2fadeb32..08ef15936363e 100644 --- a/pandas/tests/scalar/test_timestamp.py +++ b/pandas/tests/scalar/test_timestamp.py @@ -18,6 +18,7 @@ from pandas.tseries import offsets, frequencies from pandas._libs import tslib, period from pandas._libs.tslibs.timezones import get_timezone +from pandas._libs.tslibs.frequencies import _INVALID_FREQ_ERROR from pandas.compat import lrange, long, PY3 from pandas.util.testing import assert_series_equal @@ -744,8 +745,7 @@ def _check_round(freq, expected): ('S', Timestamp('2000-01-05 05:09:15'))]: _check_round(freq, expected) - msg = frequencies._INVALID_FREQ_ERROR - with tm.assert_raises_regex(ValueError, msg): + with tm.assert_raises_regex(ValueError, _INVALID_FREQ_ERROR): stamp.round('foo') def test_class_ops_pytz(self): From 0292fbe9c86469a8e2c923f9edfddea76a708e62 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 10 Nov 2017 17:46:17 -0800 Subject: [PATCH 11/32] fix import --- pandas/tests/scalar/test_period_asfreq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/scalar/test_period_asfreq.py b/pandas/tests/scalar/test_period_asfreq.py index 295209930d435..a2819a3478f79 100644 --- a/pandas/tests/scalar/test_period_asfreq.py +++ b/pandas/tests/scalar/test_period_asfreq.py @@ -706,7 +706,7 @@ def test_asfreq_MS(self): assert initial.asfreq(freq="M", how="S") == Period('2013-01', 'M') - msg = pd._libs.tslibs._INVALID_FREQ_ERROR + msg = pd._libs.tslibs.frequencies._INVALID_FREQ_ERROR with tm.assert_raises_regex(ValueError, msg): initial.asfreq(freq="MS", how="S") From 22f46ac30e23b5a9f6293efe9a3120bd2cb24020 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 10 Nov 2017 20:10:39 -0800 Subject: [PATCH 12/32] remove unused import --- pandas/tests/scalar/test_timestamp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tests/scalar/test_timestamp.py b/pandas/tests/scalar/test_timestamp.py index 08ef15936363e..fcb10d61915fd 100644 --- a/pandas/tests/scalar/test_timestamp.py +++ b/pandas/tests/scalar/test_timestamp.py @@ -15,7 +15,6 @@ from pytz.exceptions import AmbiguousTimeError, NonExistentTimeError import pandas.util.testing as tm -from pandas.tseries import offsets, frequencies from pandas._libs import tslib, period from pandas._libs.tslibs.timezones import get_timezone from pandas._libs.tslibs.frequencies import _INVALID_FREQ_ERROR From bcc5ca1fec0e73e503f9d926486fd9d4c851b10a Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sat, 11 Nov 2017 08:24:47 -0800 Subject: [PATCH 13/32] fixup missing import --- pandas/tests/scalar/test_timestamp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/tests/scalar/test_timestamp.py b/pandas/tests/scalar/test_timestamp.py index fcb10d61915fd..75572c8e08b88 100644 --- a/pandas/tests/scalar/test_timestamp.py +++ b/pandas/tests/scalar/test_timestamp.py @@ -26,6 +26,7 @@ Series, NaT, DataFrame, DatetimeIndex) from pandas.tseries.frequencies import (RESO_DAY, RESO_HR, RESO_MIN, RESO_US, RESO_MS, RESO_SEC) +from pandas.tseries import offsets class TestTimestampArithmetic(object): From 7bcdbe0ab242bd44d28bee4e98e32c77db5d27de Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 13 Nov 2017 12:29:22 -0800 Subject: [PATCH 14/32] fixup imports --- pandas/tests/indexes/period/test_tools.py | 5 +++-- pandas/tests/test_resample.py | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pandas/tests/indexes/period/test_tools.py b/pandas/tests/indexes/period/test_tools.py index 0e65b4c300d47..cbad92a6fad3c 100644 --- a/pandas/tests/indexes/period/test_tools.py +++ b/pandas/tests/indexes/period/test_tools.py @@ -5,7 +5,8 @@ import pandas.util.testing as tm import pandas.core.indexes.period as period from pandas.compat import lrange -from pandas.tseries.frequencies import get_freq, MONTHS + +from pandas._libs.tslibs.frequencies import get_freq, _MONTHS from pandas._libs.period import period_ordinal, period_asfreq from pandas import (PeriodIndex, Period, DatetimeIndex, Timestamp, Series, date_range, to_datetime, period_range) @@ -335,7 +336,7 @@ def test_period_astype_to_timestamp(self): def test_to_period_quarterly(self): # make sure we can make the round trip - for month in MONTHS: + for month in _MONTHS: freq = 'Q-%s' % month rng = period_range('1989Q3', '1991Q3', freq=freq) stamps = rng.to_timestamp() diff --git a/pandas/tests/test_resample.py b/pandas/tests/test_resample.py index 61b2b689bffd6..58452cc70e67b 100644 --- a/pandas/tests/test_resample.py +++ b/pandas/tests/test_resample.py @@ -22,7 +22,7 @@ from pandas.errors import UnsupportedFunctionCall from pandas.core.groupby import DataError from pandas._libs.tslibs.resolution import DAYS -from pandas.tseries.frequencies import MONTHS +from pandas._libs.tslibs.frequencies import _MONTHS from pandas.tseries.frequencies import to_offset from pandas.core.indexes.datetimes import date_range from pandas.tseries.offsets import Minute, BDay @@ -2343,7 +2343,7 @@ def test_annual_upsample_M_e_b(self): self._check_annual_upsample_cases('M', 'end', 'bfill') def _check_annual_upsample_cases(self, targ, conv, meth, end='12/31/1991'): - for month in MONTHS: + for month in _MONTHS: ts = _simple_pts('1/1/1990', end, freq='A-%s' % month) result = getattr(ts.resample(targ, convention=conv), meth)() @@ -2410,7 +2410,7 @@ def test_annual_upsample(self): def test_quarterly_upsample(self): targets = ['D', 'B', 'M'] - for month in MONTHS: + for month in _MONTHS: ts = _simple_pts('1/1/1990', '12/31/1995', freq='Q-%s' % month) for targ, conv in product(targets, ['start', 'end']): @@ -2547,7 +2547,7 @@ def test_resample_to_timestamps(self): assert_series_equal(result, expected) def test_resample_to_quarterly(self): - for month in MONTHS: + for month in _MONTHS: ts = _simple_pts('1990', '1992', freq='A-%s' % month) quar_ts = ts.resample('Q-%s' % month).ffill() From 002930308ae72623334c4665bbde738087bc1528 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 26 Nov 2017 10:00:00 -0800 Subject: [PATCH 15/32] Deprivatize, add typing per reviewer request --- pandas/_libs/period.pyx | 4 ++-- pandas/_libs/tslibs/frequencies.pxd | 6 +++--- pandas/_libs/tslibs/frequencies.pyx | 6 +++--- pandas/tests/tseries/offsets/test_offsets.py | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pandas/_libs/period.pyx b/pandas/_libs/period.pyx index 15e139ea65013..c9a6d2ba29576 100644 --- a/pandas/_libs/period.pyx +++ b/pandas/_libs/period.pyx @@ -32,7 +32,7 @@ from tslibs.timezones cimport ( from tslibs.timedeltas cimport delta_to_nanoseconds from tslibs.frequencies cimport (get_freq_code, get_base_alias, - get_to_timestamp_base, _get_freq_str, + get_to_timestamp_base, get_freq_str, _get_rule_month) from tslibs.frequencies import _MONTH_NUMBERS from tslibs.parsing import parse_time_string, NAT_SENTINEL @@ -573,7 +573,7 @@ cdef class _Period(object): if isinstance(freq, (int, tuple)): code, stride = get_freq_code(freq) - freq = _get_freq_str(code, stride) + freq = get_freq_str(code, stride) freq = frequencies.to_offset(freq) diff --git a/pandas/_libs/tslibs/frequencies.pxd b/pandas/_libs/tslibs/frequencies.pxd index b5bb3cd9dd596..e841a06c39e39 100644 --- a/pandas/_libs/tslibs/frequencies.pxd +++ b/pandas/_libs/tslibs/frequencies.pxd @@ -4,7 +4,7 @@ cpdef object _get_rule_month(object source, object default=*) cpdef get_freq_code(freqstr) -cpdef get_freq(freq) +cpdef object get_freq(object freq) cpdef get_base_alias(freqstr) -cpdef get_to_timestamp_base(base) -cpdef _get_freq_str(base, mult=*) +cpdef int get_to_timestamp_base(int base) +cpdef str get_freq_str(base, mult=*) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 9debd72ad5510..779fdbb1829a4 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -210,7 +210,7 @@ cpdef _period_str_to_code(freqstr): raise ValueError(_INVALID_FREQ_ERROR.format(freqstr)) -cpdef _get_freq_str(base, mult=1): +cpdef str get_freq_str(base, mult=1): code = _reverse_period_code_map.get(base) if mult == 1: return code @@ -239,7 +239,7 @@ class FreqGroup(object): FR_NS = 12000 -cpdef get_to_timestamp_base(base): +cpdef int get_to_timestamp_base(int base): """ Return frequency code group used for base of to_timestamp against frequency code. @@ -267,7 +267,7 @@ cpdef get_to_timestamp_base(base): return base -cpdef get_freq(freq): +cpdef object get_freq(object freq): """ Return frequency code of given frequency str. If input is not string, return input as it is. diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 5f92e7642b59e..85b8941238154 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -12,7 +12,7 @@ from pandas.core.series import Series -from pandas._libs.tslibs.frequencies import (get_freq_code, _get_freq_str, +from pandas._libs.tslibs.frequencies import (get_freq_code, get_freq_str, _INVALID_FREQ_ERROR) from pandas.tseries.frequencies import (_offset_map, get_offset, get_standard_freq) @@ -2895,7 +2895,7 @@ def test_rule_code(self): code, stride = get_freq_code('3' + k) assert isinstance(code, int) assert stride == 3 - assert k == _get_freq_str(code) + assert k == get_freq_str(code) def test_dateoffset_misc(): From 957345babb3fc675da35b0955d90f5039d7dedab Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 26 Nov 2017 10:21:58 -0800 Subject: [PATCH 16/32] check set inclusion instead of list inclusion --- pandas/_libs/tslibs/frequencies.pyx | 52 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 779fdbb1829a4..88595540810f6 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -315,31 +315,31 @@ def is_subperiod(source, target): if _is_quarterly(source): return _quarter_months_conform(_get_rule_month(source), _get_rule_month(target)) - return source in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] + return source in {'D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'} elif _is_quarterly(target): - return source in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] + return source in {'D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'} elif _is_monthly(target): - return source in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + return source in {'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'} elif _is_weekly(target): - return source in [target, 'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + return source in {target, 'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'} elif target == 'B': - return source in ['B', 'H', 'T', 'S', 'L', 'U', 'N'] + return source in {'B', 'H', 'T', 'S', 'L', 'U', 'N'} elif target == 'C': - return source in ['C', 'H', 'T', 'S', 'L', 'U', 'N'] + return source in {'C', 'H', 'T', 'S', 'L', 'U', 'N'} elif target == 'D': - return source in ['D', 'H', 'T', 'S', 'L', 'U', 'N'] + return source in {'D', 'H', 'T', 'S', 'L', 'U', 'N'} elif target == 'H': - return source in ['H', 'T', 'S', 'L', 'U', 'N'] + return source in {'H', 'T', 'S', 'L', 'U', 'N'} elif target == 'T': - return source in ['T', 'S', 'L', 'U', 'N'] + return source in {'T', 'S', 'L', 'U', 'N'} elif target == 'S': - return source in ['S', 'L', 'U', 'N'] + return source in {'S', 'L', 'U', 'N'} elif target == 'L': - return source in ['L', 'U', 'N'] + return source in {'L', 'U', 'N'} elif target == 'U': - return source in ['U', 'N'] + return source in {'U', 'N'} elif target == 'N': - return source in ['N'] + return source in {'N'} def is_superperiod(source, target): @@ -371,31 +371,31 @@ def is_superperiod(source, target): smonth = _get_rule_month(source) tmonth = _get_rule_month(target) return _quarter_months_conform(smonth, tmonth) - return target in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] + return target in {'D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'} elif _is_quarterly(source): - return target in ['D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'] + return target in {'D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'} elif _is_monthly(source): - return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + return target in {'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'} elif _is_weekly(source): - return target in [source, 'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + return target in {source, 'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'} elif source == 'B': - return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + return target in {'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'} elif source == 'C': - return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + return target in {'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'} elif source == 'D': - return target in ['D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'] + return target in {'D', 'C', 'B', 'H', 'T', 'S', 'L', 'U', 'N'} elif source == 'H': - return target in ['H', 'T', 'S', 'L', 'U', 'N'] + return target in {'H', 'T', 'S', 'L', 'U', 'N'} elif source == 'T': - return target in ['T', 'S', 'L', 'U', 'N'] + return target in {'T', 'S', 'L', 'U', 'N'} elif source == 'S': - return target in ['S', 'L', 'U', 'N'] + return target in {'S', 'L', 'U', 'N'} elif source == 'L': - return target in ['L', 'U', 'N'] + return target in {'L', 'U', 'N'} elif source == 'U': - return target in ['U', 'N'] + return target in {'U', 'N'} elif source == 'N': - return target in ['N'] + return target in {'N'} def _maybe_coerce_freq(code): From 4c200b29154ec635b3befde6dbc8fdd168035d4c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 29 Nov 2017 10:06:32 -0800 Subject: [PATCH 17/32] fix broken import --- pandas/tests/tseries/offsets/test_fiscal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/tests/tseries/offsets/test_fiscal.py b/pandas/tests/tseries/offsets/test_fiscal.py index 45f12c6931fd9..a3d16e8561c46 100644 --- a/pandas/tests/tseries/offsets/test_fiscal.py +++ b/pandas/tests/tseries/offsets/test_fiscal.py @@ -9,7 +9,8 @@ import pandas.util.testing as tm -from pandas.tseries.frequencies import get_offset, _INVALID_FREQ_ERROR +from pandas.tseries.frequencies import get_offset +from pandas._libs.tslibs.frequencies import _INVALID_FREQ_ERROR from pandas.tseries.offsets import FY5253Quarter, FY5253 from pandas._libs.tslibs.offsets import WeekDay From aa0f988f06ae58b6ae803d0334eee739f3eea63e Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 29 Nov 2017 18:52:22 -0800 Subject: [PATCH 18/32] dummy commit to force CI --- pandas/_libs/tslibs/parsing.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/pandas/_libs/tslibs/parsing.pyx b/pandas/_libs/tslibs/parsing.pyx index 90882eefd9f67..8ce1d9cdf2158 100644 --- a/pandas/_libs/tslibs/parsing.pyx +++ b/pandas/_libs/tslibs/parsing.pyx @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- # cython: profile=False -# cython: linetrace=False -# distutils: define_macros=CYTHON_TRACE=0 -# distutils: define_macros=CYTHON_TRACE_NOGIL=0 """ Parsing functions for datetime and datetime-like strings. """ From 2138285bbadecf13e1328e2b00b0b4f3d556445e Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 10 Dec 2017 18:10:09 -0800 Subject: [PATCH 19/32] centralize+deprivatize day/month constants in ccalendar --- pandas/_libs/tslibs/ccalendar.pyx | 11 ++++++++ pandas/_libs/tslibs/frequencies.pyx | 10 +++---- pandas/_libs/tslibs/offsets.pyx | 25 +++-------------- pandas/_libs/tslibs/parsing.pyx | 8 +++--- pandas/_libs/tslibs/period.pyx | 4 +-- pandas/_libs/tslibs/resolution.pyx | 18 +++++-------- pandas/tests/indexes/period/test_tools.py | 5 ++-- pandas/tests/scalar/test_period.py | 2 +- pandas/tests/test_resample.py | 10 +++---- pandas/tseries/offsets.py | 33 ++++++++++++----------- 10 files changed, 56 insertions(+), 70 deletions(-) diff --git a/pandas/_libs/tslibs/ccalendar.pyx b/pandas/_libs/tslibs/ccalendar.pyx index a68ecbd2e8629..f45fe11c73c04 100644 --- a/pandas/_libs/tslibs/ccalendar.pyx +++ b/pandas/_libs/tslibs/ccalendar.pyx @@ -33,6 +33,17 @@ cdef int32_t* _month_offset = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366] +# Canonical location for other modules to find name constants +MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', + 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] +MONTH_NUMBERS = {name: num for num, name in enumerate(MONTHS)} +MONTH_ALIASES = {(num + 1): name for num, name in enumerate(MONTHS)} +MONTH_TO_CAL_NUM = {name: num + 1 for num, name in enumerate(MONTHS)} + +DAYS = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] +int_to_weekday = {num: name for num, name in enumerate(DAYS)} +weekday_to_int = {_int_to_weekday[key]: key for key in _int_to_weekday} + # ---------------------------------------------------------------------- diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 88595540810f6..bad6335d693f2 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -11,13 +11,11 @@ np.import_array() from util cimport is_integer_object, is_string_object +from ccalendar import MONTH_NUMBERS + # ---------------------------------------------------------------------- # Constants -_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', - 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] -_MONTH_NUMBERS = {k: i for i, k in enumerate(_MONTHS)} - # hack to handle WOM-1MON opattern = re.compile( r'([+\-]?\d*|[+\-]?\d*\.\d*)\s*([A-Za-z]+([\-][\dA-Za-z\-]+)?)' @@ -419,8 +417,8 @@ def _maybe_coerce_freq(code): def _quarter_months_conform(source, target): - snum = _MONTH_NUMBERS[source] - tnum = _MONTH_NUMBERS[target] + snum = MONTH_NUMBERS[source] + tnum = MONTH_NUMBERS[target] return snum % 3 == tnum % 3 diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 29e14103dfe20..e6266d520541a 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -17,6 +17,7 @@ np.import_array() from util cimport is_string_object, is_integer_object +from ccalendar import MONTHS, MONTH_ALIASES from conversion cimport tz_convert_single, pydt_to_i8 from frequencies cimport get_freq_code from nattype cimport NPY_NAT @@ -27,13 +28,7 @@ from np_datetime cimport (pandas_datetimestruct, # --------------------------------------------------------------------- # Constants -# Duplicated in tslib -_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', - 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] -_int_to_month = {(k + 1): v for k, v in enumerate(_MONTHS)} -_month_to_int = {v: k for k, v in _int_to_month.items()} - - +# TODO: remove this; it is only used in tests class WeekDay(object): MON = 0 TUE = 1 @@ -44,18 +39,6 @@ class WeekDay(object): SUN = 6 -_int_to_weekday = { - WeekDay.MON: 'MON', - WeekDay.TUE: 'TUE', - WeekDay.WED: 'WED', - WeekDay.THU: 'THU', - WeekDay.FRI: 'FRI', - WeekDay.SAT: 'SAT', - WeekDay.SUN: 'SUN'} - -_weekday_to_int = {_int_to_weekday[key]: key for key in _int_to_weekday} - - _offset_to_period_map = { 'WEEKDAY': 'D', 'EOM': 'M', @@ -88,12 +71,12 @@ _offset_to_period_map = { need_suffix = ['QS', 'BQ', 'BQS', 'YS', 'AS', 'BY', 'BA', 'BYS', 'BAS'] for __prefix in need_suffix: - for _m in _MONTHS: + for _m in MONTHS: key = '%s-%s' % (__prefix, _m) _offset_to_period_map[key] = _offset_to_period_map[__prefix] for __prefix in ['A', 'Q']: - for _m in _MONTHS: + for _m in MONTHS: _alias = '%s-%s' % (__prefix, _m) _offset_to_period_map[_alias] = _alias diff --git a/pandas/_libs/tslibs/parsing.pyx b/pandas/_libs/tslibs/parsing.pyx index 8ce1d9cdf2158..1bfaa7b804a99 100644 --- a/pandas/_libs/tslibs/parsing.pyx +++ b/pandas/_libs/tslibs/parsing.pyx @@ -41,6 +41,8 @@ from dateutil.relativedelta import relativedelta from dateutil.parser import DEFAULTPARSER from dateutil.parser import parse as du_parse +from ccalendar import MONTH_NUMBERS + # ---------------------------------------------------------------------- # Constants @@ -53,10 +55,6 @@ _nat_strings = set(['NaT', 'nat', 'NAT', 'nan', 'NaN', 'NAN']) _DEFAULT_DATETIME = datetime(1, 1, 1).replace(hour=0, minute=0, second=0, microsecond=0) -_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', - 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] -_MONTH_NUMBERS = {k: i for i, k in enumerate(_MONTHS)} -_MONTH_ALIASES = {(k + 1): v for k, v in enumerate(_MONTHS)} cdef object _TIMEPAT = re.compile(r'^([01]?[0-9]|2[0-3]):([0-5][0-9])') @@ -267,7 +265,7 @@ cdef inline object _parse_dateabbr_string(object date_string, object default, if freq is not None: # hack attack, #1228 try: - mnum = _MONTH_NUMBERS[_get_rule_month(freq)] + 1 + mnum = MONTH_NUMBERS[_get_rule_month(freq)] + 1 except (KeyError, ValueError): msg = ('Unable to retrieve month information from given ' 'freq: {0}').format(freq) diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index f87c0f35e72cc..8a08012283d63 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -33,10 +33,10 @@ from timestamps import Timestamp from timezones cimport is_utc, is_tzlocal, get_utcoffset, get_dst_info from timedeltas cimport delta_to_nanoseconds +from ccalendar import MONTH_NUMBERS from frequencies cimport (get_freq_code, get_base_alias, get_to_timestamp_base, get_freq_str, _get_rule_month) -from frequencies import _MONTH_NUMBERS from parsing import parse_time_string, NAT_SENTINEL from resolution import resolution, Resolution from nattype import nat_strings, NaT, iNaT @@ -1151,7 +1151,7 @@ def _quarter_to_myear(year, quarter, freq): if quarter <= 0 or quarter > 4: raise ValueError('Quarter must be 1 <= q <= 4') - mnum = _MONTH_NUMBERS[_get_rule_month(freq)] + 1 + mnum = MONTH_NUMBERS[_get_rule_month(freq)] + 1 month = (mnum + (quarter - 1) * 3) % 12 + 1 if month > mnum: year -= 1 diff --git a/pandas/_libs/tslibs/resolution.pyx b/pandas/_libs/tslibs/resolution.pyx index d2b518c74a1e3..87175af17e495 100644 --- a/pandas/_libs/tslibs/resolution.pyx +++ b/pandas/_libs/tslibs/resolution.pyx @@ -24,6 +24,7 @@ from timezones cimport (is_utc, is_tzlocal, maybe_get_tz, get_dst_info, get_utcoffset) from fields import build_field_sarray from conversion import tz_convert +from ccalendar import MONTHS, DAYS, MONTH_ALIASES, int_to_weekday from pandas._libs.properties import cache_readonly from pandas._libs.tslib import Timestamp @@ -50,13 +51,6 @@ _ONE_MINUTE = 60 * _ONE_SECOND _ONE_HOUR = 60 * _ONE_MINUTE _ONE_DAY = 24 * _ONE_HOUR -DAYS = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] -_weekday_rule_aliases = {k: v for k, v in enumerate(DAYS)} - -_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', - 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] -_MONTH_ALIASES = {(k + 1): v for k, v in enumerate(_MONTHS)} - # ---------------------------------------------------------------------- cpdef resolution(ndarray[int64_t] stamps, tz=None): @@ -537,7 +531,7 @@ class _FrequencyInferer(object): annual_rule = self._get_annual_rule() if annual_rule: nyears = self.ydiffs[0] - month = _MONTH_ALIASES[self.rep_stamp.month] + month = MONTH_ALIASES[self.rep_stamp.month] alias = '{prefix}-{month}'.format(prefix=annual_rule, month=month) return _maybe_add_count(alias, nyears) @@ -545,7 +539,7 @@ class _FrequencyInferer(object): if quarterly_rule: nquarters = self.mdiffs[0] / 3 mod_dict = {0: 12, 2: 11, 1: 10} - month = _MONTH_ALIASES[mod_dict[self.rep_stamp.month % 3]] + month = MONTH_ALIASES[mod_dict[self.rep_stamp.month % 3]] alias = '{prefix}-{month}'.format(prefix=quarterly_rule, month=month) return _maybe_add_count(alias, nquarters) @@ -558,7 +552,7 @@ class _FrequencyInferer(object): days = self.deltas[0] / _ONE_DAY if days % 7 == 0: # Weekly - day = _weekday_rule_aliases[self.rep_stamp.weekday()] + day = int_to_weekday[self.rep_stamp.weekday()] return _maybe_add_count('W-{day}'.format(day=day), days / 7) else: return _maybe_add_count('D', days) @@ -630,7 +624,7 @@ class _FrequencyInferer(object): # get which week week = week_of_months[0] + 1 - wd = _weekday_rule_aliases[weekdays[0]] + wd = int_to_weekday[weekdays[0]] return 'WOM-{week}{weekday}'.format(week=week, weekday=wd) @@ -642,7 +636,7 @@ class _TimedeltaFrequencyInferer(_FrequencyInferer): days = self.deltas[0] / _ONE_DAY if days % 7 == 0: # Weekly - wd = _weekday_rule_aliases[self.rep_stamp.weekday()] + wd = int_to_weekday[self.rep_stamp.weekday()] alias = 'W-{weekday}'.format(weekday=wd) return _maybe_add_count(alias, days / 7) else: diff --git a/pandas/tests/indexes/period/test_tools.py b/pandas/tests/indexes/period/test_tools.py index 007178a9413da..2523441a21285 100644 --- a/pandas/tests/indexes/period/test_tools.py +++ b/pandas/tests/indexes/period/test_tools.py @@ -6,8 +6,9 @@ import pandas.core.indexes.period as period from pandas.compat import lrange -from pandas._libs.tslibs.frequencies import get_freq, _MONTHS +from pandas._libs.tslibs.frequencies import get_freq from pandas._libs.tslibs.period import period_ordinal, period_asfreq +from pandas._libs.ccalendar import MONTHS from pandas import (PeriodIndex, Period, DatetimeIndex, Timestamp, Series, date_range, to_datetime, period_range) @@ -337,7 +338,7 @@ def test_period_astype_to_timestamp(self): def test_to_period_quarterly(self): # make sure we can make the round trip - for month in _MONTHS: + for month in MONTHS: freq = 'Q-%s' % month rng = period_range('1989Q3', '1991Q3', freq=freq) stamps = rng.to_timestamp() diff --git a/pandas/tests/scalar/test_period.py b/pandas/tests/scalar/test_period.py index 0a54f5e70c87e..706e1a4a76b22 100644 --- a/pandas/tests/scalar/test_period.py +++ b/pandas/tests/scalar/test_period.py @@ -14,7 +14,7 @@ from pandas._libs.tslibs import period as libperiod from pandas._libs.tslibs.parsing import DateParseError from pandas import Period, Timestamp, offsets -from pandas._libs.tslibs.resolution import DAYS, _MONTHS as MONTHS +from pandas._libs.tslibs.ccalendar import DAYS, MONTHS class TestPeriodProperties(object): diff --git a/pandas/tests/test_resample.py b/pandas/tests/test_resample.py index f2d06a7fe2628..669e71eb0fead 100644 --- a/pandas/tests/test_resample.py +++ b/pandas/tests/test_resample.py @@ -22,8 +22,8 @@ from pandas.core.base import SpecificationError, AbstractMethodError from pandas.errors import UnsupportedFunctionCall from pandas.core.groupby import DataError -from pandas._libs.tslibs.resolution import DAYS -from pandas._libs.tslibs.frequencies import _MONTHS + +from pandas._libs.tslibs.ccalendar import DAYS, MONTHS from pandas.tseries.frequencies import to_offset from pandas.core.indexes.datetimes import date_range from pandas.tseries.offsets import Minute, BDay @@ -2334,7 +2334,7 @@ def test_annual_upsample_M_e_b(self): self._check_annual_upsample_cases('M', 'end', 'bfill') def _check_annual_upsample_cases(self, targ, conv, meth, end='12/31/1991'): - for month in _MONTHS: + for month in MONTHS: ts = _simple_pts('1/1/1990', end, freq='A-%s' % month) result = getattr(ts.resample(targ, convention=conv), meth)() @@ -2401,7 +2401,7 @@ def test_annual_upsample(self): def test_quarterly_upsample(self): targets = ['D', 'B', 'M'] - for month in _MONTHS: + for month in MONTHS: ts = _simple_pts('1/1/1990', '12/31/1995', freq='Q-%s' % month) for targ, conv in product(targets, ['start', 'end']): @@ -2538,7 +2538,7 @@ def test_resample_to_timestamps(self): assert_series_equal(result, expected) def test_resample_to_quarterly(self): - for month in _MONTHS: + for month in MONTHS: ts = _simple_pts('1990', '1992', freq='A-%s' % month) quar_ts = ts.resample('Q-%s' % month).ffill() diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index dd5f01a36a43e..8b12b2f3ad2ce 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -16,13 +16,13 @@ from pandas._libs import tslib, Timestamp, OutOfBoundsDatetime, Timedelta from pandas.util._decorators import cache_readonly +from pandas._libs.tslibs import ccalendar from pandas._libs.tslibs.timedeltas import delta_to_nanoseconds import pandas._libs.tslibs.offsets as liboffsets from pandas._libs.tslibs.offsets import ( ApplyTypeError, as_datetime, _is_normalized, _get_calendar, _to_dt64, _validate_business_time, - _int_to_weekday, _weekday_to_int, _determine_offset, apply_index_wraps, roll_yearday, @@ -933,7 +933,7 @@ def name(self): if self.isAnchored: return self.rule_code else: - month = liboffsets._int_to_month[self.n] + month = ccalendar.MONTH_ALIASES[self.n] return "{code}-{month}".format(code=self.rule_code, month=month) @@ -1348,7 +1348,8 @@ def onOffset(self, dt): def rule_code(self): suffix = '' if self.weekday is not None: - suffix = '-{weekday}'.format(weekday=_int_to_weekday[self.weekday]) + weekday = ccalendar.int_to_weekday[self.weekday] + suffix = '-{weekday}'.format(weekday=weekday) return self._prefix + suffix @classmethod @@ -1356,7 +1357,7 @@ def _from_name(cls, suffix=None): if not suffix: weekday = None else: - weekday = _weekday_to_int[suffix] + weekday = ccalendar.weekday_to_int[suffix] return cls(weekday=weekday) @@ -1430,7 +1431,7 @@ def onOffset(self, dt): @property def rule_code(self): - weekday = _int_to_weekday.get(self.weekday, '') + weekday = ccalendar.int_to_weekday.get(self.weekday, '') return '{prefix}-{week}{weekday}'.format(prefix=self._prefix, week=self.week + 1, weekday=weekday) @@ -1443,7 +1444,7 @@ def _from_name(cls, suffix=None): # TODO: handle n here... # only one digit weeks (1 --> week 0, 2 --> week 1, etc.) week = int(suffix[0]) - 1 - weekday = _weekday_to_int[suffix[1:]] + weekday = ccalendar.weekday_to_int[suffix[1:]] return cls(week=week, weekday=weekday) @@ -1509,7 +1510,7 @@ def onOffset(self, dt): @property def rule_code(self): - weekday = _int_to_weekday.get(self.weekday, '') + weekday = ccalendar.int_to_weekday.get(self.weekday, '') return '{prefix}-{weekday}'.format(prefix=self._prefix, weekday=weekday) @@ -1519,7 +1520,7 @@ def _from_name(cls, suffix=None): raise ValueError("Prefix {prefix!r} requires a suffix." .format(prefix=cls._prefix)) # TODO: handle n here... - weekday = _weekday_to_int[suffix] + weekday = ccalendar.weekday_to_int[suffix] return cls(weekday=weekday) # --------------------------------------------------------------------- @@ -1550,7 +1551,7 @@ def isAnchored(self): def _from_name(cls, suffix=None): kwargs = {} if suffix: - kwargs['startingMonth'] = liboffsets._month_to_int[suffix] + kwargs['startingMonth'] = ccalendar.MONTH_TO_CAL_NUM[suffix] else: if cls._from_name_startingMonth is not None: kwargs['startingMonth'] = cls._from_name_startingMonth @@ -1558,7 +1559,7 @@ def _from_name(cls, suffix=None): @property def rule_code(self): - month = liboffsets._int_to_month[self.startingMonth] + month = ccalendar.MONTH_ALIASES[self.startingMonth] return '{prefix}-{month}'.format(prefix=self._prefix, month=month) @apply_wraps @@ -1681,12 +1682,12 @@ def __init__(self, n=1, normalize=False, month=None): def _from_name(cls, suffix=None): kwargs = {} if suffix: - kwargs['month'] = liboffsets._month_to_int[suffix] + kwargs['month'] = ccalendar.MONTH_TO_CAL_NUM[suffix] return cls(**kwargs) @property def rule_code(self): - month = liboffsets._int_to_month[self.month] + month = ccalendar.MONTH_ALIASES[self.month] return '{prefix}-{month}'.format(prefix=self._prefix, month=month) @@ -1906,8 +1907,8 @@ def _get_suffix_prefix(self): def get_rule_code_suffix(self): prefix = self._get_suffix_prefix() - month = liboffsets._int_to_month[self.startingMonth] - weekday = _int_to_weekday[self.weekday] + month = ccalendar.MONTH_ALIASES[self.startingMonth] + weekday = ccalendar.int_to_weekday[self.weekday] return '{prefix}-{month}-{weekday}'.format(prefix=prefix, month=month, weekday=weekday) @@ -1921,8 +1922,8 @@ def _parse_suffix(cls, varion_code, startingMonth_code, weekday_code): raise ValueError("Unable to parse varion_code: " "{code}".format(code=varion_code)) - startingMonth = liboffsets._month_to_int[startingMonth_code] - weekday = _weekday_to_int[weekday_code] + startingMonth = ccalendar.MONTH_TO_CAL_NUM[startingMonth_code] + weekday = ccalendar.weekday_to_int[weekday_code] return {"weekday": weekday, "startingMonth": startingMonth, From cb73bd6bb0a72545499bf0169577d6369c80e622 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 10 Dec 2017 19:12:40 -0800 Subject: [PATCH 20/32] fixup typo --- pandas/_libs/tslibs/ccalendar.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/ccalendar.pyx b/pandas/_libs/tslibs/ccalendar.pyx index f45fe11c73c04..d7edae865911a 100644 --- a/pandas/_libs/tslibs/ccalendar.pyx +++ b/pandas/_libs/tslibs/ccalendar.pyx @@ -42,7 +42,7 @@ MONTH_TO_CAL_NUM = {name: num + 1 for num, name in enumerate(MONTHS)} DAYS = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] int_to_weekday = {num: name for num, name in enumerate(DAYS)} -weekday_to_int = {_int_to_weekday[key]: key for key in _int_to_weekday} +weekday_to_int = {int_to_weekday[key]: key for key in int_to_weekday} # ---------------------------------------------------------------------- From db3717bd63d3264e3b0b6568b37a24db0e188ee7 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 10 Dec 2017 22:00:15 -0800 Subject: [PATCH 21/32] add typing --- pandas/_libs/tslibs/frequencies.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index bad6335d693f2..d46f187d9f843 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -416,28 +416,28 @@ def _maybe_coerce_freq(code): return code.upper() -def _quarter_months_conform(source, target): +cdef bint _quarter_months_conform(str source, str target): snum = MONTH_NUMBERS[source] tnum = MONTH_NUMBERS[target] return snum % 3 == tnum % 3 -def _is_annual(rule): +cdef bint _is_annual(str rule): rule = rule.upper() return rule == 'A' or rule.startswith('A-') -def _is_quarterly(rule): +cdef bint _is_quarterly(str rule): rule = rule.upper() return rule == 'Q' or rule.startswith('Q-') or rule.startswith('BQ') -def _is_monthly(rule): +cdef bint _is_monthly(str rule): rule = rule.upper() return rule == 'M' or rule == 'BM' -def _is_weekly(rule): +cdef bint _is_weekly(str rule): rule = rule.upper() return rule == 'W' or rule.startswith('W-') From cbefe3e3948c3edc633e398db549d6ab85fcafc5 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 10 Dec 2017 22:02:09 -0800 Subject: [PATCH 22/32] remove unused imports --- pandas/_libs/tslibs/resolution.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/resolution.pyx b/pandas/_libs/tslibs/resolution.pyx index 87175af17e495..c7c53b0afdf4a 100644 --- a/pandas/_libs/tslibs/resolution.pyx +++ b/pandas/_libs/tslibs/resolution.pyx @@ -24,7 +24,7 @@ from timezones cimport (is_utc, is_tzlocal, maybe_get_tz, get_dst_info, get_utcoffset) from fields import build_field_sarray from conversion import tz_convert -from ccalendar import MONTHS, DAYS, MONTH_ALIASES, int_to_weekday +from ccalendar import MONTH_ALIASES, int_to_weekday from pandas._libs.properties import cache_readonly from pandas._libs.tslib import Timestamp From d1c67bca7e9dc9110459dcce99c517081327a466 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 11 Dec 2017 08:26:27 -0800 Subject: [PATCH 23/32] fixup typo broken import --- pandas/tests/indexes/period/test_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexes/period/test_tools.py b/pandas/tests/indexes/period/test_tools.py index 2523441a21285..0e72cadb5d494 100644 --- a/pandas/tests/indexes/period/test_tools.py +++ b/pandas/tests/indexes/period/test_tools.py @@ -8,7 +8,7 @@ from pandas._libs.tslibs.frequencies import get_freq from pandas._libs.tslibs.period import period_ordinal, period_asfreq -from pandas._libs.ccalendar import MONTHS +from pandas._libs.tslibs.ccalendar import MONTHS from pandas import (PeriodIndex, Period, DatetimeIndex, Timestamp, Series, date_range, to_datetime, period_range) From f60fe4df99f50287ee046ed38e1349d34c31baca Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 11 Dec 2017 08:26:51 -0800 Subject: [PATCH 24/32] de-privatize get_rule_month --- pandas/_libs/tslibs/frequencies.pxd | 2 +- pandas/_libs/tslibs/frequencies.pyx | 16 ++++++------- pandas/_libs/tslibs/period.pyx | 4 ++-- pandas/tests/tseries/test_frequencies.py | 30 ++++++++++++------------ 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pxd b/pandas/_libs/tslibs/frequencies.pxd index e841a06c39e39..4c0ade74285fc 100644 --- a/pandas/_libs/tslibs/frequencies.pxd +++ b/pandas/_libs/tslibs/frequencies.pxd @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # cython: profile=False -cpdef object _get_rule_month(object source, object default=*) +cpdef object get_rule_month(object source, object default=*) cpdef get_freq_code(freqstr) cpdef object get_freq(object freq) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index d46f187d9f843..deb15d182d005 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -311,8 +311,8 @@ def is_subperiod(source, target): if _is_annual(target): if _is_quarterly(source): - return _quarter_months_conform(_get_rule_month(source), - _get_rule_month(target)) + return _quarter_months_conform(get_rule_month(source), + get_rule_month(target)) return source in {'D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'} elif _is_quarterly(target): return source in {'D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'} @@ -363,11 +363,11 @@ def is_superperiod(source, target): if _is_annual(source): if _is_annual(target): - return _get_rule_month(source) == _get_rule_month(target) + return get_rule_month(source) == get_rule_month(target) if _is_quarterly(target): - smonth = _get_rule_month(source) - tmonth = _get_rule_month(target) + smonth = get_rule_month(source) + tmonth = get_rule_month(target) return _quarter_months_conform(smonth, tmonth) return target in {'D', 'C', 'B', 'M', 'H', 'T', 'S', 'L', 'U', 'N'} elif _is_quarterly(source): @@ -444,16 +444,16 @@ cdef bint _is_weekly(str rule): # ---------------------------------------------------------------------- -cpdef object _get_rule_month(object source, object default='DEC'): +cpdef object get_rule_month(object source, object default='DEC'): """ Return starting month of given freq, default is December. Example ------- - >>> _get_rule_month('D') + >>> get_rule_month('D') 'DEC' - >>> _get_rule_month('A-JAN') + >>> get_rule_month('A-JAN') 'JAN' """ if hasattr(source, 'freqstr'): diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 8a08012283d63..46365035a0b9a 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -36,7 +36,7 @@ from timedeltas cimport delta_to_nanoseconds from ccalendar import MONTH_NUMBERS from frequencies cimport (get_freq_code, get_base_alias, get_to_timestamp_base, get_freq_str, - _get_rule_month) + get_rule_month) from parsing import parse_time_string, NAT_SENTINEL from resolution import resolution, Resolution from nattype import nat_strings, NaT, iNaT @@ -1151,7 +1151,7 @@ def _quarter_to_myear(year, quarter, freq): if quarter <= 0 or quarter > 4: raise ValueError('Quarter must be 1 <= q <= 4') - mnum = MONTH_NUMBERS[_get_rule_month(freq)] + 1 + mnum = MONTH_NUMBERS[get_rule_month(freq)] + 1 month = (mnum + (quarter - 1) * 3) % 12 + 1 if month > mnum: year -= 1 diff --git a/pandas/tests/tseries/test_frequencies.py b/pandas/tests/tseries/test_frequencies.py index 7afec1c11b74d..7bdc1b4b0f164 100644 --- a/pandas/tests/tseries/test_frequencies.py +++ b/pandas/tests/tseries/test_frequencies.py @@ -7,7 +7,7 @@ from pandas import (Index, DatetimeIndex, Timestamp, Series, date_range, period_range) -from pandas._libs.tslibs.frequencies import (_period_code_map, _get_rule_month, +from pandas._libs.tslibs.frequencies import (_period_code_map, get_rule_month, _period_str_to_code, _INVALID_FREQ_ERROR) @@ -289,38 +289,38 @@ def test_rule_aliases(): def test_get_rule_month(): - result = _get_rule_month('W') + result = get_rule_month('W') assert (result == 'DEC') - result = _get_rule_month(offsets.Week()) + result = get_rule_month(offsets.Week()) assert (result == 'DEC') - result = _get_rule_month('D') + result = get_rule_month('D') assert (result == 'DEC') - result = _get_rule_month(offsets.Day()) + result = get_rule_month(offsets.Day()) assert (result == 'DEC') - result = _get_rule_month('Q') + result = get_rule_month('Q') assert (result == 'DEC') - result = _get_rule_month(offsets.QuarterEnd(startingMonth=12)) + result = get_rule_month(offsets.QuarterEnd(startingMonth=12)) print(result == 'DEC') - result = _get_rule_month('Q-JAN') + result = get_rule_month('Q-JAN') assert (result == 'JAN') - result = _get_rule_month(offsets.QuarterEnd(startingMonth=1)) + result = get_rule_month(offsets.QuarterEnd(startingMonth=1)) assert (result == 'JAN') - result = _get_rule_month('A-DEC') + result = get_rule_month('A-DEC') assert (result == 'DEC') - result = _get_rule_month('Y-DEC') + result = get_rule_month('Y-DEC') assert (result == 'DEC') - result = _get_rule_month(offsets.YearEnd()) + result = get_rule_month(offsets.YearEnd()) assert (result == 'DEC') - result = _get_rule_month('A-MAY') + result = get_rule_month('A-MAY') assert (result == 'MAY') - result = _get_rule_month('Y-MAY') + result = get_rule_month('Y-MAY') assert (result == 'MAY') - result = _get_rule_month(offsets.YearEnd(month=5)) + result = get_rule_month(offsets.YearEnd(month=5)) assert (result == 'MAY') From f250550ee6a8fa4ce99ad6be0f33c58b4185d619 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 11 Dec 2017 15:07:18 -0800 Subject: [PATCH 25/32] remove unused import --- pandas/_libs/tslibs/offsets.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index e6266d520541a..0c88bc4e42adf 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -17,7 +17,7 @@ np.import_array() from util cimport is_string_object, is_integer_object -from ccalendar import MONTHS, MONTH_ALIASES +from ccalendar import MONTHS from conversion cimport tz_convert_single, pydt_to_i8 from frequencies cimport get_freq_code from nattype cimport NPY_NAT From 23a3588a5a703dab180942b7e3c30bfd649d320d Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 20 Dec 2017 11:21:11 -0800 Subject: [PATCH 26/32] add types and docstrings --- pandas/_libs/tslibs/frequencies.pxd | 2 +- pandas/_libs/tslibs/frequencies.pyx | 79 +++++++++++++++++++++-------- 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pxd b/pandas/_libs/tslibs/frequencies.pxd index 4c0ade74285fc..98d600c540ace 100644 --- a/pandas/_libs/tslibs/frequencies.pxd +++ b/pandas/_libs/tslibs/frequencies.pxd @@ -5,6 +5,6 @@ cpdef object get_rule_month(object source, object default=*) cpdef get_freq_code(freqstr) cpdef object get_freq(object freq) -cpdef get_base_alias(freqstr) +cpdef str get_base_alias(freqstr) cpdef int get_to_timestamp_base(int base) cpdef str get_freq_str(base, mult=*) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index deb15d182d005..f8030ca20d975 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -26,6 +26,21 @@ _INVALID_FREQ_ERROR = "Invalid frequency: {0}" # --------------------------------------------------------------------- # Period codes +class FreqGroup(object): + FR_ANN = 1000 + FR_QTR = 2000 + FR_MTH = 3000 + FR_WK = 4000 + FR_BUS = 5000 + FR_DAY = 6000 + FR_HR = 7000 + FR_MIN = 8000 + FR_SEC = 9000 + FR_MS = 10000 + FR_US = 11000 + FR_NS = 12000 + + # period frequency constants corresponding to scikits timeseries # originals _period_code_map = { @@ -209,39 +224,54 @@ cpdef _period_str_to_code(freqstr): cpdef str get_freq_str(base, mult=1): + """ + Return the summary string associated with this offset code, possibly + adjusted by a multiplier. + + Parameters + ---------- + base : int (member of FreqGroup) + + Returns + ------- + freq_str : str + + Examples + -------- + >>> get_freq_str(1000) + 'A-DEC' + + >>> get_freq_str(2000, 2) + '2Q-DEC' + + >>> get_freq_str("foo") + """ code = _reverse_period_code_map.get(base) if mult == 1: return code return str(mult) + code -cpdef get_base_alias(freqstr): +cpdef str get_base_alias(freqstr): """ Returns the base frequency alias, e.g., '5D' -> 'D' """ return _base_and_stride(freqstr)[0] -class FreqGroup(object): - FR_ANN = 1000 - FR_QTR = 2000 - FR_MTH = 3000 - FR_WK = 4000 - FR_BUS = 5000 - FR_DAY = 6000 - FR_HR = 7000 - FR_MIN = 8000 - FR_SEC = 9000 - FR_MS = 10000 - FR_US = 11000 - FR_NS = 12000 - - cpdef int get_to_timestamp_base(int base): """ Return frequency code group used for base of to_timestamp against frequency code. + Parameters + ---------- + base : int (member of FreqGroup) + + Returns + ------- + base : int + Example ------- # Return day freq code against longer freq than day @@ -287,7 +317,7 @@ cpdef object get_freq(object freq): # ---------------------------------------------------------------------- # Frequency comparison -def is_subperiod(source, target): +cpdef bool is_subperiod(source, target): """ Returns True if downsampling is possible between source and target frequencies @@ -340,7 +370,7 @@ def is_subperiod(source, target): return source in {'N'} -def is_superperiod(source, target): +cpdef bool is_superperiod(source, target): """ Returns True if upsampling is possible between source and target frequencies @@ -396,7 +426,7 @@ def is_superperiod(source, target): return target in {'N'} -def _maybe_coerce_freq(code): +cdef str _maybe_coerce_freq(str code): """ we might need to coerce a code to a rule_code and uppercase it @@ -407,7 +437,7 @@ def _maybe_coerce_freq(code): Returns ------- - string code + code : string """ assert code is not None if getattr(code, '_typ', None) == 'dateoffset': @@ -448,6 +478,15 @@ cpdef object get_rule_month(object source, object default='DEC'): """ Return starting month of given freq, default is December. + Parameters + ---------- + source : object + default : object (default "DEC") + + Returns + ------- + rule_month: object (usually string) + Example ------- >>> get_rule_month('D') From a7f063cf474eb5ec3e846ef9c3d0637dc2cf8c73 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 20 Dec 2017 11:40:54 -0800 Subject: [PATCH 27/32] typo fixup bool-->bint --- pandas/_libs/tslibs/frequencies.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index f8030ca20d975..f7258abaf09c9 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -317,7 +317,7 @@ cpdef object get_freq(object freq): # ---------------------------------------------------------------------- # Frequency comparison -cpdef bool is_subperiod(source, target): +cpdef bint is_subperiod(source, target): """ Returns True if downsampling is possible between source and target frequencies @@ -370,7 +370,7 @@ cpdef bool is_subperiod(source, target): return source in {'N'} -cpdef bool is_superperiod(source, target): +cpdef bint is_superperiod(source, target): """ Returns True if upsampling is possible between source and target frequencies From 3ee6e921bd5907363da1abea9ea97c026418ec2c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 20 Dec 2017 17:26:54 -0800 Subject: [PATCH 28/32] fix typing --- pandas/_libs/tslibs/frequencies.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index f7258abaf09c9..2bdfcacfbb8b4 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -324,9 +324,9 @@ cpdef bint is_subperiod(source, target): Parameters ---------- - source : string + source : string or DateOffset Frequency converting from - target : string + target : string or DateOffset Frequency converting to Returns @@ -426,13 +426,13 @@ cpdef bint is_superperiod(source, target): return target in {'N'} -cdef str _maybe_coerce_freq(str code): +cdef str _maybe_coerce_freq(code): """ we might need to coerce a code to a rule_code and uppercase it Parameters ---------- - source : string + source : string or DateOffset Frequency converting from Returns From 64ef864bf750f57d7a3e73983e4ce64d21e8419c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 20 Dec 2017 19:32:42 -0800 Subject: [PATCH 29/32] whitespace fixup --- pandas/_libs/tslibs/frequencies.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 2bdfcacfbb8b4..097403997c1ce 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -26,6 +26,7 @@ _INVALID_FREQ_ERROR = "Invalid frequency: {0}" # --------------------------------------------------------------------- # Period codes + class FreqGroup(object): FR_ANN = 1000 FR_QTR = 2000 From 6d62e04e4c995508beac791ee320a6d0e1c21fc8 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sat, 23 Dec 2017 18:19:08 -0800 Subject: [PATCH 30/32] implement test_libfrequencies per request --- pandas/tests/tseries/test_frequencies.py | 116 +------------------- pandas/tests/tseries/test_libfrequencies.py | 116 ++++++++++++++++++++ 2 files changed, 118 insertions(+), 114 deletions(-) create mode 100644 pandas/tests/tseries/test_libfrequencies.py diff --git a/pandas/tests/tseries/test_frequencies.py b/pandas/tests/tseries/test_frequencies.py index 7bdc1b4b0f164..d4a232cf9ae0b 100644 --- a/pandas/tests/tseries/test_frequencies.py +++ b/pandas/tests/tseries/test_frequencies.py @@ -7,10 +7,9 @@ from pandas import (Index, DatetimeIndex, Timestamp, Series, date_range, period_range) -from pandas._libs.tslibs.frequencies import (_period_code_map, get_rule_month, - _period_str_to_code, +from pandas._libs.tslibs.frequencies import (_period_code_map, _INVALID_FREQ_ERROR) - +from pandas._libs.tslibs.ccalendar import MONTHS from pandas._libs.tslibs import resolution import pandas.tseries.frequencies as frequencies from pandas.core.tools.datetimes import to_datetime @@ -288,87 +287,6 @@ def test_rule_aliases(): assert rule == offsets.Micro(10) -def test_get_rule_month(): - result = get_rule_month('W') - assert (result == 'DEC') - result = get_rule_month(offsets.Week()) - assert (result == 'DEC') - - result = get_rule_month('D') - assert (result == 'DEC') - result = get_rule_month(offsets.Day()) - assert (result == 'DEC') - - result = get_rule_month('Q') - assert (result == 'DEC') - result = get_rule_month(offsets.QuarterEnd(startingMonth=12)) - print(result == 'DEC') - - result = get_rule_month('Q-JAN') - assert (result == 'JAN') - result = get_rule_month(offsets.QuarterEnd(startingMonth=1)) - assert (result == 'JAN') - - result = get_rule_month('A-DEC') - assert (result == 'DEC') - result = get_rule_month('Y-DEC') - assert (result == 'DEC') - result = get_rule_month(offsets.YearEnd()) - assert (result == 'DEC') - - result = get_rule_month('A-MAY') - assert (result == 'MAY') - result = get_rule_month('Y-MAY') - assert (result == 'MAY') - result = get_rule_month(offsets.YearEnd(month=5)) - assert (result == 'MAY') - - -def test_period_str_to_code(): - assert (_period_str_to_code('A') == 1000) - assert (_period_str_to_code('A-DEC') == 1000) - assert (_period_str_to_code('A-JAN') == 1001) - assert (_period_str_to_code('Y') == 1000) - assert (_period_str_to_code('Y-DEC') == 1000) - assert (_period_str_to_code('Y-JAN') == 1001) - - assert (_period_str_to_code('Q') == 2000) - assert (_period_str_to_code('Q-DEC') == 2000) - assert (_period_str_to_code('Q-FEB') == 2002) - - def _assert_depr(freq, expected, aliases): - assert isinstance(aliases, list) - assert (_period_str_to_code(freq) == expected) - - msg = _INVALID_FREQ_ERROR - for alias in aliases: - with tm.assert_raises_regex(ValueError, msg): - _period_str_to_code(alias) - - _assert_depr("M", 3000, ["MTH", "MONTH", "MONTHLY"]) - - assert (_period_str_to_code('W') == 4000) - assert (_period_str_to_code('W-SUN') == 4000) - assert (_period_str_to_code('W-FRI') == 4005) - - _assert_depr("B", 5000, ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY"]) - _assert_depr("D", 6000, ["DAY", "DLY", "DAILY"]) - _assert_depr("H", 7000, ["HR", "HOUR", "HRLY", "HOURLY"]) - - _assert_depr("T", 8000, ["minute", "MINUTE", "MINUTELY"]) - assert (_period_str_to_code('Min') == 8000) - - _assert_depr("S", 9000, ["sec", "SEC", "SECOND", "SECONDLY"]) - _assert_depr("L", 10000, ["MILLISECOND", "MILLISECONDLY"]) - assert (_period_str_to_code('ms') == 10000) - - _assert_depr("U", 11000, ["MICROSECOND", "MICROSECONDLY"]) - assert (_period_str_to_code('US') == 11000) - - _assert_depr("N", 12000, ["NANOSECOND", "NANOSECONDLY"]) - assert (_period_str_to_code('NS') == 12000) - - class TestFrequencyCode(object): def test_freq_code(self): @@ -890,33 +808,3 @@ def test_legacy_offset_warnings(self): with tm.assert_raises_regex(ValueError, msg): date_range('2011-01-01', periods=5, freq=freq) - - -MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', - 'NOV', 'DEC'] - - -def test_is_superperiod_subperiod(): - - # input validation - assert not (frequencies.is_superperiod(offsets.YearEnd(), None)) - assert not (frequencies.is_subperiod(offsets.MonthEnd(), None)) - assert not (frequencies.is_superperiod(None, offsets.YearEnd())) - assert not (frequencies.is_subperiod(None, offsets.MonthEnd())) - assert not (frequencies.is_superperiod(None, None)) - assert not (frequencies.is_subperiod(None, None)) - - assert (frequencies.is_superperiod(offsets.YearEnd(), offsets.MonthEnd())) - assert (frequencies.is_subperiod(offsets.MonthEnd(), offsets.YearEnd())) - - assert (frequencies.is_superperiod(offsets.Hour(), offsets.Minute())) - assert (frequencies.is_subperiod(offsets.Minute(), offsets.Hour())) - - assert (frequencies.is_superperiod(offsets.Second(), offsets.Milli())) - assert (frequencies.is_subperiod(offsets.Milli(), offsets.Second())) - - assert (frequencies.is_superperiod(offsets.Milli(), offsets.Micro())) - assert (frequencies.is_subperiod(offsets.Micro(), offsets.Milli())) - - assert (frequencies.is_superperiod(offsets.Micro(), offsets.Nano())) - assert (frequencies.is_subperiod(offsets.Nano(), offsets.Micro())) diff --git a/pandas/tests/tseries/test_libfrequencies.py b/pandas/tests/tseries/test_libfrequencies.py new file mode 100644 index 0000000000000..bac4e8db40f2b --- /dev/null +++ b/pandas/tests/tseries/test_libfrequencies.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- + +import pandas.util.testing as tm + +from pandas.tseries import offsets +from pandas._libs.tslibs.frequencies import (get_rule_month, + _period_str_to_code, + _INVALID_FREQ_ERROR, + is_superperiod, is_subperiod) + + +def test_get_rule_month(): + result = get_rule_month('W') + assert (result == 'DEC') + result = get_rule_month(offsets.Week()) + assert (result == 'DEC') + + result = get_rule_month('D') + assert (result == 'DEC') + result = get_rule_month(offsets.Day()) + assert (result == 'DEC') + + result = get_rule_month('Q') + assert (result == 'DEC') + result = get_rule_month(offsets.QuarterEnd(startingMonth=12)) + print(result == 'DEC') + + result = get_rule_month('Q-JAN') + assert (result == 'JAN') + result = get_rule_month(offsets.QuarterEnd(startingMonth=1)) + assert (result == 'JAN') + + result = get_rule_month('A-DEC') + assert (result == 'DEC') + result = get_rule_month('Y-DEC') + assert (result == 'DEC') + result = get_rule_month(offsets.YearEnd()) + assert (result == 'DEC') + + result = get_rule_month('A-MAY') + assert (result == 'MAY') + result = get_rule_month('Y-MAY') + assert (result == 'MAY') + result = get_rule_month(offsets.YearEnd(month=5)) + assert (result == 'MAY') + + +def test_period_str_to_code(): + assert (_period_str_to_code('A') == 1000) + assert (_period_str_to_code('A-DEC') == 1000) + assert (_period_str_to_code('A-JAN') == 1001) + assert (_period_str_to_code('Y') == 1000) + assert (_period_str_to_code('Y-DEC') == 1000) + assert (_period_str_to_code('Y-JAN') == 1001) + + assert (_period_str_to_code('Q') == 2000) + assert (_period_str_to_code('Q-DEC') == 2000) + assert (_period_str_to_code('Q-FEB') == 2002) + + def _assert_depr(freq, expected, aliases): + assert isinstance(aliases, list) + assert (_period_str_to_code(freq) == expected) + + msg = _INVALID_FREQ_ERROR + for alias in aliases: + with tm.assert_raises_regex(ValueError, msg): + _period_str_to_code(alias) + + _assert_depr("M", 3000, ["MTH", "MONTH", "MONTHLY"]) + + assert (_period_str_to_code('W') == 4000) + assert (_period_str_to_code('W-SUN') == 4000) + assert (_period_str_to_code('W-FRI') == 4005) + + _assert_depr("B", 5000, ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY"]) + _assert_depr("D", 6000, ["DAY", "DLY", "DAILY"]) + _assert_depr("H", 7000, ["HR", "HOUR", "HRLY", "HOURLY"]) + + _assert_depr("T", 8000, ["minute", "MINUTE", "MINUTELY"]) + assert (_period_str_to_code('Min') == 8000) + + _assert_depr("S", 9000, ["sec", "SEC", "SECOND", "SECONDLY"]) + _assert_depr("L", 10000, ["MILLISECOND", "MILLISECONDLY"]) + assert (_period_str_to_code('ms') == 10000) + + _assert_depr("U", 11000, ["MICROSECOND", "MICROSECONDLY"]) + assert (_period_str_to_code('US') == 11000) + + _assert_depr("N", 12000, ["NANOSECOND", "NANOSECONDLY"]) + assert (_period_str_to_code('NS') == 12000) + + +def test_is_superperiod_subperiod(): + + # input validation + assert not (is_superperiod(offsets.YearEnd(), None)) + assert not (is_subperiod(offsets.MonthEnd(), None)) + assert not (is_superperiod(None, offsets.YearEnd())) + assert not (is_subperiod(None, offsets.MonthEnd())) + assert not (is_superperiod(None, None)) + assert not (is_subperiod(None, None)) + + assert (is_superperiod(offsets.YearEnd(), offsets.MonthEnd())) + assert (is_subperiod(offsets.MonthEnd(), offsets.YearEnd())) + + assert (is_superperiod(offsets.Hour(), offsets.Minute())) + assert (is_subperiod(offsets.Minute(), offsets.Hour())) + + assert (is_superperiod(offsets.Second(), offsets.Milli())) + assert (is_subperiod(offsets.Milli(), offsets.Second())) + + assert (is_superperiod(offsets.Milli(), offsets.Micro())) + assert (is_subperiod(offsets.Micro(), offsets.Milli())) + + assert (is_superperiod(offsets.Micro(), offsets.Nano())) + assert (is_subperiod(offsets.Nano(), offsets.Micro())) From 1d71924615daad7361dd36b0886434ebd2a25387 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sat, 23 Dec 2017 18:21:55 -0800 Subject: [PATCH 31/32] extend docstring per request --- pandas/_libs/tslibs/frequencies.pyx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 097403997c1ce..78f6594363707 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -256,6 +256,14 @@ cpdef str get_freq_str(base, mult=1): cpdef str get_base_alias(freqstr): """ Returns the base frequency alias, e.g., '5D' -> 'D' + + Parameters + ---------- + freqstr : str + + Returns + ------- + base_alias : str """ return _base_and_stride(freqstr)[0] @@ -291,7 +299,7 @@ cpdef int get_to_timestamp_base(int base): """ if base < FreqGroup.FR_BUS: return FreqGroup.FR_DAY - if FreqGroup.FR_HR <= base <= FreqGroup.FR_SEC: + elif FreqGroup.FR_HR <= base <= FreqGroup.FR_SEC: return FreqGroup.FR_SEC return base From 8129b1c4efe278610571805cf77ea41325c028d6 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 4 Jan 2018 10:22:56 -0800 Subject: [PATCH 32/32] fix docstrings, rename _assert_depr --- pandas/_libs/tslibs/frequencies.pyx | 16 ++++----- pandas/tests/tseries/test_libfrequencies.py | 38 ++++++++++----------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 78f6594363707..cce3600371300 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -144,8 +144,8 @@ cpdef get_freq_code(freqstr): ------- return : tuple of base frequency code and stride (mult) - Example - ------- + Examples + -------- >>> get_freq_code('3D') (6000, 3) @@ -281,8 +281,8 @@ cpdef int get_to_timestamp_base(int base): ------- base : int - Example - ------- + Examples + -------- # Return day freq code against longer freq than day >>> get_to_timestamp_base(get_freq_code('D')[0]) 6000 @@ -309,8 +309,8 @@ cpdef object get_freq(object freq): Return frequency code of given frequency str. If input is not string, return input as it is. - Example - ------- + Examples + -------- >>> get_freq('A') 1000 @@ -496,8 +496,8 @@ cpdef object get_rule_month(object source, object default='DEC'): ------- rule_month: object (usually string) - Example - ------- + Examples + -------- >>> get_rule_month('D') 'DEC' diff --git a/pandas/tests/tseries/test_libfrequencies.py b/pandas/tests/tseries/test_libfrequencies.py index bac4e8db40f2b..601d542da3095 100644 --- a/pandas/tests/tseries/test_libfrequencies.py +++ b/pandas/tests/tseries/test_libfrequencies.py @@ -9,6 +9,15 @@ is_superperiod, is_subperiod) +def assert_aliases_deprecated(freq, expected, aliases): + assert isinstance(aliases, list) + assert (_period_str_to_code(freq) == expected) + + for alias in aliases: + with tm.assert_raises_regex(ValueError, _INVALID_FREQ_ERROR): + _period_str_to_code(alias) + + def test_get_rule_month(): result = get_rule_month('W') assert (result == 'DEC') @@ -23,7 +32,6 @@ def test_get_rule_month(): result = get_rule_month('Q') assert (result == 'DEC') result = get_rule_month(offsets.QuarterEnd(startingMonth=12)) - print(result == 'DEC') result = get_rule_month('Q-JAN') assert (result == 'JAN') @@ -57,36 +65,28 @@ def test_period_str_to_code(): assert (_period_str_to_code('Q-DEC') == 2000) assert (_period_str_to_code('Q-FEB') == 2002) - def _assert_depr(freq, expected, aliases): - assert isinstance(aliases, list) - assert (_period_str_to_code(freq) == expected) - - msg = _INVALID_FREQ_ERROR - for alias in aliases: - with tm.assert_raises_regex(ValueError, msg): - _period_str_to_code(alias) - - _assert_depr("M", 3000, ["MTH", "MONTH", "MONTHLY"]) + assert_aliases_deprecated("M", 3000, ["MTH", "MONTH", "MONTHLY"]) assert (_period_str_to_code('W') == 4000) assert (_period_str_to_code('W-SUN') == 4000) assert (_period_str_to_code('W-FRI') == 4005) - _assert_depr("B", 5000, ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY"]) - _assert_depr("D", 6000, ["DAY", "DLY", "DAILY"]) - _assert_depr("H", 7000, ["HR", "HOUR", "HRLY", "HOURLY"]) + assert_aliases_deprecated("B", 5000, ["BUS", "BUSINESS", + "BUSINESSLY", "WEEKDAY"]) + assert_aliases_deprecated("D", 6000, ["DAY", "DLY", "DAILY"]) + assert_aliases_deprecated("H", 7000, ["HR", "HOUR", "HRLY", "HOURLY"]) - _assert_depr("T", 8000, ["minute", "MINUTE", "MINUTELY"]) + assert_aliases_deprecated("T", 8000, ["minute", "MINUTE", "MINUTELY"]) assert (_period_str_to_code('Min') == 8000) - _assert_depr("S", 9000, ["sec", "SEC", "SECOND", "SECONDLY"]) - _assert_depr("L", 10000, ["MILLISECOND", "MILLISECONDLY"]) + assert_aliases_deprecated("S", 9000, ["sec", "SEC", "SECOND", "SECONDLY"]) + assert_aliases_deprecated("L", 10000, ["MILLISECOND", "MILLISECONDLY"]) assert (_period_str_to_code('ms') == 10000) - _assert_depr("U", 11000, ["MICROSECOND", "MICROSECONDLY"]) + assert_aliases_deprecated("U", 11000, ["MICROSECOND", "MICROSECONDLY"]) assert (_period_str_to_code('US') == 11000) - _assert_depr("N", 12000, ["NANOSECOND", "NANOSECONDLY"]) + assert_aliases_deprecated("N", 12000, ["NANOSECOND", "NANOSECONDLY"]) assert (_period_str_to_code('NS') == 12000)