Skip to content

Commit

Permalink
DEPR: Deprecate legacy offsets
Browse files Browse the repository at this point in the history
  • Loading branch information
sinhrks committed Aug 31, 2015
1 parent b4ce396 commit ebbfe87
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 77 deletions.
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

0 comments on commit ebbfe87

Please sign in to comment.