diff --git a/pandas/_libs/tslibs/dtypes.pyx b/pandas/_libs/tslibs/dtypes.pyx index 2bfbcae70b990..c8779d5b244dc 100644 --- a/pandas/_libs/tslibs/dtypes.pyx +++ b/pandas/_libs/tslibs/dtypes.pyx @@ -53,7 +53,10 @@ cdef class PeriodDtypeBase: @property def _freqstr(self) -> str: # Will be passed to to_offset in Period._maybe_convert_freq - return _reverse_period_code_map.get(self._dtype_code) + out = _reverse_period_code_map.get(self._dtype_code) + if self._n == 1: + return out + return str(self._n) + out cpdef int _get_to_timestamp_base(self): """ diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 0f2a5fe89d7bb..db309da2c0aed 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -1688,6 +1688,8 @@ cdef class _Period(PeriodMixin): # We already have a dtype code dtype = PeriodDtypeBase(freq, 1) freq = dtype._freqstr + elif isinstance(freq, PeriodDtypeBase): + freq = freq._freqstr freq = to_offset(freq) @@ -2372,7 +2374,7 @@ cdef class _Period(PeriodMixin): """ Return a string representation of the frequency. """ - return self.freq.freqstr + return self._dtype._freqstr def __repr__(self) -> str: base = self._dtype._dtype_code diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index 5104c4a30d3a7..4d336f1edbb2d 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -863,6 +863,7 @@ class PeriodDtype(PeriodDtypeBase, PandasExtensionDtype): _match = re.compile(r"(P|p)eriod\[(?P.+)\]") _cache_dtypes: dict[str_type, PandasExtensionDtype] = {} __hash__ = PeriodDtypeBase.__hash__ + _freq: BaseOffset def __new__(cls, freq): """ @@ -886,7 +887,7 @@ def __new__(cls, freq): return u def __reduce__(self): - return type(self), (self.freq,) + return type(self), (self.name,) @property def freq(self): @@ -940,7 +941,7 @@ def __str__(self) -> str_type: @property def name(self) -> str_type: - return f"period[{self.freq.freqstr}]" + return f"period[{self._freqstr}]" @property def na_value(self) -> NaTType: @@ -955,12 +956,6 @@ def __eq__(self, other: Any) -> bool: def __ne__(self, other: Any) -> bool: return not self.__eq__(other) - def __setstate__(self, state) -> None: - # for pickle compat. __getstate__ is defined in the - # PandasExtensionDtype superclass and uses the public properties to - # pickle -> need to set the settable private ones here (see GH26067) - self._freq = state["freq"] - @classmethod def is_dtype(cls, dtype: object) -> bool: """