Skip to content

DEPR: PeriodDtype.freq #61897

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ Other Deprecations

- Deprecated :func:`core.internals.api.make_block`, use public APIs instead (:issue:`56815`)
- Deprecated :meth:`.DataFrameGroupby.corrwith` (:issue:`57158`)
- Deprecated :meth:`PeriodDtype.freq`, use ``dtype.unit`` instead (:issue:`61897`)
- Deprecated :meth:`Timestamp.utcfromtimestamp`, use ``Timestamp.fromtimestamp(ts, "UTC")`` instead (:issue:`56680`)
- Deprecated :meth:`Timestamp.utcnow`, use ``Timestamp.now("UTC")`` instead (:issue:`56680`)
- Deprecated allowing non-keyword arguments in :meth:`DataFrame.all`, :meth:`DataFrame.min`, :meth:`DataFrame.max`, :meth:`DataFrame.sum`, :meth:`DataFrame.prod`, :meth:`DataFrame.mean`, :meth:`DataFrame.median`, :meth:`DataFrame.sem`, :meth:`DataFrame.var`, :meth:`DataFrame.std`, :meth:`DataFrame.skew`, :meth:`DataFrame.kurt`, :meth:`Series.all`, :meth:`Series.min`, :meth:`Series.max`, :meth:`Series.sum`, :meth:`Series.prod`, :meth:`Series.mean`, :meth:`Series.median`, :meth:`Series.sem`, :meth:`Series.var`, :meth:`Series.std`, :meth:`Series.skew`, and :meth:`Series.kurt`. (:issue:`57087`)
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/arrays/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ def astype(self, dtype, copy: bool = True):
)

elif isinstance(dtype, PeriodDtype):
return self.to_period(freq=dtype.freq)
return self.to_period(freq=dtype.unit)
return dtl.DatetimeLikeArrayMixin.astype(self, dtype, copy)

# -----------------------------------------------------------------
Expand Down
14 changes: 7 additions & 7 deletions pandas/core/arrays/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def __init__(self, values, dtype: Dtype | None = None, copy: bool = False) -> No

if isinstance(values, type(self)):
if dtype is not None and dtype != values.dtype:
raise raise_on_incompatible(values, dtype.freq)
raise raise_on_incompatible(values, dtype.unit)
values, dtype = values._ndarray, values.dtype

