Skip to content

Commit 6cfa23e

Browse files
TomAugspurgerjreback
authored andcommitted
DEPR: Deprecate range-based PeriodIndex construction (#24354)
1 parent 5a9f452 commit 6cfa23e

22 files changed

+248
-147
lines changed

asv_bench/benchmarks/period.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from pandas import (DataFrame, Series, Period, PeriodIndex, date_range,
2-
period_range)
1+
from pandas import (
2+
DataFrame, Period, PeriodIndex, Series, date_range, period_range)
33

44

55
class PeriodProperties(object):
@@ -94,7 +94,7 @@ def time_value_counts(self, typ):
9494
class Indexing(object):
9595

9696
def setup(self):
97-
self.index = PeriodIndex(start='1985', periods=1000, freq='D')
97+
self.index = period_range(start='1985', periods=1000, freq='D')
9898
self.series = Series(range(1000), index=self.index)
9999
self.period = self.index[500]
100100

doc/source/whatsnew/v0.21.0.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ Previous Behavior:
654654

655655
.. code-block:: ipython
656656
657-
In [1]: pi = pd.PeriodIndex(start='2000-01-01', freq='D', periods=10)
657+
In [1]: pi = pd.period_range(start='2000-01-01', freq='D', periods=10)
658658
659659
In [2]: s = pd.Series(np.arange(10), index=pi)
660660
@@ -674,7 +674,7 @@ New Behavior:
674674

675675
.. ipython:: python
676676
677-
pi = pd.PeriodIndex(start='2000-01-01', freq='D', periods=10)
677+
pi = pd.period_range(start='2000-01-01', freq='D', periods=10)
678678
679679
s = pd.Series(np.arange(10), index=pi)
680680

doc/source/whatsnew/v0.24.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ Deprecations
11481148
- 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`)
11491149
- :func:`pandas.api.types.is_period` is deprecated in favor of `pandas.api.types.is_period_dtype` (:issue:`23917`)
11501150
- :func:`pandas.api.types.is_datetimetz` is deprecated in favor of `pandas.api.types.is_datetime64tz` (:issue:`23917`)
1151-
- 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`)
1151+
- 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`)
11521152
- 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`).
11531153
- 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`).
11541154
- :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`)
@@ -1310,6 +1310,7 @@ Datetimelike
13101310
- 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`)
13111311
- Bug in :func:`DataFrame.combine` with datetimelike values raising a TypeError (:issue:`23079`)
13121312
- 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`)
1313+
- Bug in :func:`period_range` ignoring the frequency of ``start`` and ``end`` when those are provided as :class:`Period` objects (:issue:`20535`).
13131314
- Bug in :class:`PeriodIndex` with attribute ``freq.n`` greater than 1 where adding a :class:`DateOffset` object would return incorrect results (:issue:`23215`)
13141315
- Bug in :class:`Series` that interpreted string indices as lists of characters when setting datetimelike values (:issue:`23451`)
13151316
- Bug in :class:`Timestamp` constructor which would drop the frequency of an input :class:`Timestamp` (:issue:`22311`)

pandas/core/indexes/datetimes.py

