Skip to content

Commit 959452c

Browse files
committed
DEPR: Deprecate range-based PeriodIndex construction
Closes #20535
1 parent c230f29 commit 959452c

File tree

12 files changed

+163
-86
lines changed

12 files changed

+163
-86
lines changed

doc/source/whatsnew/v0.24.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,7 @@ Deprecations
11421142
- Timezone converting a tz-aware ``datetime.datetime`` or :class:`Timestamp` with :class:`Timestamp` and the ``tz`` argument is now deprecated. Instead, use :meth:`Timestamp.tz_convert` (:issue:`23579`)
11431143
- :func:`pandas.api.types.is_period` is deprecated in favor of `pandas.api.types.is_period_dtype` (:issue:`23917`)
11441144
- :func:`pandas.api.types.is_datetimetz` is deprecated in favor of `pandas.api.types.is_datetime64tz` (:issue:`23917`)
1145-
- Creating a :class:`TimedeltaIndex` or :class:`DatetimeIndex` by passing range arguments `start`, `end`, and `periods` is deprecated in favor of :func:`timedelta_range` and :func:`date_range` (:issue:`23919`)
1145+
- Creating a :class:`TimedeltaIndex`, :class:`DatetimeIndex`, or :class:`PeriodIndex` by passing range arguments `start`, `end`, and `periods` is deprecated in favor of :func:`timedelta_range`, :func:`date_range`, or :func:`period_range` (:issue:`23919`)
11461146
- Passing a string alias like ``'datetime64[ns, UTC]'`` as the ``unit`` parameter to :class:`DatetimeTZDtype` is deprecated. Use :class:`DatetimeTZDtype.construct_from_string` instead (:issue:`23990`).
11471147
- In :meth:`Series.where` with Categorical data, providing an ``other`` that is not present in the categories is deprecated. Convert the categorical to a different dtype or add the ``other`` to the categories first (:issue:`24077`).
11481148
- :meth:`Series.clip_lower`, :meth:`Series.clip_upper`, :meth:`DataFrame.clip_lower` and :meth:`DataFrame.clip_upper` are deprecated and will be removed in a future version. Use ``Series.clip(lower=threshold)``, ``Series.clip(upper=threshold)`` and the equivalent ``DataFrame`` methods (:issue:`24203`)
@@ -1313,6 +1313,7 @@ Datetimelike
13131313
- Bug in :meth:`Series.combine_first` not properly aligning categoricals, so that missing values in ``self`` where not filled by valid values from ``other`` (:issue:`24147`)
13141314
- Bug in :func:`DataFrame.combine` with datetimelike values raising a TypeError (:issue:`23079`)
13151315
- Bug in :func:`date_range` with frequency of ``Day`` or higher where dates sufficiently far in the future could wrap around to the past instead of raising ``OutOfBoundsDatetime`` (:issue:`14187`)
1316+
- Bug in :func:`period_range` ignoring the frequency of ``start`` and ``end`` when those are provided as :class:`Period` objects (:issue:`20535`).
13161317
- Bug in :class:`PeriodIndex` with attribute ``freq.n`` greater than 1 where adding a :class:`DateOffset` object would return incorrect results (:issue:`23215`)
13171318
- Bug in :class:`Series` that interpreted string indices as lists of characters when setting datetimelike values (:issue:`23451`)
13181319
- Bug in :class:`Timestamp` constructor which would drop the frequency of an input :class:`Timestamp` (:issue:`22311`)

pandas/core/indexes/datetimes.py

