Skip to content

Commit e68bfd0

Browse files
committed
Merge pull request #10951 from sinhrks/legacyoffset
DEPR: Deprecate legacy offsets
2 parents 18f1d77 + ebbfe87 commit e68bfd0

File tree

8 files changed

+280
-77
lines changed

8 files changed

+280
-77
lines changed

doc/source/timeseries.rst

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ apply the offset to each element.
656656
rng + DateOffset(months=2)
657657
s + DateOffset(months=2)
658658
s - DateOffset(months=2)
659-
659+
660660
If the offset class maps directly to a ``Timedelta`` (``Day``, ``Hour``,
661661
``Minute``, ``Second``, ``Micro``, ``Milli``, ``Nano``) it can be
662662
used exactly like a ``Timedelta`` - see the
@@ -670,7 +670,7 @@ used exactly like a ``Timedelta`` - see the
670670
td + Minute(15)
671671
672672
Note that some offsets (such as ``BQuarterEnd``) do not have a
673-
vectorized implementation. They can still be used but may
673+
vectorized implementation. They can still be used but may
674674
calculate signficantly slower and will raise a ``PerformanceWarning``
675675

676676
.. ipython:: python
@@ -882,10 +882,10 @@ frequencies. We will refer to these aliases as *offset aliases*
882882
"BAS", "business year start frequency"
883883
"BH", "business hour frequency"
884884
"H", "hourly frequency"
885-
"T", "minutely frequency"
885+
"T, min", "minutely frequency"
886886
"S", "secondly frequency"
887-
"L", "milliseonds"
888-
"U", "microseconds"
887+
"L, ms", "milliseonds"
888+
"U, us", "microseconds"
889889
"N", "nanoseconds"
890890

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

956+
.. _timeseries.legacyaliases:
957+
956958
Legacy Aliases
957959
~~~~~~~~~~~~~~
958-
Note that prior to v0.8.0, time rules had a slightly different look. pandas
959-
will continue to support the legacy time rules for the time being but it is
960-
strongly recommended that you switch to using the new offset aliases.
960+
Note that prior to v0.8.0, time rules had a slightly different look. These are
961+
deprecated in v0.17.0, and removed in future version.
961962

962963
.. csv-table::
963964
:header: "Legacy Time Rule", "Offset Alias"
@@ -987,9 +988,7 @@ strongly recommended that you switch to using the new offset aliases.
987988
"A\@OCT", "BA\-OCT"
988989
"A\@NOV", "BA\-NOV"
989990
"A\@DEC", "BA\-DEC"
990-
"min", "T"
991-
"ms", "L"
992-
"us", "U"
991+
993992

994993
As you can see, legacy quarterly and annual frequencies are business quarters
995994
and business year ends. Please also note the legacy time rule for milliseconds

