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: Deprecate legacy offsets #10951

Merged
merged 1 commit into from
Sep 1, 2015
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
21 changes: 10 additions & 11 deletions doc/source/timeseries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ apply the offset to each element.
rng + DateOffset(months=2)
s + DateOffset(months=2)
s - DateOffset(months=2)

If the offset class maps directly to a ``Timedelta`` (``Day``, ``Hour``,
``Minute``, ``Second``, ``Micro``, ``Milli``, ``Nano``) it can be
used exactly like a ``Timedelta`` - see the
Expand All @@ -670,7 +670,7 @@ used exactly like a ``Timedelta`` - see the
td + Minute(15)

Note that some offsets (such as ``BQuarterEnd``) do not have a
vectorized implementation. They can still be used but may
vectorized implementation. They can still be used but may
calculate signficantly slower and will raise a ``PerformanceWarning``

.. ipython:: python
Expand Down Expand Up @@ -882,10 +882,10 @@ frequencies. We will refer to these aliases as *offset aliases*
"BAS", "business year start frequency"
"BH", "business hour frequency"
"H", "hourly frequency"
"T", "minutely frequency"
"T, min", "minutely frequency"
"S", "secondly frequency"
"L", "milliseonds"
"U", "microseconds"
"L, ms", "milliseonds"
"U, us", "microseconds"
"N", "nanoseconds"

Combining Aliases
Expand Down Expand Up @@ -953,11 +953,12 @@ These can be used as arguments to ``date_range``, ``bdate_range``, constructors
for ``DatetimeIndex``, as well as various other timeseries-related functions
in pandas.

.. _timeseries.legacyaliases:

Legacy Aliases
~~~~~~~~~~~~~~
Note that prior to v0.8.0, time rules had a slightly different look. pandas
will continue to support the legacy time rules for the time being but it is
strongly recommended that you switch to using the new offset aliases.
Note that prior to v0.8.0, time rules had a slightly different look. These are
deprecated in v0.17.0, and removed in future version.

.. csv-table::
:header: "Legacy Time Rule", "Offset Alias"
Expand Down Expand Up @@ -987,9 +988,7 @@ strongly recommended that you switch to using the new offset aliases.
"A\@OCT", "BA\-OCT"
"A\@NOV", "BA\-NOV"
"A\@DEC", "BA\-DEC"
"min", "T"
"ms", "L"
"us", "U"


As you can see, legacy quarterly and annual frequencies are business quarters
and business year ends. Please also note the legacy time rule for milliseconds
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.17.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ Deprecations
``DataFrame.add(other, fill_value=0)`` and ``DataFrame.mul(other, fill_value=1.)``
(:issue:`10735`).
- ``TimeSeries`` deprecated in favor of ``Series`` (note that this has been alias since 0.13.0), (:issue:`10890`)
- Legacy offsets (like ``'A@JAN'``) listed in :ref:`here <timeseries.legacyaliases>` are deprecated (note that this has been alias since 0.8.0), (:issue:`10878`)
- ``WidePanel`` deprecated in favor of ``Panel``, ``LongPanel`` in favor of ``DataFrame`` (note these have been aliases since < 0.11.0), (:issue:`10892`)

.. _whatsnew_0170.prior_deprecations:
Expand Down
67 changes: 50 additions & 17 deletions pandas/tseries/frequencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pandas.compat import range, long, zip
from pandas import compat
import re
import warnings

import numpy as np

Expand Down Expand Up @@ -335,29 +336,18 @@ def get_period_alias(offset_str):
_rule_aliases = {
# Legacy rules that will continue to map to their original values
# essentially for the rest of time

'WEEKDAY': 'B',
'EOM': 'BM',

'W@MON': 'W-MON',
'W@TUE': 'W-TUE',
'W@WED': 'W-WED',
'W@THU': 'W-THU',
'W@FRI': 'W-FRI',
'W@SAT': 'W-SAT',
'W@SUN': 'W-SUN',
'W': 'W-SUN',

'Q@JAN': 'BQ-JAN',
'Q@FEB': 'BQ-FEB',
'Q@MAR': 'BQ-MAR',
'Q': 'Q-DEC',

'A': 'A-DEC', # YearEnd(month=12),
'AS': 'AS-JAN', # YearBegin(month=1),
'BA': 'BA-DEC', # BYearEnd(month=12),
'BAS': 'BAS-JAN', # BYearBegin(month=1),

'A@JAN': 'BA-JAN',
'A@FEB': 'BA-FEB',
'A@MAR': 'BA-MAR',
Expand All @@ -370,8 +360,17 @@ def get_period_alias(offset_str):
'A@OCT': 'BA-OCT',
'A@NOV': 'BA-NOV',
'A@DEC': 'BA-DEC',
}