+23-6
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,27 @@ class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index,
129129
start : starting value, datetime-like, optional
130130
If data is None, start is used as the start point in generating regular
131131
timestamp data.
132+
133+
.. deprecated:: 0.24.0
134+
132135
periods : int, optional, > 0
133136
Number of periods to generate, if generating index. Takes precedence
134137
over end argument
135-
end : end time, datetime-like, optional
138+
139+
.. deprecated:: 0.24.0
140+
141+
end : end time, datetime-like, optional
136142
If periods is none, generated index will extend to first conforming
137143
time on or just past end argument
144+
145+
.. deprecated:: 0.24.0
146+
138147
closed : string or None, default None
139148
Make the interval closed with respect to the given frequency to
140149
the 'left', 'right', or both sides (None)
150+
151+
.. deprecated:: 0.24. 0
152+
141153
tz : pytz.timezone or dateutil.tz.tzfile
142154
ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
143155
When clocks moved backward due to DST, ambiguous times may arise.
@@ -214,12 +226,16 @@ class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index,
214226
To learn more about the frequency strings, please see `this link
215227
<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
216228
229+
Creating a DatetimeIndex based on `start`, `periods`, and `end` has
230+
been deprecated in favor of :func:`date_range`.
231+
217232
See Also
218233
---------
219234
Index : The base pandas Index type.
220235
TimedeltaIndex : Index of timedelta64 data.
221236
PeriodIndex : Index of Period data.
222-
pandas.to_datetime : Convert argument to datetime.
237+
to_datetime : Convert argument to datetime.
238+
date_range : Create a fixed-frequency DatetimeIndex.
223239
"""
224240
_typ = 'datetimeindex'
225241
_join_precedence = 10
@@ -271,14 +287,15 @@ def __new__(cls, data=None,
271287
verify_integrity = True
272288

273289
if data is None:
274-
warnings.warn("Creating a DatetimeIndex by passing range "
275-
"endpoints is deprecated. Use "
276-
"`pandas.date_range` instead.",
277-
FutureWarning, stacklevel=2)
278290
dtarr = DatetimeArray._generate_range(
279291
start, end, periods,
280292
freq=freq, tz=tz, normalize=normalize,
281293
closed=closed, ambiguous=ambiguous)
294+
warnings.warn("Creating a DatetimeIndex by passing range "
295+
"endpoints is deprecated. Use "
296+
"`pandas.date_range` instead.",
297+
FutureWarning, stacklevel=2)
298+
282299
return cls(dtarr, name=name)
283300

284301
if is_scalar(data):

pandas/core/indexes/period.py

+55-10
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']
@@ -181,8 +194,32 @@ def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
181194

182195
if data is None and ordinal is None:
183196
# range-based.
184-
data, freq = PeriodArray._generate_range(start, end, periods,
185-
freq, fields)
197+
data, freq2 = PeriodArray._generate_range(start, end, periods,
198+
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+
msg = ("Creating a PeriodIndex by passing range "
203+
"endpoints is deprecated. Use "
204+
"`pandas.period_range` instead.")
205+
# period_range differs from PeriodIndex for cases like
206+
# start="2000", periods=4
207+
# PeriodIndex interprets that as A-DEC freq.
208+
# period_range interprets it as 'D' freq.
209+
cond = (
210+
freq is None and (
211+
(start and not isinstance(start, Period)) or
212+
(end and not isinstance(end, Period))
213+
)
214+
)
215+
if cond:
216+
msg += (
217+
" Note that the default `freq` may differ. Pass "
218+
"'freq=\"{}\"' to ensure the same output."
219+
).format(freq2.freqstr)
220+
warnings.warn(msg, FutureWarning, stacklevel=2)
221+
freq = freq2
222+
186223
data = PeriodArray(data, freq=freq)
187224
else:
188225
freq = validate_dtype_freq(dtype, freq)
@@ -939,7 +976,7 @@ def base(self):
939976
PeriodIndex._add_datetimelike_methods()
940977

941978

942-
def period_range(start=None, end=None, periods=None, freq='D', name=None):
979+
def period_range(start=None, end=None, periods=None, freq=None, name=None):
943980
"""
944981
Return a fixed frequency PeriodIndex, with day (calendar) as the default
945982
frequency
@@ -952,8 +989,11 @@ def period_range(start=None, end=None, periods=None, freq='D', name=None):
952989
Right bound for generating periods
953990
periods : integer, default None
954991
Number of periods to generate
955-
freq : string or DateOffset, default 'D'
956-
Frequency alias
992+
freq : string or DateOffset, optional
993+
Frequency alias. By default the freq is taken from `start` or `end`
994+
if those are Period objects. Otherwise, the default is ``"D"`` for
995+
daily frequency.
996+
957997
name : string, default None
958998
Name of the resulting PeriodIndex
959999
@@ -990,6 +1030,11 @@ def period_range(start=None, end=None, periods=None, freq='D', name=None):
9901030
if com.count_not_none(start, end, periods) != 2:
9911031
raise ValueError('Of the three parameters: start, end, and periods, '
9921032
'exactly two must be specified')
993-
994-
return PeriodIndex(start=start, end=end, periods=periods,
995-
freq=freq, name=name)
1033+
if freq is None and (not isinstance(start, Period)
1034+
and not isinstance(end, Period)):
1035+
freq = 'D'
1036+
1037+
data, freq = PeriodArray._generate_range(start, end, periods, freq,
1038+
fields={})
1039+
data = PeriodArray(data, freq=freq)
1040+
return PeriodIndex(data, name=name)

pandas/core/indexes/timedeltas.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,27 @@ class TimedeltaIndex(TimedeltaArray, DatetimeIndexOpsMixin,
9797
start : starting value, timedelta-like, optional
9898
If data is None, start is used as the start point in generating regular
9999
timedelta data.
100+
101+
.. deprecated:: 0.24.0
102+
100103
periods : int, optional, > 0
101104
Number of periods to generate, if generating index. Takes precedence
102105
over end argument
103-
end : end time, timedelta-like, optional
106+
107+
.. deprecated:: 0.24.0
108+
109+
end : end time, timedelta-like, optional
104110
If periods is none, generated index will extend to first conforming
105111
time on or just past end argument
112+
113+
.. deprecated:: 0.24. 0
114+
106115
closed : string or None, default None
107116
Make the interval closed with respect to the given frequency to
108117
the 'left', 'right', or both sides (None)
118+
119+
.. deprecated:: 0.24. 0
120+
109121
name : object
110122
Name to be stored in the index
111123
@@ -133,12 +145,15 @@ class TimedeltaIndex(TimedeltaArray, DatetimeIndexOpsMixin,
133145
Timedelta : Represents a duration between two dates or times.
134146
DatetimeIndex : Index of datetime64 data.
135147
PeriodIndex : Index of Period data.
148+
timedelta_range : Create a fixed-frequency TimedeltaIndex.
136149
137150
Notes
138151
-----
139-
140152
To learn more about the frequency strings, please see `this link
141153
<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
154+
155+
Creating a TimedeltaIndex based on `start`, `periods`, and `end` has
156+
been deprecated in favor of :func:`timedelta_range`.
142157
"""
143158

144159
_typ = 'timedeltaindex'

pandas/core/resample.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1491,10 +1491,10 @@ def _get_time_period_bins(self, ax):
14911491
binner = labels = PeriodIndex(data=[], freq=freq, name=ax.name)
14921492
return binner, [], labels
14931493

1494-
labels = binner = PeriodIndex(start=ax[0],
1495-
end=ax[-1],
1496-
freq=freq,
1497-
name=ax.name)
1494+
labels = binner = pd.period_range(start=ax[0],
1495+
end=ax[-1],
1496+
freq=freq,
1497+
name=ax.name)
14981498

14991499
end_stamps = (labels + freq).asfreq(freq, 's').to_timestamp()
15001500
if ax.tzinfo:
@@ -1543,8 +1543,8 @@ def _get_period_bins(self, ax):
15431543
bin_shift = start_offset.n % freq_mult
15441544
start = p_start
15451545

1546-
labels = binner = PeriodIndex(start=start, end=end,
1547-
freq=self.freq, name=ax.name)
1546+
labels = binner = pd.period_range(start=start, end=end,
1547+
freq=self.freq, name=ax.name)
15481548

15491549
i8 = memb.asi8
15501550

pandas/plotting/_converter.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import pandas.core.common as com
2323
from pandas.core.index import Index
2424
from pandas.core.indexes.datetimes import date_range
25-
from pandas.core.indexes.period import Period, PeriodIndex
25+
from pandas.core.indexes.period import Period, PeriodIndex, period_range
2626
import pandas.core.tools.datetimes as tools
2727

2828
import pandas.tseries.frequencies as frequencies
@@ -630,7 +630,7 @@ def _daily_finder(vmin, vmax, freq):
630630
(vmin, vmax) = (Period(ordinal=int(vmin), freq=freq),
631631
Period(ordinal=int(vmax), freq=freq))
632632
span = vmax.ordinal - vmin.ordinal + 1
633-
dates_ = PeriodIndex(start=vmin, end=vmax, freq=freq)
633+
dates_ = period_range(start=vmin, end=vmax, freq=freq)
634634
# Initialize the output
635635
info = np.zeros(span,
636636
dtype=[('val', np.int64), ('maj', bool),

pandas/tests/frame/test_join.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pytest
44
import numpy as np
55

6-
from pandas import DataFrame, Index, PeriodIndex
6+
from pandas import DataFrame, Index, period_range
77
from pandas.tests.frame.common import TestData
88
import pandas.util.testing as tm
99

@@ -13,7 +13,7 @@ def frame_with_period_index():
1313
return DataFrame(
1414
data=np.arange(20).reshape(4, 5),
1515
columns=list('abcde'),
16-
index=PeriodIndex(start='2000', freq='A', periods=4))
16+
index=period_range(start='2000', freq='A', periods=4))
1717

1818

1919
@pytest.fixture

pandas/tests/frame/test_period.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def test_frame_setitem(self):
4444

4545
def test_frame_to_time_stamp(self):
4646
K = 5
47-
index = PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009')
47+
index = period_range(freq='A', start='1/1/2001', end='12/1/2009')
4848
df = DataFrame(randn(len(index), K), index=index)
4949
df['mix'] = 'a'
5050

0 commit comments

Comments
 (0)