From d85fc9e4254c05659cc8c41ba4067ad43f15e634 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 11 Mar 2022 19:34:44 -0800 Subject: [PATCH 1/3] REF: de-duplicate hard-coded unit code --- pandas/_libs/tslib.pyx | 2 +- pandas/_libs/tslibs/dtypes.pxd | 22 +- pandas/_libs/tslibs/dtypes.pyi | 19 +- pandas/_libs/tslibs/dtypes.pyx | 265 +++++++++--------- pandas/_libs/tslibs/period.pyx | 34 +-- pandas/_libs/tslibs/timedeltas.pyx | 27 +- pandas/_libs/tslibs/vectorized.pyx | 16 +- pandas/core/arrays/datetimelike.py | 2 +- pandas/core/arrays/timedeltas.py | 1 + pandas/core/indexes/datetimes.py | 3 +- pandas/core/indexes/period.py | 10 +- pandas/plotting/_matplotlib/converter.py | 32 +-- .../tseries/frequencies/test_freq_code.py | 4 +- 13 files changed, 209 insertions(+), 228 deletions(-) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index 5c852e85efdc0..063b95953bd91 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -243,7 +243,7 @@ def array_with_unit_to_datetime( ) return result, tz - m, p = precision_from_unit(unit) + m, _ = precision_from_unit(unit) if is_raise: # try a quick conversion to i8/f8 diff --git a/pandas/_libs/tslibs/dtypes.pxd b/pandas/_libs/tslibs/dtypes.pxd index ae25de38c5bfd..e3854507124b6 100644 --- a/pandas/_libs/tslibs/dtypes.pxd +++ b/pandas/_libs/tslibs/dtypes.pxd @@ -1,7 +1,13 @@ +from pandas._libs.tslibs.np_datetime cimport NPY_DATETIMEUNIT + + +cdef str npy_unit_to_abbrev(NPY_DATETIMEUNIT unit) +cdef NPY_DATETIMEUNIT freq_group_code_to_npy_unit(int freq) nogil + cdef dict attrname_to_abbrevs cdef enum c_FreqGroup: - # Mirrors FreqGroup in the .pxy file + # Mirrors FreqGroup in the .pyx file FR_ANN = 1000 FR_QTR = 2000 FR_MTH = 3000 @@ -17,6 +23,20 @@ cdef enum c_FreqGroup: FR_UND = -10000 # undefined +cdef enum c_Resolution: + # Mirros Resolution in the .pyx file + RESO_NS = 0 + RESO_US = 1 + RESO_MS = 2 + RESO_SEC = 3 + RESO_MIN = 4 + RESO_HR = 5 + RESO_DAY = 6 + RESO_MTH = 7 + RESO_QTR = 8 + RESO_YR = 9 + + cdef enum PeriodDtypeCode: # Annual freqs with various fiscal year ends. # eg, 2005 for A_FEB runs Mar 1, 2004 to Feb 28, 2005 diff --git a/pandas/_libs/tslibs/dtypes.pyi b/pandas/_libs/tslibs/dtypes.pyi index 8c642bc847a29..eab95050c64a8 100644 --- a/pandas/_libs/tslibs/dtypes.pyi +++ b/pandas/_libs/tslibs/dtypes.pyi @@ -2,6 +2,8 @@ from enum import Enum from pandas._libs.tslibs.offsets import BaseOffset +# These are not public API, but are exposed in the .pyi file because they +# are imported in tests. _attrname_to_abbrevs: dict[str, str] _period_code_map: dict[str, int] @@ -10,13 +12,12 @@ class PeriodDtypeBase: # actually __cinit__ def __new__(cls, code: int): ... - def freq_group_code(self) -> int: ... - def date_offset(self) -> BaseOffset: ... - @classmethod - def from_date_offset(cls, offset: BaseOffset) -> PeriodDtypeBase: ... + def _freq_group_code(self) -> int: ... + @property + def _resolution_obj(self) -> Resolution: ... @property - def resolution(self) -> Resolution: ... def _get_to_timestamp_base(self) -> int: ... + def _freqstr(self) -> str: ... class FreqGroup(Enum): FR_ANN: int @@ -33,7 +34,7 @@ class FreqGroup(Enum): FR_NS: int FR_UND: int @staticmethod - def get_freq_group(code: int) -> FreqGroup: ... + def from_period_dtype_code(code: int) -> FreqGroup: ... class Resolution(Enum): RESO_NS: int @@ -49,10 +50,10 @@ class Resolution(Enum): def __lt__(self, other: Resolution) -> bool: ... def __ge__(self, other: Resolution) -> bool: ... @property - def freq_group(self) -> FreqGroup: ... - @property def attrname(self) -> str: ... @classmethod def from_attrname(cls, attrname: str) -> Resolution: ... @classmethod - def get_reso_from_freq(cls, freq: str) -> Resolution: ... + def get_reso_from_freqstr(cls, freq: str) -> Resolution: ... + @property + def attr_abbrev(self) -> str: ... diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index b64a360510ab9..43f33b98b8eaa 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -2,6 +2,8 @@ # originals from enum import Enum +from pandas._libs.tslibs.np_datetime cimport NPY_DATETIMEUNIT + cdef class PeriodDtypeBase: """ @@ -23,32 +25,24 @@ cdef class PeriodDtypeBase: return self._dtype_code == other._dtype_code @property - def freq_group_code(self) -> int: + def _freq_group_code(self) -> int: # See also: libperiod.get_freq_group return (self._dtype_code // 1000) * 1000 @property - def resolution(self) -> "Resolution": - fgc = self.freq_group_code - return Resolution.from_freq_group(FreqGroup(fgc)) + def _resolution_obj(self) -> "Resolution": + fgc = self._freq_group_code + freq_group = FreqGroup(fgc) + abbrev = _reverse_period_code_map[freq_group.value].split("-")[0] + if abbrev == "B": + return Resolution.RESO_DAY + attrname = _abbrev_to_attrnames[abbrev] + return Resolution.from_attrname(attrname) @property - def date_offset(self): - """ - Corresponding DateOffset object. - - This mapping is mainly for backward-compatibility. - """ - from .offsets import to_offset - - freqstr = _reverse_period_code_map.get(self._dtype_code) - - return to_offset(freqstr) - - @classmethod - def from_date_offset(cls, offset): - code = offset._period_dtype_code - return cls(code) + def _freqstr(self) -> str: + # Will be passed to to_offset in Period._maybe_convert_freq + return _reverse_period_code_map.get(self._dtype_code) cpdef int _get_to_timestamp_base(self): """ @@ -73,52 +67,52 @@ cdef class PeriodDtypeBase: _period_code_map = { # Annual freqs with various fiscal year ends. # eg, 2005 for A-FEB runs Mar 1, 2004 to Feb 28, 2005 - "A-DEC": 1000, # Annual - December year end - "A-JAN": 1001, # Annual - January year end - "A-FEB": 1002, # Annual - February year end - "A-MAR": 1003, # Annual - March year end - "A-APR": 1004, # Annual - April year end - "A-MAY": 1005, # Annual - May year end - "A-JUN": 1006, # Annual - June year end - "A-JUL": 1007, # Annual - July year end - "A-AUG": 1008, # Annual - August year end - "A-SEP": 1009, # Annual - September year end - "A-OCT": 1010, # Annual - October year end - "A-NOV": 1011, # Annual - November year end + "A-DEC": PeriodDtypeCode.A_DEC, # Annual - December year end + "A-JAN": PeriodDtypeCode.A_JAN, # Annual - January year end + "A-FEB": PeriodDtypeCode.A_FEB, # Annual - February year end + "A-MAR": PeriodDtypeCode.A_MAR, # Annual - March year end + "A-APR": PeriodDtypeCode.A_APR, # Annual - April year end + "A-MAY": PeriodDtypeCode.A_MAY, # Annual - May year end + "A-JUN": PeriodDtypeCode.A_JUN, # Annual - June year end + "A-JUL": PeriodDtypeCode.A_JUL, # Annual - July year end + "A-AUG": PeriodDtypeCode.A_AUG, # Annual - August year end + "A-SEP": PeriodDtypeCode.A_SEP, # Annual - September year end + "A-OCT": PeriodDtypeCode.A_OCT, # Annual - October year end + "A-NOV": PeriodDtypeCode.A_NOV, # Annual - November year end # Quarterly frequencies with various fiscal year ends. # eg, Q42005 for Q-OCT runs Aug 1, 2005 to Oct 31, 2005 - "Q-DEC": 2000, # Quarterly - December year end - "Q-JAN": 2001, # Quarterly - January year end - "Q-FEB": 2002, # Quarterly - February year end - "Q-MAR": 2003, # Quarterly - March year end - "Q-APR": 2004, # Quarterly - April year end - "Q-MAY": 2005, # Quarterly - May year end - "Q-JUN": 2006, # Quarterly - June year end - "Q-JUL": 2007, # Quarterly - July year end - "Q-AUG": 2008, # Quarterly - August year end - "Q-SEP": 2009, # Quarterly - September year end - "Q-OCT": 2010, # Quarterly - October year end - "Q-NOV": 2011, # Quarterly - November year end - - "M": 3000, # Monthly - - "W-SUN": 4000, # Weekly - Sunday end of week - "W-MON": 4001, # Weekly - Monday end of week - "W-TUE": 4002, # Weekly - Tuesday end of week - "W-WED": 4003, # Weekly - Wednesday end of week - "W-THU": 4004, # Weekly - Thursday end of week - "W-FRI": 4005, # Weekly - Friday end of week - "W-SAT": 4006, # Weekly - Saturday end of week - - "B": 5000, # Business days - "D": 6000, # Daily - "H": 7000, # Hourly - "T": 8000, # Minutely - "S": 9000, # Secondly - "L": 10000, # Millisecondly - "U": 11000, # Microsecondly - "N": 12000, # Nanosecondly + "Q-DEC": PeriodDtypeCode.Q_DEC, # Quarterly - December year end + "Q-JAN": PeriodDtypeCode.Q_JAN, # Quarterly - January year end + "Q-FEB": PeriodDtypeCode.Q_FEB, # Quarterly - February year end + "Q-MAR": PeriodDtypeCode.Q_MAR, # Quarterly - March year end + "Q-APR": PeriodDtypeCode.Q_APR, # Quarterly - April year end + "Q-MAY": PeriodDtypeCode.Q_MAY, # Quarterly - May year end + "Q-JUN": PeriodDtypeCode.Q_JUN, # Quarterly - June year end + "Q-JUL": PeriodDtypeCode.Q_JUL, # Quarterly - July year end + "Q-AUG": PeriodDtypeCode.Q_AUG, # Quarterly - August year end + "Q-SEP": PeriodDtypeCode.Q_SEP, # Quarterly - September year end + "Q-OCT": PeriodDtypeCode.Q_OCT, # Quarterly - October year end + "Q-NOV": PeriodDtypeCode.Q_NOV, # Quarterly - November year end + + "M": PeriodDtypeCode.M, # Monthly + + "W-SUN": PeriodDtypeCode.W_SUN, # Weekly - Sunday end of week + "W-MON": PeriodDtypeCode.W_MON, # Weekly - Monday end of week + "W-TUE": PeriodDtypeCode.W_TUE, # Weekly - Tuesday end of week + "W-WED": PeriodDtypeCode.W_WED, # Weekly - Wednesday end of week + "W-THU": PeriodDtypeCode.W_THU, # Weekly - Thursday end of week + "W-FRI": PeriodDtypeCode.W_FRI, # Weekly - Friday end of week + "W-SAT": PeriodDtypeCode.W_SAT, # Weekly - Saturday end of week + + "B": PeriodDtypeCode.B, # Business days + "D": PeriodDtypeCode.D, # Daily + "H": PeriodDtypeCode.H, # Hourly + "T": PeriodDtypeCode.T, # Minutely + "S": PeriodDtypeCode.S, # Secondly + "L": PeriodDtypeCode.L, # Millisecondly + "U": PeriodDtypeCode.U, # Microsecondly + "N": PeriodDtypeCode.N, # Nanosecondly } _reverse_period_code_map = { @@ -131,7 +125,7 @@ _period_code_map.update({"Y" + key[1:]: _period_code_map[key] _period_code_map.update({ "Q": 2000, # Quarterly - December year end (default quarterly) - "A": 1000, # Annual + "A": PeriodDtypeCode.A, # Annual "W": 4000, # Weekly "C": 5000, # Custom Business Day }) @@ -159,41 +153,38 @@ cdef dict _abbrev_to_attrnames = {v: k for k, v in attrname_to_abbrevs.items()} class FreqGroup(Enum): # Mirrors c_FreqGroup in the .pxd file - 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 - FR_UND = -10000 # undefined + FR_ANN = c_FreqGroup.FR_ANN + FR_QTR = c_FreqGroup.FR_QTR + FR_MTH = c_FreqGroup.FR_MTH + FR_WK = c_FreqGroup.FR_WK + FR_BUS = c_FreqGroup.FR_BUS + FR_DAY = c_FreqGroup.FR_DAY + FR_HR = c_FreqGroup.FR_HR + FR_MIN = c_FreqGroup.FR_MIN + FR_SEC = c_FreqGroup.FR_SEC + FR_MS = c_FreqGroup.FR_MS + FR_US = c_FreqGroup.FR_US + FR_NS = c_FreqGroup.FR_NS + FR_UND = -c_FreqGroup.FR_UND # undefined @staticmethod - def get_freq_group(code: int) -> "FreqGroup": - # See also: PeriodDtypeBase.freq_group_code + def from_period_dtype_code(code: int) -> "FreqGroup": + # See also: PeriodDtypeBase._freq_group_code code = (code // 1000) * 1000 return FreqGroup(code) class Resolution(Enum): - - # Note: cython won't allow us to reference the cdef versions at the - # module level - RESO_NS = 0 - RESO_US = 1 - RESO_MS = 2 - RESO_SEC = 3 - RESO_MIN = 4 - RESO_HR = 5 - RESO_DAY = 6 - RESO_MTH = 7 - RESO_QTR = 8 - RESO_YR = 9 + RESO_NS = c_Resolution.RESO_NS + RESO_US = c_Resolution.RESO_US + RESO_MS = c_Resolution.RESO_MS + RESO_SEC = c_Resolution.RESO_SEC + RESO_MIN = c_Resolution.RESO_MIN + RESO_HR = c_Resolution.RESO_HR + RESO_DAY = c_Resolution.RESO_DAY + RESO_MTH = c_Resolution.RESO_MTH + RESO_QTR = c_Resolution.RESO_QTR + RESO_YR = c_Resolution.RESO_YR def __lt__(self, other): return self.value < other.value @@ -202,29 +193,9 @@ class Resolution(Enum): return self.value >= other.value @property - def freq_group(self) -> FreqGroup: - if self == Resolution.RESO_NS: - return FreqGroup.FR_NS - elif self == Resolution.RESO_US: - return FreqGroup.FR_US - elif self == Resolution.RESO_MS: - return FreqGroup.FR_MS - elif self == Resolution.RESO_SEC: - return FreqGroup.FR_SEC - elif self == Resolution.RESO_MIN: - return FreqGroup.FR_MIN - elif self == Resolution.RESO_HR: - return FreqGroup.FR_HR - elif self == Resolution.RESO_DAY: - return FreqGroup.FR_DAY - elif self == Resolution.RESO_MTH: - return FreqGroup.FR_MTH - elif self == Resolution.RESO_QTR: - return FreqGroup.FR_QTR - elif self == Resolution.RESO_YR: - return FreqGroup.FR_ANN - else: - raise ValueError(self) # pragma: no cover + def attr_abbrev(self) -> str: + # string that we can pass to to_offset + return _attrname_to_abbrevs[self.attrname] @property def attrname(self) -> str: @@ -254,7 +225,7 @@ class Resolution(Enum): return cls(_str_reso_map[attrname]) @classmethod - def get_reso_from_freq(cls, freq: str) -> "Resolution": + def get_reso_from_freqstr(cls, freq: str) -> "Resolution": """ Return resolution code against frequency str. @@ -262,10 +233,10 @@ class Resolution(Enum): Examples -------- - >>> Resolution.get_reso_from_freq('H') + >>> Resolution.get_reso_from_freqstr('H') - >>> Resolution.get_reso_from_freq('H') == Resolution.RESO_HR + >>> Resolution.get_reso_from_freqstr('H') == Resolution.RESO_HR True """ try: @@ -283,13 +254,57 @@ class Resolution(Enum): return cls.from_attrname(attr_name) - @classmethod - def from_freq_group(cls, freq_group: FreqGroup) -> "Resolution": - abbrev = _reverse_period_code_map[freq_group.value].split("-")[0] - if abbrev == "B": - return cls.RESO_DAY - attrname = _abbrev_to_attrnames[abbrev] - return cls.from_attrname(attrname) + +cdef str npy_unit_to_abbrev(NPY_DATETIMEUNIT unit): + if unit == NPY_DATETIMEUNIT.NPY_FR_ns or unit == NPY_DATETIMEUNIT.NPY_FR_GENERIC: + # generic -> default to nanoseconds + return "ns" + elif unit == NPY_DATETIMEUNIT.NPY_FR_us: + return "us" + elif unit == NPY_DATETIMEUNIT.NPY_FR_ms: + return "ms" + elif unit == NPY_DATETIMEUNIT.NPY_FR_s: + return "s" + elif unit == NPY_DATETIMEUNIT.NPY_FR_m: + return "m" + elif unit == NPY_DATETIMEUNIT.NPY_FR_h: + return "h" + elif unit == NPY_DATETIMEUNIT.NPY_FR_D: + return "D" + elif unit == NPY_DATETIMEUNIT.NPY_FR_W: + return "W" + elif unit == NPY_DATETIMEUNIT.NPY_FR_M: + return "M" + elif unit == NPY_DATETIMEUNIT.NPY_FR_Y: + return "Y" + else: + raise NotImplementedError(unit) + + +cdef NPY_DATETIMEUNIT freq_group_code_to_npy_unit(int freq) nogil: + """ + Convert the freq to the corresponding NPY_DATETIMEUNIT to pass + to npy_datetimestruct_to_datetime. + """ + if freq == FR_MTH: + return NPY_DATETIMEUNIT.NPY_FR_M + elif freq == FR_DAY: + return NPY_DATETIMEUNIT.NPY_FR_D + elif freq == FR_HR: + return NPY_DATETIMEUNIT.NPY_FR_h + elif freq == FR_MIN: + return NPY_DATETIMEUNIT.NPY_FR_m + elif freq == FR_SEC: + return NPY_DATETIMEUNIT.NPY_FR_s + elif freq == FR_MS: + return NPY_DATETIMEUNIT.NPY_FR_ms + elif freq == FR_US: + return NPY_DATETIMEUNIT.NPY_FR_us + elif freq == FR_NS: + return NPY_DATETIMEUNIT.NPY_FR_ns + elif freq == FR_UND: + # Default to Day + return NPY_DATETIMEUNIT.NPY_FR_D cdef dict _reso_str_map = { diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 54cae834d7024..9c05c6867f71f 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -95,6 +95,7 @@ from pandas._libs.tslibs.dtypes cimport ( FR_WK, PeriodDtypeBase, attrname_to_abbrevs, + freq_group_code_to_npy_unit, ) from pandas._libs.tslibs.parsing cimport quarter_to_myear @@ -689,8 +690,7 @@ cdef char* c_strftime(npy_datetimestruct *dts, char *fmt): # Conversion between date_info and npy_datetimestruct cdef inline int get_freq_group(int freq) nogil: - # Note: this is equivalent to libfrequencies.get_freq_group, specialized - # to integer argument. + # See also FreqGroup.get_freq_group return (freq // 1000) * 1000 @@ -806,36 +806,10 @@ cdef int64_t get_period_ordinal(npy_datetimestruct *dts, int freq) nogil: unix_date = npy_datetimestruct_to_datetime(NPY_FR_D, dts) return DtoB(dts, 0, unix_date) - unit = get_unit(freq) + unit = freq_group_code_to_npy_unit(freq) return npy_datetimestruct_to_datetime(unit, dts) -cdef NPY_DATETIMEUNIT get_unit(int freq) nogil: - """ - Convert the freq to the corresponding NPY_DATETIMEUNIT to pass - to npy_datetimestruct_to_datetime. - """ - if freq == FR_MTH: - return NPY_DATETIMEUNIT.NPY_FR_M - elif freq == FR_DAY: - return NPY_DATETIMEUNIT.NPY_FR_D - elif freq == FR_HR: - return NPY_DATETIMEUNIT.NPY_FR_h - elif freq == FR_MIN: - return NPY_DATETIMEUNIT.NPY_FR_m - elif freq == FR_SEC: - return NPY_DATETIMEUNIT.NPY_FR_s - elif freq == FR_MS: - return NPY_DATETIMEUNIT.NPY_FR_ms - elif freq == FR_US: - return NPY_DATETIMEUNIT.NPY_FR_us - elif freq == FR_NS: - return NPY_DATETIMEUNIT.NPY_FR_ns - elif freq == FR_UND: - # Default to Day - return NPY_DATETIMEUNIT.NPY_FR_D - - cdef void get_date_info(int64_t ordinal, int freq, npy_datetimestruct *dts) nogil: cdef: int64_t unix_date, nanos @@ -1649,7 +1623,7 @@ cdef class _Period(PeriodMixin): if isinstance(freq, int): # We already have a dtype code dtype = PeriodDtypeBase(freq) - freq = dtype.date_offset + freq = dtype._freqstr freq = to_offset(freq) diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 8eaf86b3d193f..e88fb42adfc78 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -36,6 +36,7 @@ from pandas._libs.tslibs.conversion cimport ( cast_from_unit, precision_from_unit, ) +from pandas._libs.tslibs.dtypes cimport npy_unit_to_abbrev from pandas._libs.tslibs.nattype cimport ( NPY_NAT, c_NaT as NaT, @@ -192,32 +193,6 @@ cpdef int64_t delta_to_nanoseconds(delta) except? -1: raise TypeError(type(delta)) -cdef str npy_unit_to_abbrev(NPY_DATETIMEUNIT unit): - if unit == NPY_DATETIMEUNIT.NPY_FR_ns or unit == NPY_DATETIMEUNIT.NPY_FR_GENERIC: - # generic -> default to nanoseconds - return "ns" - elif unit == NPY_DATETIMEUNIT.NPY_FR_us: - return "us" - elif unit == NPY_DATETIMEUNIT.NPY_FR_ms: - return "ms" - elif unit == NPY_DATETIMEUNIT.NPY_FR_s: - return "s" - elif unit == NPY_DATETIMEUNIT.NPY_FR_m: - return "m" - elif unit == NPY_DATETIMEUNIT.NPY_FR_h: - return "h" - elif unit == NPY_DATETIMEUNIT.NPY_FR_D: - return "D" - elif unit == NPY_DATETIMEUNIT.NPY_FR_W: - return "W" - elif unit == NPY_DATETIMEUNIT.NPY_FR_M: - return "M" - elif unit == NPY_DATETIMEUNIT.NPY_FR_Y: - return "Y" - else: - raise NotImplementedError(unit) - - @cython.overflowcheck(True) cdef object ensure_td64ns(object ts): """ diff --git a/pandas/_libs/tslibs/vectorized.pyx b/pandas/_libs/tslibs/vectorized.pyx index bc254b6c5a5cf..f63cb4f58609a 100644 --- a/pandas/_libs/tslibs/vectorized.pyx +++ b/pandas/_libs/tslibs/vectorized.pyx @@ -191,16 +191,12 @@ def ints_to_pydatetime( # ------------------------------------------------------------------------- cdef: - int RESO_NS = 0 - int RESO_US = 1 - int RESO_MS = 2 - int RESO_SEC = 3 - int RESO_MIN = 4 - int RESO_HR = 5 - int RESO_DAY = 6 - int RESO_MTH = 7 - int RESO_QTR = 8 - int RESO_YR = 9 + int RESO_US = Resolution.RESO_US.value + int RESO_MS = Resolution.RESO_MS.value + int RESO_SEC = Resolution.RESO_SEC.value + int RESO_MIN = Resolution.RESO_MIN.value + int RESO_HR = Resolution.RESO_HR.value + int RESO_DAY = Resolution.RESO_DAY.value cdef inline int _reso_stamp(npy_datetimestruct *dts): diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 458960225f206..c57fab5fe40be 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -959,7 +959,7 @@ def _resolution_obj(self) -> Resolution | None: if freqstr is None: return None try: - return Resolution.get_reso_from_freq(freqstr) + return Resolution.get_reso_from_freqstr(freqstr) except KeyError: return None diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index afc20fcc54376..dc63cd92bbb2b 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -1023,6 +1023,7 @@ def sequence_to_td64ns( # cast the unit, multiply base/frac separately # to avoid precision issues from float -> int mask = np.isnan(data) + # The next few lines are effectively a vectorized 'cast_from_unit' m, p = precision_from_unit(unit or "ns") base = data.astype(np.int64) frac = data - base diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 4acdc7e6c7556..3954cb28c2aca 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -573,8 +573,7 @@ def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime): ------- lower, upper: pd.Timestamp """ - grp = reso.freq_group - per = Period(parsed, freq=grp.value) + per = Period(parsed, freq=reso.attr_abbrev) start, end = per.start_time, per.end_time # GH 24076 diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index a296ce130bbf5..3105bef6d506f 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -446,8 +446,9 @@ def get_loc(self, key, method=None, tolerance=None): # TODO: pass if method is not None, like DTI does? raise KeyError(key) from err - if reso == self.dtype.resolution: - # the reso < self.dtype.resolution case goes through _get_string_slice + if reso == self.dtype._resolution_obj: + # the reso < self.dtype._resolution_obj case goes + # through _get_string_slice key = Period(parsed, freq=self.freq) loc = self.get_loc(key, method=method, tolerance=tolerance) # Recursing instead of falling through matters for the exception @@ -495,14 +496,13 @@ def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default): return super()._maybe_cast_slice_bound(label, side, kind=kind) def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime): - grp = reso.freq_group - iv = Period(parsed, freq=grp.value) + iv = Period(parsed, freq=reso.attr_abbrev) return (iv.asfreq(self.freq, how="start"), iv.asfreq(self.freq, how="end")) def _can_partial_date_slice(self, reso: Resolution) -> bool: assert isinstance(reso, Resolution), (type(reso), reso) # e.g. test_getitem_setitem_periodindex - return reso > self.dtype.resolution + return reso > self.dtype._resolution_obj def period_range( diff --git a/pandas/plotting/_matplotlib/converter.py b/pandas/plotting/_matplotlib/converter.py index 6c40770b4fac5..e5fb6b1dafa98 100644 --- a/pandas/plotting/_matplotlib/converter.py +++ b/pandas/plotting/_matplotlib/converter.py @@ -533,34 +533,35 @@ def has_level_label(label_flags: np.ndarray, vmin: float) -> bool: def _daily_finder(vmin, vmax, freq: BaseOffset): # error: "BaseOffset" has no attribute "_period_dtype_code" dtype_code = freq._period_dtype_code # type: ignore[attr-defined] + freq_group = FreqGroup.from_period_dtype_code(dtype_code) periodsperday = -1 if dtype_code >= FreqGroup.FR_HR.value: - if dtype_code == FreqGroup.FR_NS.value: + if freq_group == FreqGroup.FR_NS: periodsperday = 24 * 60 * 60 * 1000000000 - elif dtype_code == FreqGroup.FR_US.value: + elif freq_group == FreqGroup.FR_US: periodsperday = 24 * 60 * 60 * 1000000 - elif dtype_code == FreqGroup.FR_MS.value: + elif freq_group == FreqGroup.FR_MS: periodsperday = 24 * 60 * 60 * 1000 - elif dtype_code == FreqGroup.FR_SEC.value: + elif freq_group == FreqGroup.FR_SEC: periodsperday = 24 * 60 * 60 - elif dtype_code == FreqGroup.FR_MIN.value: + elif freq_group == FreqGroup.FR_MIN: periodsperday = 24 * 60 - elif dtype_code == FreqGroup.FR_HR.value: + elif freq_group == FreqGroup.FR_HR: periodsperday = 24 else: # pragma: no cover raise ValueError(f"unexpected frequency: {dtype_code}") periodsperyear = 365 * periodsperday periodspermonth = 28 * periodsperday - elif dtype_code == FreqGroup.FR_BUS.value: + elif freq_group == FreqGroup.FR_BUS: periodsperyear = 261 periodspermonth = 19 - elif dtype_code == FreqGroup.FR_DAY.value: + elif freq_group == FreqGroup.FR_DAY: periodsperyear = 365 periodspermonth = 28 - elif FreqGroup.get_freq_group(dtype_code) == FreqGroup.FR_WK: + elif freq_group == FreqGroup.FR_WK: periodsperyear = 52 periodspermonth = 3 else: # pragma: no cover @@ -898,14 +899,13 @@ def _annual_finder(vmin, vmax, freq): def get_finder(freq: BaseOffset): # error: "BaseOffset" has no attribute "_period_dtype_code" dtype_code = freq._period_dtype_code # type: ignore[attr-defined] - fgroup = (dtype_code // 1000) * 1000 - fgroup = FreqGroup(fgroup) + fgroup = FreqGroup.from_period_dtype_code(dtype_code) if fgroup == FreqGroup.FR_ANN: return _annual_finder elif fgroup == FreqGroup.FR_QTR: return _quarterly_finder - elif dtype_code == FreqGroup.FR_MTH.value: + elif fgroup == FreqGroup.FR_MTH: return _monthly_finder elif (dtype_code >= FreqGroup.FR_BUS.value) or fgroup == FreqGroup.FR_WK: return _daily_finder @@ -919,7 +919,7 @@ class TimeSeries_DateLocator(Locator): Parameters ---------- - freq : {var} + freq : BaseOffset Valid frequency specifier. minor_locator : {False, True}, optional Whether the locator is for minor ticks (True) or not. @@ -933,7 +933,7 @@ class TimeSeries_DateLocator(Locator): def __init__( self, - freq, + freq: BaseOffset, minor_locator=False, dynamic_mode=True, base=1, @@ -1010,7 +1010,7 @@ class TimeSeries_DateFormatter(Formatter): Parameters ---------- - freq : {int, string} + freq : BaseOffset Valid frequency specifier. minor_locator : bool, default False Whether the current formatter should apply to minor ticks (True) or @@ -1021,7 +1021,7 @@ class TimeSeries_DateFormatter(Formatter): def __init__( self, - freq, + freq: BaseOffset, minor_locator: bool = False, dynamic_mode: bool = True, plot_obj=None, diff --git a/pandas/tests/tseries/frequencies/test_freq_code.py b/pandas/tests/tseries/frequencies/test_freq_code.py index 41257bb3f67fd..e961fdc295c96 100644 --- a/pandas/tests/tseries/frequencies/test_freq_code.py +++ b/pandas/tests/tseries/frequencies/test_freq_code.py @@ -38,12 +38,12 @@ def test_get_to_timestamp_base(freqstr, exp_freqstr): ], ) def test_get_attrname_from_abbrev(freqstr, expected): - assert Resolution.get_reso_from_freq(freqstr).attrname == expected + assert Resolution.get_reso_from_freqstr(freqstr).attrname == expected @pytest.mark.parametrize("freq", ["D", "H", "T", "S", "L", "U", "N"]) def test_get_freq_roundtrip2(freq): - obj = Resolution.get_reso_from_freq(freq) + obj = Resolution.get_reso_from_freqstr(freq) result = _attrname_to_abbrevs[obj.attrname] assert freq == result From 95864e9091c7ba2ab073973aec52e9275825af83 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 11 Mar 2022 19:35:45 -0800 Subject: [PATCH 2/3] typo fixup --- pandas/_libs/tslibs/dtypes.pxd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/dtypes.pxd b/pandas/_libs/tslibs/dtypes.pxd index e3854507124b6..3686ee216b546 100644 --- a/pandas/_libs/tslibs/dtypes.pxd +++ b/pandas/_libs/tslibs/dtypes.pxd @@ -24,7 +24,7 @@ cdef enum c_FreqGroup: cdef enum c_Resolution: - # Mirros Resolution in the .pyx file + # Mirrors Resolution in the .pyx file RESO_NS = 0 RESO_US = 1 RESO_MS = 2 From 21f1b22d16663adc38efbb1b42286fb53a3f5ac9 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 12 Mar 2022 13:18:00 -0800 Subject: [PATCH 3/3] mypy fixup --- pandas/_libs/tslibs/dtypes.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/_libs/tslibs/dtypes.pyi b/pandas/_libs/tslibs/dtypes.pyi index eab95050c64a8..ab66677a8be3a 100644 --- a/pandas/_libs/tslibs/dtypes.pyi +++ b/pandas/_libs/tslibs/dtypes.pyi @@ -15,7 +15,6 @@ class PeriodDtypeBase: def _freq_group_code(self) -> int: ... @property def _resolution_obj(self) -> Resolution: ... - @property def _get_to_timestamp_base(self) -> int: ... def _freqstr(self) -> str: ...