doc/source/whatsnew/v0.17.0.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,7 @@ Deprecations
653653
``DataFrame.add(other, fill_value=0)`` and ``DataFrame.mul(other, fill_value=1.)``
654654
(:issue:`10735`).
655655
- ``TimeSeries`` deprecated in favor of ``Series`` (note that this has been alias since 0.13.0), (:issue:`10890`)
656+
- 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`)
656657
- ``WidePanel`` deprecated in favor of ``Panel``, ``LongPanel`` in favor of ``DataFrame`` (note these have been aliases since < 0.11.0), (:issue:`10892`)
657658

658659
.. _whatsnew_0170.prior_deprecations:

pandas/tseries/frequencies.py

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from pandas.compat import range, long, zip
33
from pandas import compat
44
import re
5+
import warnings
56

67
import numpy as np
78

@@ -335,29 +336,18 @@ def get_period_alias(offset_str):
335336
_rule_aliases = {
336337
# Legacy rules that will continue to map to their original values
337338
# essentially for the rest of time
338-
339339
'WEEKDAY': 'B',
340340
'EOM': 'BM',
341-
342341
'W@MON': 'W-MON',
343342
'W@TUE': 'W-TUE',
344343
'W@WED': 'W-WED',
345344
'W@THU': 'W-THU',
346345
'W@FRI': 'W-FRI',
347346
'W@SAT': 'W-SAT',
348347
'W@SUN': 'W-SUN',
349-
'W': 'W-SUN',
350-
351348
'Q@JAN': 'BQ-JAN',
352349
'Q@FEB': 'BQ-FEB',
353350
'Q@MAR': 'BQ-MAR',
354-
'Q': 'Q-DEC',
355-
356-
'A': 'A-DEC', # YearEnd(month=12),
357-
'AS': 'AS-JAN', # YearBegin(month=1),
358-
'BA': 'BA-DEC', # BYearEnd(month=12),
359-
'BAS': 'BAS-JAN', # BYearBegin(month=1),
360-
361351
'A@JAN': 'BA-JAN',
362352
'A@FEB': 'BA-FEB',
363353
'A@MAR': 'BA-MAR',
@@ -370,8 +360,17 @@ def get_period_alias(offset_str):
370360
'A@OCT': 'BA-OCT',
371361
'A@NOV': 'BA-NOV',
372362
'A@DEC': 'BA-DEC',
363+
}
364+
365+
_lite_rule_alias = {
366+
'W': 'W-SUN',
367+
'Q': 'Q-DEC',
368+
369+
'A': 'A-DEC', # YearEnd(month=12),
370+
'AS': 'AS-JAN', # YearBegin(month=1),
371+
'BA': 'BA-DEC', # BYearEnd(month=12),
372+
'BAS': 'BAS-JAN', # BYearBegin(month=1),
373373

374-
# lite aliases
375374
'Min': 'T',
376375
'min': 'T',
377376
'ms': 'L',
@@ -386,6 +385,7 @@ def get_period_alias(offset_str):
386385

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

@@ -501,6 +501,9 @@ def get_base_alias(freqstr):
501501
_dont_uppercase = set(('MS', 'ms'))
502502

503503

504+
_LEGACY_FREQ_WARNING = 'Freq "{0}" is deprecated, use "{1}" as alternative.'
505+
506+
504507
def get_offset(name):
505508
"""
506509
Return DateOffset object associated with rule name
@@ -513,12 +516,26 @@ def get_offset(name):
513516
name = name.upper()
514517

515518
if name in _rule_aliases:
516-
name = _rule_aliases[name]
519+
new = _rule_aliases[name]
520+
warnings.warn(_LEGACY_FREQ_WARNING.format(name, new),
521+
FutureWarning)
522+
name = new
517523
elif name.lower() in _rule_aliases:
518-
name = _rule_aliases[name.lower()]
524+
new = _rule_aliases[name.lower()]
525+
warnings.warn(_LEGACY_FREQ_WARNING.format(name, new),
526+
FutureWarning)
527+
name = new
528+
529+
name = _lite_rule_alias.get(name, name)
530+
name = _lite_rule_alias.get(name.lower(), name)
531+
519532
else:
520533
if name in _rule_aliases:
521-
name = _rule_aliases[name]
534+
new = _rule_aliases[name]
535+
warnings.warn(_LEGACY_FREQ_WARNING.format(name, new),
536+
FutureWarning)
537+
name = new
538+
name = _lite_rule_alias.get(name, name)
522539

523540
if name not in _offset_map:
524541
try:
@@ -561,6 +578,9 @@ def get_legacy_offset_name(offset):
561578
"""
562579
Return the pre pandas 0.8.0 name for the date offset
563580
"""
581+
582+
# This only used in test_timeseries_legacy.py
583+
564584
name = offset.name
565585
return _legacy_reverse_map.get(name, name)
566586

@@ -754,10 +774,21 @@ def _period_alias_dictionary():
754774

755775
def _period_str_to_code(freqstr):
756776
# hack
757-
freqstr = _rule_aliases.get(freqstr, freqstr)
777+
if freqstr in _rule_aliases:
778+
new = _rule_aliases[freqstr]
779+
warnings.warn(_LEGACY_FREQ_WARNING.format(freqstr, new),
780+
FutureWarning)
781+
freqstr = new
782+
freqstr = _lite_rule_alias.get(freqstr, freqstr)
758783

759784
if freqstr not in _dont_uppercase:
760-
freqstr = _rule_aliases.get(freqstr.lower(), freqstr)
785+
lower = freqstr.lower()
786+
if lower in _rule_aliases:
787+
new = _rule_aliases[lower]
788+
warnings.warn(_LEGACY_FREQ_WARNING.format(lower, new),
789+
FutureWarning)
790+
freqstr = new
791+
freqstr = _lite_rule_alias.get(lower, freqstr)
761792

762793
try:
763794
if freqstr not in _dont_uppercase:
@@ -766,6 +797,8 @@ def _period_str_to_code(freqstr):
766797
except KeyError:
767798
try:
768799
alias = _period_alias_dict[freqstr]
800+
warnings.warn(_LEGACY_FREQ_WARNING.format(freqstr, alias),
801+
FutureWarning)
769802
except KeyError:
770803
raise ValueError("Unknown freqstr: %s" % freqstr)
771804

pandas/tseries/offsets.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2650,14 +2650,13 @@ def generate_range(start=None, end=None, periods=None,
26502650

26512651
prefix_mapping['N'] = Nano
26522652

2653-
26542653
def _make_offset(key):
26552654
"""Gets offset based on key. KeyError if prefix is bad, ValueError if
26562655
suffix is bad. All handled by `get_offset` in tseries/frequencies. Not
26572656
public."""
26582657
if key is None:
26592658
return None
2660-
split = key.replace('@', '-').split('-')
2659+
split = key.split('-')
26612660
klass = prefix_mapping[split[0]]
26622661
# handles case where there's no suffix (and will TypeError if too many '-')
26632662
obj = klass._from_name(*split[1:])

pandas/tseries/tests/test_frequencies.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,9 +529,13 @@ def test_series(self):
529529
self.assertRaises(ValueError, lambda : frequencies.infer_freq(Series(['foo','bar'])))
530530

531531
# cannot infer on PeriodIndex
532-
for freq in [None, 'L', 'Y']:
532+
for freq in [None, 'L']:
533533
s = Series(period_range('2013',periods=10,freq=freq))
534534
self.assertRaises(TypeError, lambda : frequencies.infer_freq(s))
535+
for freq in ['Y']:
536+
with tm.assert_produces_warning(FutureWarning):
537+
s = Series(period_range('2013',periods=10,freq=freq))
538+
self.assertRaises(TypeError, lambda : frequencies.infer_freq(s))
535539

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

550+
def test_legacy_offset_warnings(self):
551+
for k, v in compat.iteritems(frequencies._rule_aliases):
552+
with tm.assert_produces_warning(FutureWarning):
553+
result = frequencies.get_offset(k)
554+
exp = frequencies.get_offset(v)
555+
self.assertEqual(result, exp)
556+
557+
with tm.assert_produces_warning(FutureWarning):
558+
idx = date_range('2011-01-01', periods=5, freq=k)
559+
exp = date_range('2011-01-01', periods=5, freq=v)
560+
self.assert_index_equal(idx, exp)
561+
562+
546563
MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP',
547564
'OCT', 'NOV', 'DEC']
548565

pandas/tseries/tests/test_offsets.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3615,7 +3615,6 @@ def test_get_offset():
36153615
('Bm', BMonthEnd()), ('W-MON', Week(weekday=0)),
36163616
('W-TUE', Week(weekday=1)), ('W-WED', Week(weekday=2)),
36173617
('W-THU', Week(weekday=3)), ('W-FRI', Week(weekday=4)),
3618-
('w@Sat', Week(weekday=5)),
36193618
("RE-N-DEC-MON", makeFY5253NearestEndMonth(weekday=0, startingMonth=12)),
36203619
("RE-L-DEC-TUE", makeFY5253LastOfMonth(weekday=1, startingMonth=12)),
36213620
("REQ-L-MAR-TUE-4", makeFY5253LastOfMonthQuarter(weekday=1, startingMonth=3, qtr_with_extra_week=4)),
@@ -3628,6 +3627,13 @@ def test_get_offset():
36283627
assert offset == expected, ("Expected %r to yield %r (actual: %r)" %
36293628
(name, expected, offset))
36303629

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

36323638
class TestParseTimeString(tm.TestCase):
36333639

@@ -3663,11 +3669,18 @@ def test_get_standard_freq():
36633669
assert fstr == get_standard_freq('w')
36643670
assert fstr == get_standard_freq('1w')
36653671
assert fstr == get_standard_freq(('W', 1))
3666-
assert fstr == get_standard_freq('WeEk')
3672+
3673+
with tm.assert_produces_warning(FutureWarning):
3674+
result = get_standard_freq('WeEk')
3675+
assert fstr == result
36673676

36683677
fstr = get_standard_freq('5Q')
36693678
assert fstr == get_standard_freq('5q')
3670-
assert fstr == get_standard_freq('5QuarTer')
3679+
3680+
with tm.assert_produces_warning(FutureWarning):
3681+
result = get_standard_freq('5QuarTer')
3682+
assert fstr == result
3683+
36713684
assert fstr == get_standard_freq(('q', 5))
36723685

36733686

0 commit comments

Comments
 (0)