Skip to content

REF: make FreqGroup an Enum #39027

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 3 commits into from
Jan 8, 2021
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
15 changes: 7 additions & 8 deletions pandas/_libs/tslibs/dtypes.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ cdef class PeriodDtypeBase:
return self._dtype_code == other._dtype_code

@property
def freq_group(self) -> int:
def freq_group_code(self) -> int:
# See also: libperiod.get_freq_group
return (self._dtype_code // 1000) * 1000

Expand All @@ -37,7 +37,6 @@ cdef class PeriodDtypeBase:
from .offsets import to_offset

freqstr = _reverse_period_code_map.get(self._dtype_code)
# equiv: freqstr = libfrequencies.get_freq_str(self._dtype_code)

return to_offset(freqstr)

Expand Down Expand Up @@ -134,7 +133,7 @@ cdef dict attrname_to_abbrevs = _attrname_to_abbrevs
cdef dict _abbrev_to_attrnames = {v: k for k, v in attrname_to_abbrevs.items()}


class FreqGroup:
class FreqGroup(Enum):
# Mirrors c_FreqGroup in the .pxd file
FR_ANN = 1000
FR_QTR = 2000
Expand All @@ -151,9 +150,10 @@ class FreqGroup:
FR_UND = -10000 # undefined

@staticmethod
def get_freq_group(code: int) -> int:
# See also: PeriodDtypeBase.freq_group
return (code // 1000) * 1000
def get_freq_group(code: int) -> "FreqGroup":
# See also: PeriodDtypeBase.freq_group_code
code = (code // 1000) * 1000
return FreqGroup(code)


class Resolution(Enum):
Expand All @@ -178,8 +178,7 @@ class Resolution(Enum):
return self.value >= other.value

@property
def freq_group(self):
# TODO: annotate as returning FreqGroup once that is an enum
def freq_group(self) -> FreqGroup:
if self == Resolution.RESO_NS:
return FreqGroup.FR_NS
elif self == Resolution.RESO_US:
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/arrays/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,11 +1068,11 @@ def _range_from_fields(
if quarter is not None:
if freq is None:
freq = to_offset("Q")
base = FreqGroup.FR_QTR
base = FreqGroup.FR_QTR.value
else:
freq = to_offset(freq)
base = libperiod.freq_to_dtype_code(freq)
if base != FreqGroup.FR_QTR:
if base != FreqGroup.FR_QTR.value:
raise AssertionError("base must equal FR_QTR")

freqstr = freq.freqstr
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime):
raise KeyError

grp = reso.freq_group
per = Period(parsed, freq=grp)
per = Period(parsed, freq=grp.value)
start, end = per.start_time, per.end_time

# GH 24076
Expand Down
10 changes: 5 additions & 5 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,8 @@ def get_loc(self, key, method=None, tolerance=None):
raise KeyError(f"Cannot interpret '{key}' as period") from err

reso = Resolution.from_attrname(reso)
grp = reso.freq_group
freqn = self.dtype.freq_group
grp = reso.freq_group.value
freqn = self.dtype.freq_group_code

# _get_string_slice will handle cases where grp < freqn
assert grp >= freqn
Expand Down Expand Up @@ -580,15 +580,15 @@ def _maybe_cast_slice_bound(self, label, side: str, kind: str):

def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime):
grp = reso.freq_group
iv = Period(parsed, freq=grp)
iv = Period(parsed, freq=grp.value)
return (iv.asfreq(self.freq, how="start"), iv.asfreq(self.freq, how="end"))

def _validate_partial_date_slice(self, reso: Resolution):
assert isinstance(reso, Resolution), (type(reso), reso)
grp = reso.freq_group
freqn = self.dtype.freq_group
freqn = self.dtype.freq_group_code

if not grp < freqn:
if not grp.value < freqn:
# TODO: we used to also check for
# reso in ["day", "hour", "minute", "second"]
# why is that check not needed?
Expand Down
25 changes: 13 additions & 12 deletions pandas/plotting/_matplotlib/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,28 +510,28 @@ def _daily_finder(vmin, vmax, freq: BaseOffset):

periodsperday = -1

if dtype_code >= FreqGroup.FR_HR:
if dtype_code == FreqGroup.FR_NS:
if dtype_code >= FreqGroup.FR_HR.value:
if dtype_code == FreqGroup.FR_NS.value:
periodsperday = 24 * 60 * 60 * 1000000000
elif dtype_code == FreqGroup.FR_US:
elif dtype_code == FreqGroup.FR_US.value:
periodsperday = 24 * 60 * 60 * 1000000
elif dtype_code == FreqGroup.FR_MS:
elif dtype_code == FreqGroup.FR_MS.value:
periodsperday = 24 * 60 * 60 * 1000
elif dtype_code == FreqGroup.FR_SEC:
elif dtype_code == FreqGroup.FR_SEC.value:
periodsperday = 24 * 60 * 60
elif dtype_code == FreqGroup.FR_MIN:
elif dtype_code == FreqGroup.FR_MIN.value:
periodsperday = 24 * 60
elif dtype_code == FreqGroup.FR_HR:
elif dtype_code == FreqGroup.FR_HR.value:
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:
elif dtype_code == FreqGroup.FR_BUS.value:
periodsperyear = 261
periodspermonth = 19
elif dtype_code == FreqGroup.FR_DAY:
elif dtype_code == FreqGroup.FR_DAY.value:
periodsperyear = 365
periodspermonth = 28
elif FreqGroup.get_freq_group(dtype_code) == FreqGroup.FR_WK:
Expand Down Expand Up @@ -661,7 +661,7 @@ def _second_finder(label_interval):
elif span <= periodsperyear // 4:
month_start = period_break(dates_, "month")
info_maj[month_start] = True
if dtype_code < FreqGroup.FR_HR:
if dtype_code < FreqGroup.FR_HR.value:
info["min"] = True
else:
day_start = period_break(dates_, "day")
Expand Down Expand Up @@ -872,14 +872,15 @@ def _annual_finder(vmin, vmax, freq):
def get_finder(freq: BaseOffset):
dtype_code = freq._period_dtype_code
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldnt this return FreqGroup too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there isnt a one-to-one mapping from period_dtype_code to FreqGroup

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k, maybe try to add a public method to freq which does this in the future

fgroup = (dtype_code // 1000) * 1000
fgroup = FreqGroup(fgroup)

if fgroup == FreqGroup.FR_ANN:
return _annual_finder
elif fgroup == FreqGroup.FR_QTR:
return _quarterly_finder
elif dtype_code == FreqGroup.FR_MTH:
elif dtype_code == FreqGroup.FR_MTH.value:
return _monthly_finder
elif (dtype_code >= FreqGroup.FR_BUS) or fgroup == FreqGroup.FR_WK:
elif (dtype_code >= FreqGroup.FR_BUS.value) or fgroup == FreqGroup.FR_WK:
return _daily_finder
else: # pragma: no cover
raise NotImplementedError(f"Unsupported frequency: {dtype_code}")
Expand Down
2 changes: 1 addition & 1 deletion pandas/plotting/_matplotlib/timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def use_dynamic_x(ax: "Axes", data: FrameOrSeriesUnion) -> bool:
if isinstance(data.index, ABCDatetimeIndex):
base = to_offset(freq)._period_dtype_code
x = data.index
if base <= FreqGroup.FR_DAY:
if base <= FreqGroup.FR_DAY.value:
return x[:1].is_normalized
return Period(x[0], freq).to_timestamp().tz_localize(x.tz) == x[0]
return True
Expand Down