Skip to content

DEPR: Deprecate range-based PeriodIndex construction #24354

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

Merged
merged 13 commits into from
Dec 28, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion doc/source/whatsnew/v0.24.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ Deprecations
- 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`)
- :func:`pandas.api.types.is_period` is deprecated in favor of `pandas.api.types.is_period_dtype` (:issue:`23917`)
- :func:`pandas.api.types.is_datetimetz` is deprecated in favor of `pandas.api.types.is_datetime64tz` (:issue:`23917`)
- 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`)
- 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`)
- 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`).
- 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`).
- :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`)
Expand Down Expand Up @@ -1313,6 +1313,7 @@ Datetimelike
- 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`)
- Bug in :func:`DataFrame.combine` with datetimelike values raising a TypeError (:issue:`23079`)
- 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`)
- Bug in :func:`period_range` ignoring the frequency of ``start`` and ``end`` when those are provided as :class:`Period` objects (:issue:`20535`).
- Bug in :class:`PeriodIndex` with attribute ``freq.n`` greater than 1 where adding a :class:`DateOffset` object would return incorrect results (:issue:`23215`)
- Bug in :class:`Series` that interpreted string indices as lists of characters when setting datetimelike values (:issue:`23451`)
- Bug in :class:`Timestamp` constructor which would drop the frequency of an input :class:`Timestamp` (:issue:`22311`)
Expand Down
26 changes: 21 additions & 5 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,27 @@ class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index):
start : starting value, datetime-like, optional
If data is None, start is used as the start point in generating regular
timestamp data.

.. deprecated:: 0.24.0

periods : int, optional, > 0
Number of periods to generate, if generating index. Takes precedence
over end argument
end : end time, datetime-like, optional

.. deprecated:: 0.24.0

end : end time, datetime-like, optional
If periods is none, generated index will extend to first conforming
time on or just past end argument

.. deprecated:: 0.24.0

closed : string or None, default None
Make the interval closed with respect to the given frequency to
the 'left', 'right', or both sides (None)

.. deprecated:: 0.24. 0