if not copy:
Expand Down Expand Up @@ -271,7 +271,7 @@ def _from_sequence(
if dtype is not None:
dtype = pandas_dtype(dtype)
if dtype and isinstance(dtype, PeriodDtype):
freq = dtype.freq
freq = dtype.unit
else:
freq = None

Expand Down Expand Up @@ -380,7 +380,7 @@ def freq(self) -> BaseOffset:
"""
Return the frequency object for this PeriodArray.
"""
return self.dtype.freq
return self.dtype.unit

@property
def freqstr(self) -> str:
Expand Down Expand Up @@ -945,7 +945,7 @@ def astype(self, dtype, copy: bool = True):
else:
return self.copy()
if isinstance(dtype, PeriodDtype):
return self.asfreq(dtype.freq)
return self.asfreq(dtype.unit)

if lib.is_np_dtype(dtype, "M") or isinstance(dtype, DatetimeTZDtype):
# GH#45038 match PeriodIndex behavior.
Expand Down Expand Up @@ -1227,7 +1227,7 @@ def period_array(
if isinstance(data_dtype, PeriodDtype):
out = PeriodArray(data)
if freq is not None:
if freq == data_dtype.freq:
if freq == data_dtype.unit:
return out
return out.asfreq(freq)
return out
Expand Down Expand Up @@ -1298,8 +1298,8 @@ def validate_dtype_freq(
if not isinstance(dtype, PeriodDtype):
raise ValueError("dtype must be PeriodDtype")
if freq is None:
freq = dtype.freq
elif freq != dtype.freq:
freq = dtype.unit
elif freq != dtype.unit:
raise IncompatibleFrequency("specified freq and dtype are different")
# error: Incompatible return value type (got "Union[BaseOffset, Any, None]",
# expected "BaseOffset")
Expand Down
35 changes: 35 additions & 0 deletions pandas/core/dtypes/dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,11 +1071,41 @@ def __new__(cls, freq) -> PeriodDtype: # noqa: PYI034
def __reduce__(self) -> tuple[type_t[Self], tuple[str_type]]:
return type(self), (self.name,)

@property
def unit(self):
"""
The unit object of this PeriodDtype.

The `unit` property returns the `BaseOffset` object that represents the
unit of the PeriodDtype. This unit specifies the interval (e.g.,
daily, monthly, yearly) associated with the Period type. It is essential
for operations that depend on time-based calculations within a period index
or series.

See Also
--------
Period : Represents a period of time.
PeriodIndex : Immutable ndarray holding ordinal values indicating
regular periods.
PeriodDtype : An ExtensionDtype for Period data.
date_range : Return a fixed frequency range of dates.

Examples
--------
>>> dtype = pd.PeriodDtype("D")
>>> dtype.unit
<Day>
"""
return self._freq

@property
def freq(self) -> BaseOffset:
"""
The frequency object of this PeriodDtype.

.. deprecated:: 3.0
Use dtype.unit instead.

The `freq` property returns the `BaseOffset` object that represents the
frequency of the PeriodDtype. This frequency specifies the interval (e.g.,
daily, monthly, yearly) associated with the Period type. It is essential
Expand All @@ -1096,6 +1126,11 @@ def freq(self) -> BaseOffset:
>>> dtype.freq
<Day>
"""
warnings.warn(
"PeriodDtype.freq is deprecated, use dtype.unit instead",
FutureWarning,
stacklevel=find_stack_level(),
)
return self._freq

@classmethod
Expand Down
2 changes: 1 addition & 1 deletion pandas/io/json/_table_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def convert_pandas_type_to_json_field(arr) -> dict[str, JSONSerializable]:
field["constraints"] = {"enum": list(cats)}
field["ordered"] = ordered
elif isinstance(dtype, PeriodDtype):
field["freq"] = dtype.freq.freqstr
field["freq"] = dtype.unit.freqstr
elif isinstance(dtype, DatetimeTZDtype):
if timezones.is_utc(dtype.tz):
field["tz"] = "UTC"
Expand Down
15 changes: 10 additions & 5 deletions pandas/tests/dtypes/test_dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,11 +426,11 @@ def test_construction(self):

for s in ["period[D]", "Period[D]", "D"]:
dt = PeriodDtype(s)
assert dt.freq == pd.tseries.offsets.Day()
assert dt.unit == pd.tseries.offsets.Day()

for s in ["period[3D]", "Period[3D]", "3D"]:
dt = PeriodDtype(s)
assert dt.freq == pd.tseries.offsets.Day(3)
assert dt.unit == pd.tseries.offsets.Day(3)

for s in [
"period[26h]",
Expand All @@ -441,7 +441,7 @@ def test_construction(self):
"1D2h",
]:
dt = PeriodDtype(s)
assert dt.freq == pd.tseries.offsets.Hour(26)
assert dt.unit == pd.tseries.offsets.Hour(26)

def test_cannot_use_custom_businessday(self):
# GH#52534
Expand Down Expand Up @@ -565,10 +565,10 @@ def test_not_string(self):
def test_perioddtype_caching_dateoffset_normalize(self):
# GH 24121
per_d = PeriodDtype(pd.offsets.YearEnd(normalize=True))
assert per_d.freq.normalize
assert per_d.unit.normalize

per_d2 = PeriodDtype(pd.offsets.YearEnd(normalize=False))
assert not per_d2.freq.normalize
assert not per_d2.unit.normalize

def test_dont_keep_ref_after_del(self):
# GH 54184
Expand All @@ -577,6 +577,11 @@ def test_dont_keep_ref_after_del(self):
del dtype
assert ref() is None

def test_freq_deprecation(self, dtype):
msg = "PeriodDtype.freq is deprecated, use dtype.unit instead"
with tm.assert_produces_warning(FutureWarning, match=msg):
dtype.freq


class TestIntervalDtype(Base):
@pytest.fixture
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/extension/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
expected = exp_op(skipna=skipna)
# error: Item "dtype[Any]" of "dtype[Any] | ExtensionDtype" has no
# attribute "freq"
freq = ser.dtype.freq # type: ignore[union-attr]
freq = ser.dtype.unit # type: ignore[union-attr]
expected = Period._from_ordinal(int(expected), freq=freq)
tm.assert_almost_equal(result, expected)

Expand Down
Loading