+21-5
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,27 @@ class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index):
8181
start : starting value, datetime-like, optional
8282
If data is None, start is used as the start point in generating regular
8383
timestamp data.
84+
85+
.. deprecated:: 0.24.0
86+
8487
periods : int, optional, > 0
8588
Number of periods to generate, if generating index. Takes precedence
8689
over end argument
87-
end : end time, datetime-like, optional
90+
91+
.. deprecated:: 0.24.0
92+
93+
end : end time, datetime-like, optional
8894
If periods is none, generated index will extend to first conforming
8995
time on or just past end argument
96+
97+
.. deprecated:: 0.24.0
98+
9099
closed : string or None, default None
91100
Make the interval closed with respect to the given frequency to
92101
the 'left', 'right', or both sides (None)
102+
103+
.. deprecated:: 0.24. 0
104+
93105
tz : pytz.timezone or dateutil.tz.tzfile
94106
ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
95107
When clocks moved backward due to DST, ambiguous times may arise.
@@ -166,12 +178,16 @@ class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index):
166178
To learn more about the frequency strings, please see `this link
167179
<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
168180
181+
Creating a DatetimeIndex based on `start`, `periods`, and `end` has
182+
been deprecated in favor of :func:`date_range`.
183+
169184
See Also
170185
---------
171186
Index : The base pandas Index type.
172187
TimedeltaIndex : Index of timedelta64 data.
173188
PeriodIndex : Index of Period data.
174-
pandas.to_datetime : Convert argument to datetime.
189+
to_datetime : Convert argument to datetime.
190+
date_range : Create a fixed-frequency DatetimeIndex.
175191
"""
176192
_typ = 'datetimeindex'
177193
_join_precedence = 10
@@ -223,13 +239,13 @@ def __new__(cls, data=None,
223239
verify_integrity = True
224240

225241
if data is None:
242+
result = cls._generate_range(start, end, periods,
243+
freq=freq, tz=tz, normalize=normalize,
244+
closed=closed, ambiguous=ambiguous)
226245
warnings.warn("Creating a DatetimeIndex by passing range "
227246
"endpoints is deprecated. Use "
228247
"`pandas.date_range` instead.",
229248
FutureWarning, stacklevel=2)
230-
result = cls._generate_range(start, end, periods,
231-
freq=freq, tz=tz, normalize=normalize,
232-
closed=closed, ambiguous=ambiguous)
233249
result.name = name
234250
return result
235251

pandas/core/indexes/period.py

+33-7
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,21 @@ class PeriodIndex(DatelikeOps, DatetimeIndexOpsMixin, Int64Index,
9292
start : starting value, period-like, optional
9393
If data is None, used as the start point in generating regular
9494
period data.
95+
96+
.. deprecated:: 0.24.0
97+
9598
periods : int, optional, > 0
9699
Number of periods to generate, if generating index. Takes precedence
97100
over end argument
101+
102+
.. deprecated:: 0.24.0
103+
98104
end : end value, period-like, optional
99105
If periods is none, generated index will extend to first conforming
100106
period on or just past end argument
107+
108+
.. deprecated:: 0.24.0
109+
101110
year : int, array, or Series, default None
102111
month : int, array, or Series, default None
103112
quarter : int, array, or Series, default None
@@ -138,18 +147,22 @@ class PeriodIndex(DatelikeOps, DatetimeIndexOpsMixin, Int64Index,
138147
strftime
139148
to_timestamp
140149
150+
Notes
151+
-----
152+
Creating a PeriodIndex based on `start`, `periods`, and `end` has
153+
been deprecated in favor of :func:`period_range`.
154+
141155
Examples
142156
--------
143157
>>> idx = pd.PeriodIndex(year=year_arr, quarter=q_arr)
144158
145-
>>> idx2 = pd.PeriodIndex(start='2000', end='2010', freq='A')
146-
147159
See Also
148160
---------
149161
Index : The base pandas Index type.
150162
Period : Represents a period of time.
151163
DatetimeIndex : Index with datetime64 data.
152164
TimedeltaIndex : Index of timedelta64 data.
165+
period_range : Create a fixed-frequency PeriodIndex.
153166
"""
154167
_typ = 'periodindex'
155168
_attributes = ['name', 'freq']
@@ -183,6 +196,14 @@ def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
183196
# range-based.
184197
data, freq = PeriodArray._generate_range(start, end, periods,
185198
freq, fields)
199+
# PeriodArray._generate range does validate that fields is
200+
# empty when really using the range-based constructor.
201+
if not fields:
202+
warnings.warn("Creating a PeriodIndex by passing range "
203+
"endpoints is deprecated. Use "
204+
"`pandas.period_range` instead.",
205+
FutureWarning, stacklevel=2)
206+
186207
data = PeriodArray(data, freq=freq)
187208
else:
188209
freq = validate_dtype_freq(dtype, freq)
@@ -983,7 +1004,7 @@ def base(self):
9831004
PeriodIndex._add_datetimelike_methods()
9841005

9851006

986-
def period_range(start=None, end=None, periods=None, freq='D', name=None):
1007+
def period_range(start=None, end=None, periods=None, freq=None, name=None):
9871008
"""
9881009
Return a fixed frequency PeriodIndex, with day (calendar) as the default
9891010
frequency
@@ -996,8 +1017,11 @@ def period_range(start=None, end=None, periods=None, freq='D', name=None):
9961017
Right bound for generating periods
9971018
periods : integer, default None
9981019
Number of periods to generate
999-
freq : string or DateOffset, default 'D'
1000-
Frequency alias
1020+
freq : string or DateOffset, optional
1021+
Frequency alias. By default the freq is taken from `start` or `end`
1022+
if those are Period objects. Otherwise, the default is ``"D"`` for
1023+
daily frequency.
1024+
10011025
name : string, default None
10021026
Name of the resulting PeriodIndex
10031027
@@ -1035,5 +1059,7 @@ def period_range(start=None, end=None, periods=None, freq='D', name=None):
10351059
raise ValueError('Of the three parameters: start, end, and periods, '
10361060
'exactly two must be specified')
10371061

1038-
return PeriodIndex(start=start, end=end, periods=periods,
1039-
freq=freq, name=name)
1062+
data, freq = PeriodArray._generate_range(start, end, periods, freq,
1063+
fields={})
1064+
data = PeriodArray(data, freq=freq)
1065+
return PeriodIndex(data, name=name)

pandas/core/indexes/timedeltas.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,27 @@ class TimedeltaIndex(TimedeltaArray, DatetimeIndexOpsMixin,
6868
start : starting value, timedelta-like, optional
6969
If data is None, start is used as the start point in generating regular
7070
timedelta data.
71+
72+
.. deprecated:: 0.24.0
73+
7174
periods : int, optional, > 0
7275
Number of periods to generate, if generating index. Takes precedence
7376
over end argument
74-
end : end time, timedelta-like, optional
77+
78+
.. deprecated:: 0.24.0
79+
80+
end : end time, timedelta-like, optional
7581
If periods is none, generated index will extend to first conforming
7682
time on or just past end argument
83+
84+
.. deprecated:: 0.24. 0
85+
7786
closed : string or None, default None
7887
Make the interval closed with respect to the given frequency to
7988
the 'left', 'right', or both sides (None)
89+
90+
.. deprecated:: 0.24. 0
91+
8092
name : object
8193
Name to be stored in the index
8294
@@ -104,12 +116,15 @@ class TimedeltaIndex(TimedeltaArray, DatetimeIndexOpsMixin,
104116
Timedelta : Represents a duration between two dates or times.
105117
DatetimeIndex : Index of datetime64 data.
106118
PeriodIndex : Index of Period data.
119+
timedelta_range : Create a fixed-frequency TimedeltaIndex.
107120
108121
Notes
109122
-----
110-
111123
To learn more about the frequency strings, please see `this link
112124
<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
125+
126+
Creating a TimedeltaIndex based on `start`, `periods`, and `end` has
127+
been deprecated in favor of :func:`timedelta_range`.
113128
"""
114129

115130
_typ = 'timedeltaindex'

pandas/tests/indexes/period/test_arithmetic.py

+14-14
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pytest
55

66
import pandas as pd
7-
from pandas import PeriodIndex
7+
from pandas import PeriodIndex, period_range
88
import pandas.util.testing as tm
99

1010

@@ -26,36 +26,36 @@ def test_pi_shift_ndarray(self):
2626
tm.assert_index_equal(result, expected)
2727

2828
def test_shift(self):
29-
pi1 = PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009')
30-
pi2 = PeriodIndex(freq='A', start='1/1/2002', end='12/1/2010')
29+
pi1 = period_range(freq='A', start='1/1/2001', end='12/1/2009')
30+
pi2 = period_range(freq='A', start='1/1/2002', end='12/1/2010')
3131

3232
tm.assert_index_equal(pi1.shift(0), pi1)
3333

3434
assert len(pi1) == len(pi2)
3535
tm.assert_index_equal(pi1.shift(1), pi2)
3636

37-
pi1 = PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009')
38-
pi2 = PeriodIndex(freq='A', start='1/1/2000', end='12/1/2008')
37+
pi1 = period_range(freq='A', start='1/1/2001', end='12/1/2009')
38+
pi2 = period_range(freq='A', start='1/1/2000', end='12/1/2008')
3939
assert len(pi1) == len(pi2)
4040
tm.assert_index_equal(pi1.shift(-1), pi2)
4141

42-
pi1 = PeriodIndex(freq='M', start='1/1/2001', end='12/1/2009')
43-
pi2 = PeriodIndex(freq='M', start='2/1/2001', end='1/1/2010')
42+
pi1 = period_range(freq='M', start='1/1/2001', end='12/1/2009')
43+
pi2 = period_range(freq='M', start='2/1/2001', end='1/1/2010')
4444
assert len(pi1) == len(pi2)
4545
tm.assert_index_equal(pi1.shift(1), pi2)
4646

47-
pi1 = PeriodIndex(freq='M', start='1/1/2001', end='12/1/2009')
48-
pi2 = PeriodIndex(freq='M', start='12/1/2000', end='11/1/2009')
47+
pi1 = period_range(freq='M', start='1/1/2001', end='12/1/2009')
48+
pi2 = period_range(freq='M', start='12/1/2000', end='11/1/2009')
4949
assert len(pi1) == len(pi2)
5050
tm.assert_index_equal(pi1.shift(-1), pi2)
5151

52-
pi1 = PeriodIndex(freq='D', start='1/1/2001', end='12/1/2009')
53-
pi2 = PeriodIndex(freq='D', start='1/2/2001', end='12/2/2009')
52+
pi1 = period_range(freq='D', start='1/1/2001', end='12/1/2009')
53+
pi2 = period_range(freq='D', start='1/2/2001', end='12/2/2009')
5454
assert len(pi1) == len(pi2)
5555
tm.assert_index_equal(pi1.shift(1), pi2)
5656

57-
pi1 = PeriodIndex(freq='D', start='1/1/2001', end='12/1/2009')
58-
pi2 = PeriodIndex(freq='D', start='12/31/2000', end='11/30/2009')
57+
pi1 = period_range(freq='D', start='1/1/2001', end='12/1/2009')
58+
pi2 = period_range(freq='D', start='12/31/2000', end='11/30/2009')
5959
assert len(pi1) == len(pi2)
6060
tm.assert_index_equal(pi1.shift(-1), pi2)
6161

@@ -100,7 +100,7 @@ def test_shift_gh8083(self):
100100

101101
def test_shift_periods(self):
102102
# GH #22458 : argument 'n' was deprecated in favor of 'periods'
103-
idx = PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009')
103+
idx = period_range(freq='A', start='1/1/2001', end='12/1/2009')
104104
tm.assert_index_equal(idx.shift(periods=0), idx)
105105
tm.assert_index_equal(idx.shift(0), idx)
106106
with tm.assert_produces_warning(FutureWarning,

pandas/tests/indexes/period/test_asfreq.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
22
import pytest
33

44
import pandas as pd
5-
from pandas import DataFrame, PeriodIndex, Series
5+
from pandas import DataFrame, PeriodIndex, Series, period_range
66
from pandas.util import testing as tm
77

88

99
class TestPeriodIndex(object):
1010

1111
def test_asfreq(self):
12-
pi1 = PeriodIndex(freq='A', start='1/1/2001', end='1/1/2001')
13-
pi2 = PeriodIndex(freq='Q', start='1/1/2001', end='1/1/2001')
14-
pi3 = PeriodIndex(freq='M', start='1/1/2001', end='1/1/2001')
15-
pi4 = PeriodIndex(freq='D', start='1/1/2001', end='1/1/2001')
16-
pi5 = PeriodIndex(freq='H', start='1/1/2001', end='1/1/2001 00:00')
17-
pi6 = PeriodIndex(freq='Min', start='1/1/2001', end='1/1/2001 00:00')
18-
pi7 = PeriodIndex(freq='S', start='1/1/2001', end='1/1/2001 00:00:00')
12+
pi1 = period_range(freq='A', start='1/1/2001', end='1/1/2001')
13+
pi2 = period_range(freq='Q', start='1/1/2001', end='1/1/2001')
14+
pi3 = period_range(freq='M', start='1/1/2001', end='1/1/2001')
15+
pi4 = period_range(freq='D', start='1/1/2001', end='1/1/2001')
16+
pi5 = period_range(freq='H', start='1/1/2001', end='1/1/2001 00:00')
17+
pi6 = period_range(freq='Min', start='1/1/2001', end='1/1/2001 00:00')
18+
pi7 = period_range(freq='S', start='1/1/2001', end='1/1/2001 00:00:00')
1919

2020
assert pi1.asfreq('Q', 'S') == pi2
2121
assert pi1.asfreq('Q', 's') == pi2
@@ -70,7 +70,7 @@ def test_asfreq(self):
7070
pytest.raises(ValueError, pi7.asfreq, 'T', 'foo')
7171
result1 = pi1.asfreq('3M')
7272
result2 = pi1.asfreq('M')
73-
expected = PeriodIndex(freq='M', start='2001-12', end='2001-12')
73+
expected = period_range(freq='M', start='2001-12', end='2001-12')
7474
tm.assert_numpy_array_equal(result1.asi8, expected.asi8)
7575
assert result1.freqstr == '3M'
7676
tm.assert_numpy_array_equal(result2.asi8, expected.asi8)
@@ -126,7 +126,7 @@ def test_asfreq_combined_pi(self):
126126
assert result.freq == exp.freq
127127

128128
def test_asfreq_ts(self):
129-
index = PeriodIndex(freq='A', start='1/1/2001', end='12/31/2010')
129+
index = period_range(freq='A', start='1/1/2001', end='12/31/2010')
130130
ts = Series(np.random.randn(len(index)), index=index)
131131
df = DataFrame(np.random.randn(len(index), 3), index=index)
132132

0 commit comments

Comments
 (0)