tz : pytz.timezone or dateutil.tz.tzfile
ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
When clocks moved backward due to DST, ambiguous times may arise.
Expand Down Expand Up @@ -166,12 +178,16 @@ class DatetimeIndex(DatetimeArray, DatetimeIndexOpsMixin, Int64Index):
To learn more about the frequency strings, please see `this link
<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

Creating a DatetimeIndex based on `start`, `periods`, and `end` has
been deprecated in favor of :func:`date_range`.

See Also
---------
Index : The base pandas Index type.
TimedeltaIndex : Index of timedelta64 data.
PeriodIndex : Index of Period data.
pandas.to_datetime : Convert argument to datetime.
to_datetime : Convert argument to datetime.
date_range : Create a fixed-frequency DatetimeIndex.
"""
_typ = 'datetimeindex'
_join_precedence = 10
Expand Down Expand Up @@ -223,13 +239,13 @@ def __new__(cls, data=None,
verify_integrity = True

if data is None:
result = cls._generate_range(start, end, periods,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, if you passed a bad set of arguments (e.g. forgot freq), then we would warn and then raise an exception.

freq=freq, tz=tz, normalize=normalize,
closed=closed, ambiguous=ambiguous)
warnings.warn("Creating a DatetimeIndex by passing range "
"endpoints is deprecated. Use "
"`pandas.date_range` instead.",
FutureWarning, stacklevel=2)
result = cls._generate_range(start, end, periods,
freq=freq, tz=tz, normalize=normalize,
closed=closed, ambiguous=ambiguous)
result.name = name
return result

Expand Down
40 changes: 33 additions & 7 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,21 @@ class PeriodIndex(DatelikeOps, DatetimeIndexOpsMixin, Int64Index,
start : starting value, period-like, optional
If data is None, used as the start point in generating regular
period data.

.. deprecated:: 0.24.0

periods : int, optional, > 0
Number of periods to generate, if generating index. Takes precedence
over end argument

.. deprecated:: 0.24.0

end : end value, period-like, optional
If periods is none, generated index will extend to first conforming
period on or just past end argument

.. deprecated:: 0.24.0

year : int, array, or Series, default None
month : int, array, or Series, default None
quarter : int, array, or Series, default None
Expand Down Expand Up @@ -138,18 +147,22 @@ class PeriodIndex(DatelikeOps, DatetimeIndexOpsMixin, Int64Index,
strftime
to_timestamp

Notes
-----
Creating a PeriodIndex based on `start`, `periods`, and `end` has
been deprecated in favor of :func:`period_range`.

Examples
--------
>>> idx = pd.PeriodIndex(year=year_arr, quarter=q_arr)

>>> idx2 = pd.PeriodIndex(start='2000', end='2010', freq='A')

See Also
---------
Index : The base pandas Index type.
Period : Represents a period of time.
DatetimeIndex : Index with datetime64 data.
TimedeltaIndex : Index of timedelta64 data.
period_range : Create a fixed-frequency PeriodIndex.
"""
_typ = 'periodindex'
_attributes = ['name', 'freq']
Expand Down Expand Up @@ -183,6 +196,14 @@ def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
# range-based.
data, freq = PeriodArray._generate_range(start, end, periods,
freq, fields)
# PeriodArray._generate range does validate that fields is
# empty when really using the range-based constructor.
if not fields:
warnings.warn("Creating a PeriodIndex by passing range "
"endpoints is deprecated. Use "
"`pandas.period_range` instead.",
FutureWarning, stacklevel=2)

data = PeriodArray(data, freq=freq)
else:
freq = validate_dtype_freq(dtype, freq)
Expand Down Expand Up @@ -983,7 +1004,7 @@ def base(self):
PeriodIndex._add_datetimelike_methods()


def period_range(start=None, end=None, periods=None, freq='D', name=None):
def period_range(start=None, end=None, periods=None, freq=None, name=None):
"""
Return a fixed frequency PeriodIndex, with day (calendar) as the default
frequency
Expand All @@ -996,8 +1017,11 @@ def period_range(start=None, end=None, periods=None, freq='D', name=None):
Right bound for generating periods
periods : integer, default None
Number of periods to generate
freq : string or DateOffset, default 'D'
Frequency alias
freq : string or DateOffset, optional
Frequency alias. By default the freq is taken from `start` or `end`
if those are Period objects. Otherwise, the default is ``"D"`` for
daily frequency.

name : string, default None
Name of the resulting PeriodIndex

Expand Down Expand Up @@ -1035,5 +1059,7 @@ def period_range(start=None, end=None, periods=None, freq='D', name=None):
raise ValueError('Of the three parameters: start, end, and periods, '
'exactly two must be specified')

return PeriodIndex(start=start, end=end, periods=periods,
freq=freq, name=name)
data, freq = PeriodArray._generate_range(start, end, periods, freq,
fields={})
data = PeriodArray(data, freq=freq)
return PeriodIndex(data, name=name)
19 changes: 17 additions & 2 deletions pandas/core/indexes/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,27 @@ class TimedeltaIndex(TimedeltaArray, DatetimeIndexOpsMixin,
start : starting value, timedelta-like, optional
If data is None, start is used as the start point in generating regular
timedelta data.

.. deprecated:: 0.24.0

periods : int, optional, > 0
Number of periods to generate, if generating index. Takes precedence
over end argument
end : end time, timedelta-like, optional

.. deprecated:: 0.24.0

end : end time, timedelta-like, optional
If periods is none, generated index will extend to first conforming
time on or just past end argument

.. deprecated:: 0.24. 0

closed : string or None, default None
Make the interval closed with respect to the given frequency to
the 'left', 'right', or both sides (None)

.. deprecated:: 0.24. 0

name : object
Name to be stored in the index

Expand Down Expand Up @@ -104,12 +116,15 @@ class TimedeltaIndex(TimedeltaArray, DatetimeIndexOpsMixin,
Timedelta : Represents a duration between two dates or times.
DatetimeIndex : Index of datetime64 data.
PeriodIndex : Index of Period data.
timedelta_range : Create a fixed-frequency TimedeltaIndex.

Notes
-----

To learn more about the frequency strings, please see `this link
<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

Creating a TimedeltaIndex based on `start`, `periods`, and `end` has
been deprecated in favor of :func:`timedelta_range`.
"""

_typ = 'timedeltaindex'
Expand Down
28 changes: 14 additions & 14 deletions pandas/tests/indexes/period/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

import pandas as pd
from pandas import PeriodIndex
from pandas import PeriodIndex, period_range
import pandas.util.testing as tm


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

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

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

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

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

pi1 = PeriodIndex(freq='M', start='1/1/2001', end='12/1/2009')
pi2 = PeriodIndex(freq='M', start='2/1/2001', end='1/1/2010')
pi1 = period_range(freq='M', start='1/1/2001', end='12/1/2009')
pi2 = period_range(freq='M', start='2/1/2001', end='1/1/2010')
assert len(pi1) == len(pi2)
tm.assert_index_equal(pi1.shift(1), pi2)

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

pi1 = PeriodIndex(freq='D', start='1/1/2001', end='12/1/2009')
pi2 = PeriodIndex(freq='D', start='1/2/2001', end='12/2/2009')
pi1 = period_range(freq='D', start='1/1/2001', end='12/1/2009')
pi2 = period_range(freq='D', start='1/2/2001', end='12/2/2009')
assert len(pi1) == len(pi2)
tm.assert_index_equal(pi1.shift(1), pi2)

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

Expand Down Expand Up @@ -100,7 +100,7 @@ def test_shift_gh8083(self):

def test_shift_periods(self):
# GH #22458 : argument 'n' was deprecated in favor of 'periods'
idx = PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009')
idx = period_range(freq='A', start='1/1/2001', end='12/1/2009')
tm.assert_index_equal(idx.shift(periods=0), idx)
tm.assert_index_equal(idx.shift(0), idx)
with tm.assert_produces_warning(FutureWarning,
Expand Down
20 changes: 10 additions & 10 deletions pandas/tests/indexes/period/test_asfreq.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@
import pytest

import pandas as pd
from pandas import DataFrame, PeriodIndex, Series
from pandas import DataFrame, PeriodIndex, Series, period_range
from pandas.util import testing as tm


class TestPeriodIndex(object):

def test_asfreq(self):
pi1 = PeriodIndex(freq='A', start='1/1/2001', end='1/1/2001')
pi2 = PeriodIndex(freq='Q', start='1/1/2001', end='1/1/2001')
pi3 = PeriodIndex(freq='M', start='1/1/2001', end='1/1/2001')
pi4 = PeriodIndex(freq='D', start='1/1/2001', end='1/1/2001')
pi5 = PeriodIndex(freq='H', start='1/1/2001', end='1/1/2001 00:00')
pi6 = PeriodIndex(freq='Min', start='1/1/2001', end='1/1/2001 00:00')
pi7 = PeriodIndex(freq='S', start='1/1/2001', end='1/1/2001 00:00:00')
pi1 = period_range(freq='A', start='1/1/2001', end='1/1/2001')
pi2 = period_range(freq='Q', start='1/1/2001', end='1/1/2001')
pi3 = period_range(freq='M', start='1/1/2001', end='1/1/2001')
pi4 = period_range(freq='D', start='1/1/2001', end='1/1/2001')
pi5 = period_range(freq='H', start='1/1/2001', end='1/1/2001 00:00')
pi6 = period_range(freq='Min', start='1/1/2001', end='1/1/2001 00:00')
pi7 = period_range(freq='S', start='1/1/2001', end='1/1/2001 00:00:00')

assert pi1.asfreq('Q', 'S') == pi2
assert pi1.asfreq('Q', 's') == pi2
Expand Down Expand Up @@ -70,7 +70,7 @@ def test_asfreq(self):
pytest.raises(ValueError, pi7.asfreq, 'T', 'foo')
result1 = pi1.asfreq('3M')
result2 = pi1.asfreq('M')
expected = PeriodIndex(freq='M', start='2001-12', end='2001-12')
expected = period_range(freq='M', start='2001-12', end='2001-12')
tm.assert_numpy_array_equal(result1.asi8, expected.asi8)
assert result1.freqstr == '3M'
tm.assert_numpy_array_equal(result2.asi8, expected.asi8)
Expand Down Expand Up @@ -126,7 +126,7 @@ def test_asfreq_combined_pi(self):
assert result.freq == exp.freq

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

Expand Down
Loading