diff --git a/pandas/_libs/tslibs/dtypes.pxd b/pandas/_libs/tslibs/dtypes.pxd index 88cfa6ca60d93..33f6789f3b402 100644 --- a/pandas/_libs/tslibs/dtypes.pxd +++ b/pandas/_libs/tslibs/dtypes.pxd @@ -11,7 +11,6 @@ cpdef int64_t periods_per_second(NPY_DATETIMEUNIT reso) except? -1 cdef NPY_DATETIMEUNIT get_supported_reso(NPY_DATETIMEUNIT reso) cdef bint is_supported_unit(NPY_DATETIMEUNIT reso) -cpdef freq_to_period_freqstr(freq_n, freq_name) cdef dict c_OFFSET_TO_PERIOD_FREQSTR cdef dict c_OFFSET_DEPR_FREQSTR cdef dict c_REVERSE_OFFSET_DEPR_FREQSTR diff --git a/pandas/_libs/tslibs/dtypes.pyi b/pandas/_libs/tslibs/dtypes.pyi index dc2855cbecd69..b435b9e2d8b31 100644 --- a/pandas/_libs/tslibs/dtypes.pyi +++ b/pandas/_libs/tslibs/dtypes.pyi @@ -7,7 +7,6 @@ OFFSET_TO_PERIOD_FREQSTR: dict[str, str] def periods_per_day(reso: int = ...) -> int: ... def periods_per_second(reso: int) -> int: ... def abbrev_to_npy_unit(abbrev: str | None) -> int: ... -def freq_to_period_freqstr(freq_n: int, freq_name: str) -> str: ... class PeriodDtypeBase: _dtype_code: int # PeriodDtypeCode diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index 52e1133e596c5..c0d1b2e79f587 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -334,15 +334,6 @@ cdef dict c_REVERSE_OFFSET_DEPR_FREQSTR = { v: k for k, v in c_OFFSET_DEPR_FREQSTR.items() } -cpdef freq_to_period_freqstr(freq_n, freq_name): - if freq_n == 1: - freqstr = f"""{c_OFFSET_TO_PERIOD_FREQSTR.get( - freq_name, freq_name)}""" - else: - freqstr = f"""{freq_n}{c_OFFSET_TO_PERIOD_FREQSTR.get( - freq_name, freq_name)}""" - return freqstr - # Map deprecated resolution abbreviations to correct resolution abbreviations cdef dict c_DEPR_ABBREVS = { "A": "Y", diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 35ec7964fb5fb..5971927a4dad8 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -4698,7 +4698,7 @@ _lite_rule_alias = { "ns": "ns", } -_dont_uppercase = _dont_uppercase = {"h", "bh", "cbh", "MS", "ms", "s"} +_dont_uppercase = {"h", "bh", "cbh", "MS", "ms", "s"} INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}" diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 74804336f09a3..3da0fa182faf3 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -38,10 +38,7 @@ from libc.time cimport ( tm, ) -from pandas._libs.tslibs.dtypes cimport ( - c_OFFSET_TO_PERIOD_FREQSTR, - freq_to_period_freqstr, -) +from pandas._libs.tslibs.dtypes cimport c_OFFSET_TO_PERIOD_FREQSTR from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime @@ -97,9 +94,6 @@ from pandas._libs.tslibs.dtypes cimport ( attrname_to_abbrevs, freq_group_code_to_npy_unit, ) - -from pandas._libs.tslibs.dtypes import freq_to_period_freqstr - from pandas._libs.tslibs.parsing cimport quarter_to_myear from pandas._libs.tslibs.parsing import parse_datetime_string_with_reso @@ -1554,7 +1548,7 @@ def extract_ordinals(ndarray values, freq) -> np.ndarray: # if we don't raise here, we'll segfault later! raise TypeError("extract_ordinals values must be object-dtype") - freqstr = freq_to_period_freqstr(freq.n, freq.name) + freqstr = PeriodDtypeBase(freq._period_dtype_code, freq.n)._freqstr for i in range(n): # Analogous to: p = values[i] @@ -1722,8 +1716,15 @@ cdef class PeriodMixin: condition = self.freq != other if condition: - freqstr = freq_to_period_freqstr(self.freq.n, self.freq.name) - other_freqstr = freq_to_period_freqstr(other.n, other.name) + freqstr = PeriodDtypeBase( + self.freq._period_dtype_code, self.freq.n + )._freqstr + if hasattr(other, "_period_dtype_code"): + other_freqstr = PeriodDtypeBase( + other._period_dtype_code, other.n + )._freqstr + else: + other_freqstr = other.freqstr msg = DIFFERENT_FREQ.format( cls=type(self).__name__, own_freq=freqstr, @@ -2479,7 +2480,7 @@ cdef class _Period(PeriodMixin): >>> pd.Period('2020-01', 'D').freqstr 'D' """ - freqstr = freq_to_period_freqstr(self.freq.n, self.freq.name) + freqstr = PeriodDtypeBase(self.freq._period_dtype_code, self.freq.n)._freqstr return freqstr def __repr__(self) -> str: diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 67f37b8b0c523..640f6669e21eb 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -37,7 +37,6 @@ from pandas._libs.tslibs.dtypes import ( FreqGroup, PeriodDtypeBase, - freq_to_period_freqstr, ) from pandas._libs.tslibs.fields import isleapyear_arr from pandas._libs.tslibs.offsets import ( @@ -325,7 +324,7 @@ def _from_datetime64(cls, data, freq, tz=None) -> Self: PeriodArray[freq] """ if isinstance(freq, BaseOffset): - freq = freq_to_period_freqstr(freq.n, freq.name) + freq = PeriodDtype(freq)._freqstr data, freq = dt64arr_to_periodarr(data, freq, tz) dtype = PeriodDtype(freq) return cls(data, dtype=dtype) @@ -399,7 +398,7 @@ def freq(self) -> BaseOffset: @property def freqstr(self) -> str: - return freq_to_period_freqstr(self.freq.n, self.freq.name) + return PeriodDtype(self.freq)._freqstr def __array__(self, dtype: NpDtype | None = None) -> np.ndarray: if dtype == "i8": @@ -1002,13 +1001,17 @@ def raise_on_incompatible(left, right) -> IncompatibleFrequency: if isinstance(right, (np.ndarray, ABCTimedeltaArray)) or right is None: other_freq = None elif isinstance(right, BaseOffset): - other_freq = freq_to_period_freqstr(right.n, right.name) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", r"PeriodDtype\[B\] is deprecated", category=FutureWarning + ) + other_freq = PeriodDtype(right)._freqstr elif isinstance(right, (ABCPeriodIndex, PeriodArray, Period)): other_freq = right.freqstr else: other_freq = delta_to_tick(Timedelta(right)).freqstr - own_freq = freq_to_period_freqstr(left.freq.n, left.freq.name) + own_freq = PeriodDtype(left.freq)._freqstr msg = DIFFERENT_FREQ.format( cls=type(left).__name__, own_freq=own_freq, other_freq=other_freq ) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 8cf23a7ba6a72..1bc6b7a3eea03 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -34,7 +34,6 @@ Timestamp, to_offset, ) -from pandas._libs.tslibs.dtypes import freq_to_period_freqstr from pandas._typing import ( AlignJoin, AnyArrayLike, @@ -123,6 +122,7 @@ from pandas.core.dtypes.dtypes import ( DatetimeTZDtype, ExtensionDtype, + PeriodDtype, ) from pandas.core.dtypes.generic import ( ABCDataFrame, @@ -10358,9 +10358,9 @@ def _shift_with_freq(self, periods: int, axis: int, freq) -> Self: if freq != orig_freq: assert orig_freq is not None # for mypy raise ValueError( - f"Given freq {freq_to_period_freqstr(freq.n, freq.name)} " + f"Given freq {PeriodDtype(freq)._freqstr} " f"does not match PeriodIndex freq " - f"{freq_to_period_freqstr(orig_freq.n, orig_freq.name)}" + f"{PeriodDtype(orig_freq)._freqstr}" ) new_ax: Index = index.shift(periods) else: diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index efaf1945c8534..a17b585fb1166 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -28,7 +28,6 @@ parsing, to_offset, ) -from pandas._libs.tslibs.dtypes import freq_to_period_freqstr from pandas.compat.numpy import function as nv from pandas.errors import ( InvalidIndexError, @@ -45,7 +44,10 @@ is_list_like, ) from pandas.core.dtypes.concat import concat_compat -from pandas.core.dtypes.dtypes import CategoricalDtype +from pandas.core.dtypes.dtypes import ( + CategoricalDtype, + PeriodDtype, +) from pandas.core.arrays import ( DatetimeArray, @@ -139,7 +141,7 @@ def freqstr(self) -> str: if self._data.freqstr is not None and isinstance( self._data, (PeriodArray, PeriodIndex) ): - freq = freq_to_period_freqstr(self._data.freq.n, self._data.freq.name) + freq = PeriodDtype(self._data.freq)._freqstr return freq else: return self._data.freqstr # type: ignore[return-value] diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 915cd189c73a9..4a5feb92c02f9 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -25,7 +25,6 @@ Timestamp, to_offset, ) -from pandas._libs.tslibs.dtypes import freq_to_period_freqstr from pandas._typing import NDFrameT from pandas.errors import AbstractMethodError from pandas.util._decorators import ( @@ -38,7 +37,10 @@ rewrite_warning, ) -from pandas.core.dtypes.dtypes import ArrowDtype +from pandas.core.dtypes.dtypes import ( + ArrowDtype, + PeriodDtype, +) from pandas.core.dtypes.generic import ( ABCDataFrame, ABCSeries, @@ -2650,7 +2652,7 @@ def asfreq( if isinstance(freq, BaseOffset): if hasattr(freq, "_period_dtype_code"): - freq = freq_to_period_freqstr(freq.n, freq.name) + freq = PeriodDtype(freq)._freqstr new_obj = obj.copy() new_obj.index = obj.index.asfreq(freq, how=how) diff --git a/pandas/io/json/_table_schema.py b/pandas/io/json/_table_schema.py index c279eeea78c6b..a3b912dec66fd 100644 --- a/pandas/io/json/_table_schema.py +++ b/pandas/io/json/_table_schema.py @@ -15,7 +15,6 @@ from pandas._libs import lib from pandas._libs.json import ujson_loads from pandas._libs.tslibs import timezones -from pandas._libs.tslibs.dtypes import freq_to_period_freqstr from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.base import _registry as registry @@ -212,8 +211,7 @@ def convert_json_field_to_pandas_type(field) -> str | CategoricalDtype: elif field.get("freq"): # GH#9586 rename frequency M to ME for offsets offset = to_offset(field["freq"]) - freq_n, freq_name = offset.n, offset.name - freq = freq_to_period_freqstr(freq_n, freq_name) + freq = PeriodDtype(offset)._freqstr # GH#47747 using datetime over period to minimize the change surface return f"period[{freq}]" else: diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 82524ea115019..ed915a8878c9a 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -11,7 +11,9 @@ OutOfBoundsDatetime, Timestamp, ) -from pandas._libs.tslibs.dtypes import freq_to_period_freqstr +from pandas._libs.tslibs import to_offset + +from pandas.core.dtypes.dtypes import PeriodDtype import pandas as pd from pandas import ( @@ -51,7 +53,7 @@ def period_index(freqstr): warnings.filterwarnings( "ignore", message="Period with BDay freq", category=FutureWarning ) - freqstr = freq_to_period_freqstr(1, freqstr) + freqstr = PeriodDtype(to_offset(freqstr))._freqstr pi = pd.period_range(start=Timestamp("2000-01-01"), periods=100, freq=freqstr) return pi @@ -761,7 +763,7 @@ def test_to_period(self, datetime_index, freqstr): dti = datetime_index arr = dti._data - freqstr = freq_to_period_freqstr(1, freqstr) + freqstr = PeriodDtype(to_offset(freqstr))._freqstr expected = dti.to_period(freq=freqstr) result = arr.to_period(freq=freqstr) assert isinstance(result, PeriodArray) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index d478fbfb46b08..5d44c399ee726 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -14,7 +14,8 @@ BaseOffset, to_offset, ) -from pandas._libs.tslibs.dtypes import freq_to_period_freqstr + +from pandas.core.dtypes.dtypes import PeriodDtype from pandas import ( DataFrame, @@ -239,8 +240,7 @@ def test_line_plot_period_mlt_frame(self, frqncy): index=idx, columns=["A", "B", "C"], ) - freq = freq_to_period_freqstr(1, df.index.freq.rule_code) - freq = df.index.asfreq(freq).freq + freq = df.index.freq.rule_code _check_plot_works(df.plot, freq) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") @@ -254,7 +254,7 @@ def test_line_plot_datetime_frame(self, freq): index=idx, columns=["A", "B", "C"], ) - freq = freq_to_period_freqstr(1, df.index.freq.rule_code) + freq = PeriodDtype(df.index.freq)._freqstr freq = df.index.to_period(freq).freq _check_plot_works(df.plot, freq) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 92b4bcc17946f..534bee5fede44 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -19,10 +19,7 @@ MONTHS, int_to_weekday, ) -from pandas._libs.tslibs.dtypes import ( - OFFSET_TO_PERIOD_FREQSTR, - freq_to_period_freqstr, -) +from pandas._libs.tslibs.dtypes import OFFSET_TO_PERIOD_FREQSTR from pandas._libs.tslibs.fields import ( build_field_sarray, month_position_check, @@ -559,7 +556,7 @@ def _maybe_coerce_freq(code) -> str: """ assert code is not None if isinstance(code, DateOffset): - code = freq_to_period_freqstr(1, code.name) + code = PeriodDtype(to_offset(code.name))._freqstr if code in {"h", "min", "s", "ms", "us", "ns"}: return code else: