Skip to content
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

DEPR: fractional periods in date_range, timedelta_range, period_range… #56036

Merged
merged 4 commits into from
Nov 20, 2023
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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v2.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ Other Deprecations
- Deprecated :meth:`.DataFrameGroupBy.fillna` and :meth:`.SeriesGroupBy.fillna`; use :meth:`.DataFrameGroupBy.ffill`, :meth:`.DataFrameGroupBy.bfill` for forward and backward filling or :meth:`.DataFrame.fillna` to fill with a single value (or the Series equivalents) (:issue:`55718`)
- Deprecated :meth:`Index.format`, use ``index.astype(str)`` or ``index.map(formatter)`` instead (:issue:`55413`)
- Deprecated ``year``, ``month``, ``quarter``, ``day``, ``hour``, ``minute``, and ``second`` keywords in the :class:`PeriodIndex` constructor, use :meth:`PeriodIndex.from_fields` instead (:issue:`55960`)
- Deprecated allowing non-integer ``periods`` argument in :func:`date_range`, :func:`timedelta_range`, :func:`period_range`, and :func:`interval_range` (:issue:`56036`)
- Deprecated allowing non-keyword arguments in :meth:`DataFrame.to_clipboard`. (:issue:`54229`)
- Deprecated allowing non-keyword arguments in :meth:`DataFrame.to_csv` except ``path_or_buf``. (:issue:`54229`)
- Deprecated allowing non-keyword arguments in :meth:`DataFrame.to_dict`. (:issue:`54229`)
Expand Down
8 changes: 8 additions & 0 deletions pandas/core/arrays/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -2454,6 +2454,14 @@ def validate_periods(periods: int | float | None) -> int | None:
"""
if periods is not None:
if lib.is_float(periods):
warnings.warn(
# GH#56036
"Non-integer 'periods' in pd.date_range, pd.timedelta_range, "
"pd.period_range, and pd.interval_range are deprecated and "
"will raise in a future version.",
FutureWarning,
stacklevel=find_stack_level(),
)
periods = int(periods)
elif not lib.is_integer(periods):
raise TypeError(f"periods must be a number, got {periods}")
Expand Down
7 changes: 2 additions & 5 deletions pandas/core/indexes/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
)
from pandas.core.dtypes.common import (
ensure_platform_int,
is_float,
is_float_dtype,
is_integer,
is_integer_dtype,
Expand All @@ -60,6 +59,7 @@
from pandas.core.dtypes.missing import is_valid_na_for_dtype

from pandas.core.algorithms import unique
from pandas.core.arrays.datetimelike import validate_periods
from pandas.core.arrays.interval import (
IntervalArray,
_interval_shared_docs,
Expand Down Expand Up @@ -1076,10 +1076,7 @@ def interval_range(
if not _is_valid_endpoint(end):
raise ValueError(f"end must be numeric or datetime-like, got {end}")

if is_float(periods):
periods = int(periods)
elif not is_integer(periods) and periods is not None:
raise TypeError(f"periods must be a number, got {periods}")
periods = validate_periods(periods)

if freq is not None and not is_number(freq):
try:
Expand Down
7 changes: 4 additions & 3 deletions pandas/tests/indexes/datetimes/test_date_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,10 @@ def test_date_range_invalid_periods(self):
with pytest.raises(TypeError, match=msg):
date_range(start="1/1/2000", periods="foo", freq="D")

def test_date_range_float_periods(self):
# TODO: reconsider allowing this?
rng = date_range("1/1/2000", periods=10.5)
def test_date_range_fractional_period(self):
msg = "Non-integer 'periods' in pd.date_range, pd.timedelta_range"
with tm.assert_produces_warning(FutureWarning, match=msg):
rng = date_range("1/1/2000", periods=10.5)
exp = date_range("1/1/2000", periods=10)
tm.assert_index_equal(rng, exp)

Expand Down
7 changes: 5 additions & 2 deletions pandas/tests/indexes/interval/test_interval_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,15 @@ def test_float_subtype(self, start, end, freq):
expected = "int64" if is_integer(start + end) else "float64"
assert result == expected

def test_constructor_coverage(self):
def test_interval_range_fractional_period(self):
# float value for periods
expected = interval_range(start=0, periods=10)
result = interval_range(start=0, periods=10.5)
msg = "Non-integer 'periods' in pd.date_range, .* pd.interval_range"
with tm.assert_produces_warning(FutureWarning, match=msg):
result = interval_range(start=0, periods=10.5)
tm.assert_index_equal(result, expected)

def test_constructor_coverage(self):
# equivalent timestamp-like start/end
start, end = Timestamp("2017-01-01"), Timestamp("2017-01-15")
expected = interval_range(start=start, end=end)
Expand Down
6 changes: 4 additions & 2 deletions pandas/tests/indexes/period/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,10 @@ def test_constructor_invalid_quarters(self):
year=range(2000, 2004), quarter=list(range(4)), freq="Q-DEC"
)

def test_constructor_corner(self):
result = period_range("2007-01", periods=10.5, freq="M")
def test_period_range_fractional_period(self):
msg = "Non-integer 'periods' in pd.date_range, pd.timedelta_range"
with tm.assert_produces_warning(FutureWarning, match=msg):
result = period_range("2007-01", periods=10.5, freq="M")
exp = period_range("2007-01", periods=10, freq="M")
tm.assert_index_equal(result, exp)

Expand Down
7 changes: 5 additions & 2 deletions pandas/tests/indexes/timedeltas/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,14 @@ def test_constructor_iso(self):
result = to_timedelta(durations)
tm.assert_index_equal(result, expected)

def test_constructor_coverage(self):
rng = timedelta_range("1 days", periods=10.5)
def test_timedelta_range_fractional_period(self):
msg = "Non-integer 'periods' in pd.date_range, pd.timedelta_range"
with tm.assert_produces_warning(FutureWarning, match=msg):
rng = timedelta_range("1 days", periods=10.5)
exp = timedelta_range("1 days", periods=10)
tm.assert_index_equal(rng, exp)

def test_constructor_coverage(self):
msg = "periods must be a number, got foo"
with pytest.raises(TypeError, match=msg):
timedelta_range(start="1 days", periods="foo", freq="D")
Expand Down
Loading