Skip to content

REF: move get_freq_group to libfreqs, de-duplicate with Resolution.get_freq_group #34203

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pandas/_libs/tslibs/frequencies.pxd
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
cdef dict attrname_to_abbrevs

cpdef str get_rule_month(object source, str default=*)

cpdef get_freq_code(freqstr)
Expand Down
42 changes: 42 additions & 0 deletions pandas/_libs/tslibs/frequencies.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,50 @@ _lite_rule_alias = {

_dont_uppercase = {'MS', 'ms'}

# Map attribute-name resolutions to resolution abbreviations
_attrname_to_abbrevs = {
"year": "A",
"quarter": "Q",
"month": "M",
"day": "D",
"hour": "H",
"minute": "T",
"second": "S",
"millisecond": "L",
"microsecond": "U",
"nanosecond": "N",
}
cdef dict attrname_to_abbrevs = _attrname_to_abbrevs


# ----------------------------------------------------------------------

def get_freq_group(freq) -> int:
"""
Return frequency code group of given frequency str or offset.

Examples
--------
>>> get_freq_group('W-MON')
4000

>>> get_freq_group('W-FRI')
4000
"""
if is_offset_object(freq):
freq = freq.rule_code

if isinstance(freq, str):
freq = attrname_to_abbrevs.get(freq, freq)
base, mult = get_freq_code(freq)
freq = base
elif isinstance(freq, int):
pass
else:
raise ValueError('input must be str, offset or int')
return (freq // 1000) * 1000


cpdef get_freq_code(freqstr):
"""
Return freq str or tuple to freq code and stride (mult)
Expand Down
6 changes: 4 additions & 2 deletions pandas/_libs/tslibs/period.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ from pandas._libs.tslibs.ccalendar cimport (
)
from pandas._libs.tslibs.ccalendar cimport c_MONTH_NUMBERS
from pandas._libs.tslibs.frequencies cimport (
attrname_to_abbrevs,
get_base_alias,
get_freq_code,
get_freq_str,
get_rule_month,
get_to_timestamp_base,
)
from pandas._libs.tslibs.parsing import parse_time_string
from pandas._libs.tslibs.resolution import Resolution
from pandas._libs.tslibs.nattype cimport (
_nat_scalar_rules,
NPY_NAT,
Expand Down Expand Up @@ -708,6 +708,8 @@ 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.
return (freq // 1000) * 1000


Expand Down Expand Up @@ -2431,7 +2433,7 @@ class Period(_Period):

if freq is None:
try:
freq = Resolution.get_freq(reso)
freq = attrname_to_abbrevs[reso]
except KeyError:
raise ValueError(f"Invalid frequency or could not "
f"infer: {reso}")
Expand Down
72 changes: 6 additions & 66 deletions pandas/_libs/tslibs/resolution.pyx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import numpy as np
from numpy cimport ndarray, int64_t, int32_t

from pandas._libs.tslibs.util cimport get_nat, is_offset_object
from pandas._libs.tslibs.util cimport get_nat

from pandas._libs.tslibs.np_datetime cimport (
npy_datetimestruct, dt64_to_dtstruct)
from pandas._libs.tslibs.frequencies cimport get_freq_code
from pandas._libs.tslibs.frequencies cimport attrname_to_abbrevs
from pandas._libs.tslibs.timezones cimport (
is_utc, is_tzlocal, maybe_get_tz, get_dst_info)
from pandas._libs.tslibs.ccalendar cimport get_days_in_month
Expand All @@ -25,6 +25,7 @@ cdef:
int RESO_HR = 5
int RESO_DAY = 6


# ----------------------------------------------------------------------

cpdef resolution(const int64_t[:] stamps, tz=None):
Expand Down Expand Up @@ -106,31 +107,6 @@ cdef inline int _reso_stamp(npy_datetimestruct *dts):
return RESO_DAY


def get_freq_group(freq) -> int:
"""
Return frequency code group of given frequency str or offset.

Examples
--------
>>> get_freq_group('W-MON')
4000

>>> get_freq_group('W-FRI')
4000
"""
if is_offset_object(freq):
freq = freq.rule_code

if isinstance(freq, str):
base, mult = get_freq_code(freq)
freq = base
elif isinstance(freq, int):
pass
else:
raise ValueError('input must be str, offset or int')
return (freq // 1000) * 1000


class Resolution:

# Note: cython won't allow us to reference the cdef versions at the
Expand Down Expand Up @@ -163,7 +139,7 @@ class Resolution:
RESO_HR: 60,
RESO_DAY: 24}

_reso_str_bump_map = {
reso_str_bump_map = {
'D': 'H',
'H': 'T',
'T': 'S',
Expand All @@ -174,19 +150,7 @@ class Resolution:

_str_reso_map = {v: k for k, v in _reso_str_map.items()}

_reso_freq_map = {
'year': 'A',
'quarter': 'Q',
'month': 'M',
'day': 'D',
'hour': 'H',
'minute': 'T',
'second': 'S',
'millisecond': 'L',
'microsecond': 'U',
'nanosecond': 'N'}

_freq_reso_map = {v: k for k, v in _reso_freq_map.items()}
_freq_reso_map = {v: k for k, v in attrname_to_abbrevs.items()}

@classmethod
def get_str(cls, reso: int) -> str:
Expand Down Expand Up @@ -215,30 +179,6 @@ class Resolution:
"""
return cls._str_reso_map.get(resostr, cls.RESO_DAY)

@classmethod
def get_freq_group(cls, resostr: str) -> int:
"""
Return frequency str against resolution str.

Examples
--------
>>> f.Resolution.get_freq_group('day')
4000
"""
return get_freq_group(cls.get_freq(resostr))

@classmethod
def get_freq(cls, resostr: str) -> str:
"""
Return frequency str against resolution str.

Examples
--------
>>> f.Resolution.get_freq('day')
'D'
"""
return cls._reso_freq_map[resostr]

@classmethod
def get_str_from_freq(cls, freq: str) -> str:
"""
Expand Down Expand Up @@ -303,7 +243,7 @@ class Resolution:
)

next_value = cls._reso_mult_map[start_reso] * value
next_name = cls._reso_str_bump_map[freq]
next_name = cls.reso_str_bump_map[freq]
return cls.get_stride_from_decimal(next_value, next_name)


Expand Down
5 changes: 3 additions & 2 deletions pandas/core/arrays/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import numpy as np

from pandas._libs import NaT, NaTType, Period, Timestamp, algos, iNaT, lib
from pandas._libs.tslibs.resolution import Resolution
from pandas._libs.tslibs.timedeltas import delta_to_nanoseconds
from pandas._libs.tslibs.timestamps import (
RoundTo,
Expand Down Expand Up @@ -1094,14 +1095,14 @@ def inferred_freq(self):

@property # NB: override with cache_readonly in immutable subclasses
def _resolution(self):
return frequencies.Resolution.get_reso_from_freq(self.freqstr)
return Resolution.get_reso_from_freq(self.freqstr)

@property # NB: override with cache_readonly in immutable subclasses
def resolution(self) -> str:
"""
Returns day, hour, minute, second, millisecond or microsecond
"""
return frequencies.Resolution.get_str(self._resolution)
return Resolution.get_str(self._resolution)

@classmethod
def _validate_frequency(cls, index, freq, **kwargs):
Expand Down
1 change: 0 additions & 1 deletion pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
ABCCategorical,
ABCDataFrame,
ABCDatetimeIndex,
ABCIntervalIndex,
ABCMultiIndex,
ABCPandasArray,
ABCPeriodIndex,
Expand Down
13 changes: 7 additions & 6 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

import numpy as np

from pandas._libs import NaT, Period, Timestamp, index as libindex, lib, tslib as libts
from pandas._libs.tslibs import fields, parsing, timezones
from pandas._libs import NaT, Period, Timestamp, index as libindex, lib, tslib
from pandas._libs.tslibs import fields, parsing, resolution as libresolution, timezones
from pandas._libs.tslibs.frequencies import get_freq_group
from pandas._typing import DtypeObj, Label
from pandas.util._decorators import cache_readonly

Expand All @@ -28,7 +29,7 @@
from pandas.core.indexes.extension import inherit_names
import pandas.core.tools.datetimes as tools

from pandas.tseries.frequencies import Resolution, to_offset
from pandas.tseries.frequencies import to_offset
from pandas.tseries.offsets import prefix_mapping


Expand Down Expand Up @@ -323,7 +324,7 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:

def _mpl_repr(self):
# how to represent ourselves to matplotlib
return libts.ints_to_pydatetime(self.asi8, self.tz)
return tslib.ints_to_pydatetime(self.asi8, self.tz)

@property
def _formatter_func(self):
Expand Down Expand Up @@ -500,7 +501,7 @@ def _parsed_string_to_bounds(self, reso: str, parsed: datetime):
if reso not in valid_resos:
raise KeyError

grp = Resolution.get_freq_group(reso)
grp = get_freq_group(reso)
per = Period(parsed, freq=(grp, 1))
start, end = per.start_time, per.end_time

Expand All @@ -525,7 +526,7 @@ def _validate_partial_date_slice(self, reso: str):
if (
self.is_monotonic
and reso in ["day", "hour", "minute", "second"]
and self._resolution >= Resolution.get_reso(reso)
and self._resolution >= libresolution.Resolution.get_reso(reso)
):
# These resolution/monotonicity validations came from GH3931,
# GH3452 and GH2369.
Expand Down
13 changes: 7 additions & 6 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from pandas._libs import index as libindex
from pandas._libs.lib import no_default
from pandas._libs.tslibs import Period, resolution
from pandas._libs.tslibs import Period
from pandas._libs.tslibs.frequencies import get_freq_group
from pandas._libs.tslibs.parsing import parse_time_string
from pandas._typing import DtypeObj, Label
from pandas.util._decorators import Appender, cache_readonly, doc
Expand Down Expand Up @@ -501,8 +502,8 @@ def get_loc(self, key, method=None, tolerance=None):
# A string with invalid format
raise KeyError(f"Cannot interpret '{key}' as period") from err

grp = resolution.Resolution.get_freq_group(reso)
freqn = resolution.get_freq_group(self.freq)
grp = get_freq_group(reso)
freqn = get_freq_group(self.freq)

# _get_string_slice will handle cases where grp < freqn
assert grp >= freqn
Expand Down Expand Up @@ -573,13 +574,13 @@ def _parsed_string_to_bounds(self, reso: str, parsed: datetime):
if reso not in ["year", "month", "quarter", "day", "hour", "minute", "second"]:
raise KeyError(reso)

grp = resolution.Resolution.get_freq_group(reso)
grp = get_freq_group(reso)
iv = Period(parsed, freq=(grp, 1))
return (iv.asfreq(self.freq, how="start"), iv.asfreq(self.freq, how="end"))

def _validate_partial_date_slice(self, reso: str):
grp = resolution.Resolution.get_freq_group(reso)
freqn = resolution.get_freq_group(self.freq)
grp = get_freq_group(reso)
freqn = get_freq_group(self.freq)

if not grp < freqn:
# TODO: we used to also check for
Expand Down
7 changes: 3 additions & 4 deletions pandas/plotting/_matplotlib/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
import numpy as np

from pandas._libs import lib, tslibs
from pandas._libs.tslibs import resolution
from pandas._libs.tslibs.frequencies import FreqGroup, get_freq_code
from pandas._libs.tslibs.frequencies import FreqGroup, get_freq_code, get_freq_group

from pandas.core.dtypes.common import (
is_datetime64_ns_dtype,
Expand Down Expand Up @@ -550,7 +549,7 @@ def _daily_finder(vmin, vmax, freq):
elif freq == FreqGroup.FR_DAY:
periodsperyear = 365
periodspermonth = 28
elif resolution.get_freq_group(freq) == FreqGroup.FR_WK:
elif get_freq_group(freq) == FreqGroup.FR_WK:
periodsperyear = 52
periodspermonth = 3
else: # pragma: no cover
Expand Down Expand Up @@ -888,7 +887,7 @@ def _annual_finder(vmin, vmax, freq):
def get_finder(freq):
if isinstance(freq, str):
freq = get_freq_code(freq)[0]
fgroup = resolution.get_freq_group(freq)
fgroup = get_freq_group(freq)

if fgroup == FreqGroup.FR_ANN:
return _annual_finder
Expand Down
Loading