Skip to content

Commit 965bd59

Browse files
authored
REF: move get_freq_group to libfreqs, de-duplicate with Resolution.get_freq_group (#34203)
1 parent dede2c7 commit 965bd59

File tree

10 files changed

+92
-101
lines changed

10 files changed

+92
-101
lines changed

pandas/_libs/tslibs/frequencies.pxd

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
cdef dict attrname_to_abbrevs
2+
13
cpdef str get_rule_month(object source, str default=*)
24

35
cpdef get_freq_code(freqstr)

pandas/_libs/tslibs/frequencies.pyx

+42
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,50 @@ _lite_rule_alias = {
124124

125125
_dont_uppercase = {'MS', 'ms'}
126126

127+
# Map attribute-name resolutions to resolution abbreviations
128+
_attrname_to_abbrevs = {
129+
"year": "A",
130+
"quarter": "Q",
131+
"month": "M",
132+
"day": "D",
133+
"hour": "H",
134+
"minute": "T",
135+
"second": "S",
136+
"millisecond": "L",
137+
"microsecond": "U",
138+
"nanosecond": "N",
139+
}
140+
cdef dict attrname_to_abbrevs = _attrname_to_abbrevs
141+
142+
127143
# ----------------------------------------------------------------------
128144

145+
def get_freq_group(freq) -> int:
146+
"""
147+
Return frequency code group of given frequency str or offset.
148+
149+
Examples
150+
--------
151+
>>> get_freq_group('W-MON')
152+
4000
153+
154+
>>> get_freq_group('W-FRI')
155+
4000
156+
"""
157+
if is_offset_object(freq):
158+
freq = freq.rule_code
159+
160+
if isinstance(freq, str):
161+
freq = attrname_to_abbrevs.get(freq, freq)
162+
base, mult = get_freq_code(freq)
163+
freq = base
164+
elif isinstance(freq, int):
165+
pass
166+
else:
167+
raise ValueError('input must be str, offset or int')
168+
return (freq // 1000) * 1000
169+
170+
129171
cpdef get_freq_code(freqstr):
130172
"""
131173
Return freq str or tuple to freq code and stride (mult)

pandas/_libs/tslibs/period.pyx

+4-2
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ from pandas._libs.tslibs.ccalendar cimport (
5353
)
5454
from pandas._libs.tslibs.ccalendar cimport c_MONTH_NUMBERS
5555
from pandas._libs.tslibs.frequencies cimport (
56+
attrname_to_abbrevs,
5657
get_base_alias,
5758
get_freq_code,
5859
get_freq_str,
5960
get_rule_month,
6061
get_to_timestamp_base,
6162
)
6263
from pandas._libs.tslibs.parsing import parse_time_string
63-
from pandas._libs.tslibs.resolution import Resolution
6464
from pandas._libs.tslibs.nattype cimport (
6565
_nat_scalar_rules,
6666
NPY_NAT,
@@ -708,6 +708,8 @@ cdef char* c_strftime(npy_datetimestruct *dts, char *fmt):
708708
# Conversion between date_info and npy_datetimestruct
709709

710710
cdef inline int get_freq_group(int freq) nogil:
711+
# Note: this is equivalent to libfrequencies.get_freq_group, specialized
712+
# to integer argument.
711713
return (freq // 1000) * 1000
712714

713715

@@ -2431,7 +2433,7 @@ class Period(_Period):
24312433

24322434
if freq is None:
24332435
try:
2434-
freq = Resolution.get_freq(reso)
2436+
freq = attrname_to_abbrevs[reso]
24352437
except KeyError:
24362438
raise ValueError(f"Invalid frequency or could not "
24372439
f"infer: {reso}")

pandas/_libs/tslibs/resolution.pyx

+6-66
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import numpy as np
22
from numpy cimport ndarray, int64_t, int32_t
33

4-
from pandas._libs.tslibs.util cimport get_nat, is_offset_object
4+
from pandas._libs.tslibs.util cimport get_nat
55

66
from pandas._libs.tslibs.np_datetime cimport (
77
npy_datetimestruct, dt64_to_dtstruct)
8-
from pandas._libs.tslibs.frequencies cimport get_freq_code
8+
from pandas._libs.tslibs.frequencies cimport attrname_to_abbrevs
99
from pandas._libs.tslibs.timezones cimport (
1010
is_utc, is_tzlocal, maybe_get_tz, get_dst_info)
1111
from pandas._libs.tslibs.ccalendar cimport get_days_in_month
@@ -25,6 +25,7 @@ cdef:
2525
int RESO_HR = 5
2626
int RESO_DAY = 6
2727

28+
2829
# ----------------------------------------------------------------------
2930

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

108109

109-
def get_freq_group(freq) -> int:
110-
"""
111-
Return frequency code group of given frequency str or offset.
112-
113-
Examples
114-
--------
115-
>>> get_freq_group('W-MON')
116-
4000
117-
118-
>>> get_freq_group('W-FRI')
119-
4000
120-
"""
121-
if is_offset_object(freq):
122-
freq = freq.rule_code
123-
124-
if isinstance(freq, str):
125-
base, mult = get_freq_code(freq)
126-
freq = base
127-
elif isinstance(freq, int):
128-
pass
129-
else:
130-
raise ValueError('input must be str, offset or int')
131-
return (freq // 1000) * 1000
132-
133-
134110
class Resolution:
135111

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

166-
_reso_str_bump_map = {
142+
reso_str_bump_map = {
167143
'D': 'H',
168144
'H': 'T',
169145
'T': 'S',
@@ -174,19 +150,7 @@ class Resolution:
174150

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

177-
_reso_freq_map = {
178-
'year': 'A',
179-
'quarter': 'Q',
180-
'month': 'M',
181-
'day': 'D',
182-
'hour': 'H',
183-
'minute': 'T',
184-
'second': 'S',
185-
'millisecond': 'L',
186-
'microsecond': 'U',
187-
'nanosecond': 'N'}
188-
189-
_freq_reso_map = {v: k for k, v in _reso_freq_map.items()}
153+
_freq_reso_map = {v: k for k, v in attrname_to_abbrevs.items()}
190154

191155
@classmethod
192156
def get_str(cls, reso: int) -> str:
@@ -215,30 +179,6 @@ class Resolution:
215179
"""
216180
return cls._str_reso_map.get(resostr, cls.RESO_DAY)
217181

218-
@classmethod
219-
def get_freq_group(cls, resostr: str) -> int:
220-
"""
221-
Return frequency str against resolution str.
222-
223-
Examples
224-
--------
225-
>>> f.Resolution.get_freq_group('day')
226-
4000
227-
"""
228-
return get_freq_group(cls.get_freq(resostr))
229-
230-
@classmethod
231-
def get_freq(cls, resostr: str) -> str:
232-
"""
233-
Return frequency str against resolution str.
234-
235-
Examples
236-
--------
237-
>>> f.Resolution.get_freq('day')
238-
'D'
239-
"""
240-
return cls._reso_freq_map[resostr]
241-
242182
@classmethod
243183
def get_str_from_freq(cls, freq: str) -> str:
244184
"""
@@ -303,7 +243,7 @@ class Resolution:
303243
)
304244

305245
next_value = cls._reso_mult_map[start_reso] * value
306-
next_name = cls._reso_str_bump_map[freq]
246+
next_name = cls.reso_str_bump_map[freq]
307247
return cls.get_stride_from_decimal(next_value, next_name)
308248

309249

pandas/core/arrays/datetimelike.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import numpy as np
77

88
from pandas._libs import NaT, NaTType, Period, Timestamp, algos, iNaT, lib
9+
from pandas._libs.tslibs.resolution import Resolution
910
from pandas._libs.tslibs.timedeltas import delta_to_nanoseconds
1011
from pandas._libs.tslibs.timestamps import (
1112
RoundTo,
@@ -1094,14 +1095,14 @@ def inferred_freq(self):
10941095

10951096
@property # NB: override with cache_readonly in immutable subclasses
10961097
def _resolution(self):
1097-
return frequencies.Resolution.get_reso_from_freq(self.freqstr)
1098+
return Resolution.get_reso_from_freq(self.freqstr)
10981099

10991100
@property # NB: override with cache_readonly in immutable subclasses
11001101
def resolution(self) -> str:
11011102
"""
11021103
Returns day, hour, minute, second, millisecond or microsecond
11031104
"""
1104-
return frequencies.Resolution.get_str(self._resolution)
1105+
return Resolution.get_str(self._resolution)
11051106

11061107
@classmethod
11071108
def _validate_frequency(cls, index, freq, **kwargs):

pandas/core/indexes/datetimes.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
import numpy as np
77

8-
from pandas._libs import NaT, Period, Timestamp, index as libindex, lib, tslib as libts
9-
from pandas._libs.tslibs import fields, parsing, timezones
8+
from pandas._libs import NaT, Period, Timestamp, index as libindex, lib, tslib
9+
from pandas._libs.tslibs import fields, parsing, resolution as libresolution, timezones
10+
from pandas._libs.tslibs.frequencies import get_freq_group
1011
from pandas._typing import DtypeObj, Label
1112
from pandas.util._decorators import cache_readonly
1213

@@ -28,7 +29,7 @@
2829
from pandas.core.indexes.extension import inherit_names
2930
import pandas.core.tools.datetimes as tools
3031

31-
from pandas.tseries.frequencies import Resolution, to_offset
32+
from pandas.tseries.frequencies import to_offset
3233
from pandas.tseries.offsets import prefix_mapping
3334

3435

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

324325
def _mpl_repr(self):
325326
# how to represent ourselves to matplotlib
326-
return libts.ints_to_pydatetime(self.asi8, self.tz)
327+
return tslib.ints_to_pydatetime(self.asi8, self.tz)
327328

328329
@property
329330
def _formatter_func(self):
@@ -500,7 +501,7 @@ def _parsed_string_to_bounds(self, reso: str, parsed: datetime):
500501
if reso not in valid_resos:
501502
raise KeyError
502503

503-
grp = Resolution.get_freq_group(reso)
504+
grp = get_freq_group(reso)
504505
per = Period(parsed, freq=(grp, 1))
505506
start, end = per.start_time, per.end_time
506507

@@ -525,7 +526,7 @@ def _validate_partial_date_slice(self, reso: str):
525526
if (
526527
self.is_monotonic
527528
and reso in ["day", "hour", "minute", "second"]
528-
and self._resolution >= Resolution.get_reso(reso)
529+
and self._resolution >= libresolution.Resolution.get_reso(reso)
529530
):
530531
# These resolution/monotonicity validations came from GH3931,
531532
# GH3452 and GH2369.

pandas/core/indexes/period.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

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

504-
grp = resolution.Resolution.get_freq_group(reso)
505-
freqn = resolution.get_freq_group(self.freq)
505+
grp = get_freq_group(reso)
506+
freqn = get_freq_group(self.freq)
506507

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

576-
grp = resolution.Resolution.get_freq_group(reso)
577+
grp = get_freq_group(reso)
577578
iv = Period(parsed, freq=(grp, 1))
578579
return (iv.asfreq(self.freq, how="start"), iv.asfreq(self.freq, how="end"))
579580

580581
def _validate_partial_date_slice(self, reso: str):
581-
grp = resolution.Resolution.get_freq_group(reso)
582-
freqn = resolution.get_freq_group(self.freq)
582+
grp = get_freq_group(reso)
583+
freqn = get_freq_group(self.freq)
583584

584585
if not grp < freqn:
585586
# TODO: we used to also check for

pandas/plotting/_matplotlib/converter.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
import numpy as np
1212

1313
from pandas._libs import lib, tslibs
14-
from pandas._libs.tslibs import resolution
15-
from pandas._libs.tslibs.frequencies import FreqGroup, get_freq_code
14+
from pandas._libs.tslibs.frequencies import FreqGroup, get_freq_code, get_freq_group
1615

1716
from pandas.core.dtypes.common import (
1817
is_datetime64_ns_dtype,
@@ -550,7 +549,7 @@ def _daily_finder(vmin, vmax, freq):
550549
elif freq == FreqGroup.FR_DAY:
551550
periodsperyear = 365
552551
periodspermonth = 28
553-
elif resolution.get_freq_group(freq) == FreqGroup.FR_WK:
552+
elif get_freq_group(freq) == FreqGroup.FR_WK:
554553
periodsperyear = 52
555554
periodspermonth = 3
556555
else: # pragma: no cover
@@ -888,7 +887,7 @@ def _annual_finder(vmin, vmax, freq):
888887
def get_finder(freq):
889888
if isinstance(freq, str):
890889
freq = get_freq_code(freq)[0]
891-
fgroup = resolution.get_freq_group(freq)
890+
fgroup = get_freq_group(freq)
892891

893892
if fgroup == FreqGroup.FR_ANN:
894893
return _annual_finder

0 commit comments

Comments
 (0)