_lite_rule_alias = {
'W': 'W-SUN',
'Q': 'Q-DEC',

'A': 'A-DEC', # YearEnd(month=12),
'AS': 'AS-JAN', # YearBegin(month=1),
'BA': 'BA-DEC', # BYearEnd(month=12),
'BAS': 'BAS-JAN', # BYearBegin(month=1),

# lite aliases
'Min': 'T',
'min': 'T',
'ms': 'L',
Expand All @@ -386,6 +385,7 @@ def get_period_alias(offset_str):

# Note that _rule_aliases is not 1:1 (d[BA]==d[A@DEC]), and so traversal
# order matters when constructing an inverse. we pick one. #2331
# Used in get_legacy_offset_name
_legacy_reverse_map = dict((v, k) for k, v in
reversed(sorted(compat.iteritems(_rule_aliases))))

Expand Down Expand Up @@ -501,6 +501,9 @@ def get_base_alias(freqstr):
_dont_uppercase = set(('MS', 'ms'))


_LEGACY_FREQ_WARNING = 'Freq "{0}" is deprecated, use "{1}" as alternative.'


def get_offset(name):
"""
Return DateOffset object associated with rule name
Expand All @@ -513,12 +516,26 @@ def get_offset(name):
name = name.upper()

if name in _rule_aliases:
name = _rule_aliases[name]
new = _rule_aliases[name]
warnings.warn(_LEGACY_FREQ_WARNING.format(name, new),
FutureWarning)
name = new
elif name.lower() in _rule_aliases:
name = _rule_aliases[name.lower()]
new = _rule_aliases[name.lower()]
warnings.warn(_LEGACY_FREQ_WARNING.format(name, new),
FutureWarning)
name = new

name = _lite_rule_alias.get(name, name)
name = _lite_rule_alias.get(name.lower(), name)

else:
if name in _rule_aliases:
name = _rule_aliases[name]
new = _rule_aliases[name]
warnings.warn(_LEGACY_FREQ_WARNING.format(name, new),
FutureWarning)
name = new
name = _lite_rule_alias.get(name, name)

if name not in _offset_map:
try:
Expand Down Expand Up @@ -561,6 +578,9 @@ def get_legacy_offset_name(offset):
"""
Return the pre pandas 0.8.0 name for the date offset
"""

# This only used in test_timeseries_legacy.py

name = offset.name
return _legacy_reverse_map.get(name, name)

Expand Down Expand Up @@ -754,10 +774,21 @@ def _period_alias_dictionary():

def _period_str_to_code(freqstr):
# hack
freqstr = _rule_aliases.get(freqstr, freqstr)
if freqstr in _rule_aliases:
new = _rule_aliases[freqstr]
warnings.warn(_LEGACY_FREQ_WARNING.format(freqstr, new),
FutureWarning)
freqstr = new
freqstr = _lite_rule_alias.get(freqstr, freqstr)

if freqstr not in _dont_uppercase:
freqstr = _rule_aliases.get(freqstr.lower(), freqstr)
lower = freqstr.lower()
if lower in _rule_aliases:
new = _rule_aliases[lower]
warnings.warn(_LEGACY_FREQ_WARNING.format(lower, new),
FutureWarning)
freqstr = new
freqstr = _lite_rule_alias.get(lower, freqstr)

try:
if freqstr not in _dont_uppercase:
Expand All @@ -766,6 +797,8 @@ def _period_str_to_code(freqstr):
except KeyError:
try:
alias = _period_alias_dict[freqstr]
warnings.warn(_LEGACY_FREQ_WARNING.format(freqstr, alias),
FutureWarning)
except KeyError:
raise ValueError("Unknown freqstr: %s" % freqstr)

Expand Down
3 changes: 1 addition & 2 deletions pandas/tseries/offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2650,14 +2650,13 @@ def generate_range(start=None, end=None, periods=None,

prefix_mapping['N'] = Nano


def _make_offset(key):
"""Gets offset based on key. KeyError if prefix is bad, ValueError if
suffix is bad. All handled by `get_offset` in tseries/frequencies. Not
public."""
if key is None:
return None
split = key.replace('@', '-').split('-')
split = key.split('-')
klass = prefix_mapping[split[0]]
# handles case where there's no suffix (and will TypeError if too many '-')
obj = klass._from_name(*split[1:])
Expand Down
19 changes: 18 additions & 1 deletion pandas/tseries/tests/test_frequencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,9 +529,13 @@ def test_series(self):
self.assertRaises(ValueError, lambda : frequencies.infer_freq(Series(['foo','bar'])))

# cannot infer on PeriodIndex
for freq in [None, 'L', 'Y']:
for freq in [None, 'L']:
s = Series(period_range('2013',periods=10,freq=freq))
self.assertRaises(TypeError, lambda : frequencies.infer_freq(s))
for freq in ['Y']:
with tm.assert_produces_warning(FutureWarning):
s = Series(period_range('2013',periods=10,freq=freq))
self.assertRaises(TypeError, lambda : frequencies.infer_freq(s))

# DateTimeIndex
for freq in ['M', 'L', 'S']:
Expand All @@ -543,6 +547,19 @@ def test_series(self):
inferred = frequencies.infer_freq(s)
self.assertEqual(inferred,'D')

def test_legacy_offset_warnings(self):
for k, v in compat.iteritems(frequencies._rule_aliases):
with tm.assert_produces_warning(FutureWarning):
result = frequencies.get_offset(k)
exp = frequencies.get_offset(v)
self.assertEqual(result, exp)

with tm.assert_produces_warning(FutureWarning):
idx = date_range('2011-01-01', periods=5, freq=k)
exp = date_range('2011-01-01', periods=5, freq=v)
self.assert_index_equal(idx, exp)


MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP',
'OCT', 'NOV', 'DEC']

Expand Down
19 changes: 16 additions & 3 deletions pandas/tseries/tests/test_offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3615,7 +3615,6 @@ def test_get_offset():
('Bm', BMonthEnd()), ('W-MON', Week(weekday=0)),
('W-TUE', Week(weekday=1)), ('W-WED', Week(weekday=2)),
('W-THU', Week(weekday=3)), ('W-FRI', Week(weekday=4)),
('w@Sat', Week(weekday=5)),
("RE-N-DEC-MON", makeFY5253NearestEndMonth(weekday=0, startingMonth=12)),
("RE-L-DEC-TUE", makeFY5253LastOfMonth(weekday=1, startingMonth=12)),
("REQ-L-MAR-TUE-4", makeFY5253LastOfMonthQuarter(weekday=1, startingMonth=3, qtr_with_extra_week=4)),
Expand All @@ -3628,6 +3627,13 @@ def test_get_offset():
assert offset == expected, ("Expected %r to yield %r (actual: %r)" %
(name, expected, offset))

def test_get_offset_legacy():
pairs = [('w@Sat', Week(weekday=5))]
for name, expected in pairs:
with tm.assert_produces_warning(FutureWarning):
offset = get_offset(name)
assert offset == expected, ("Expected %r to yield %r (actual: %r)" %
(name, expected, offset))

class TestParseTimeString(tm.TestCase):

Expand Down Expand Up @@ -3663,11 +3669,18 @@ def test_get_standard_freq():
assert fstr == get_standard_freq('w')
assert fstr == get_standard_freq('1w')
assert fstr == get_standard_freq(('W', 1))
assert fstr == get_standard_freq('WeEk')

with tm.assert_produces_warning(FutureWarning):
result = get_standard_freq('WeEk')
assert fstr == result

fstr = get_standard_freq('5Q')
assert fstr == get_standard_freq('5q')
assert fstr == get_standard_freq('5QuarTer')

with tm.assert_produces_warning(FutureWarning):
result = get_standard_freq('5QuarTer')
assert fstr == result

assert fstr == get_standard_freq(('q', 5))


Expand Down
Loading