Skip to content

BUG: Unable to infer negative freq #11018

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 1 commit into from
Sep 7, 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
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v0.17.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1009,4 +1009,6 @@ Bug Fixes
- Bug in ``.var()`` causing roundoff errors for highly similar values (:issue:`10242`)
- Bug in ``DataFrame.plot(subplots=True)`` with duplicated columns outputs incorrect result (:issue:`10962`)
- Bug in ``Index`` arithmetic may result in incorrect class (:issue:`10638`)
- Bug in ``date_range`` results in empty if freq is negative annualy, quarterly and monthly (:issue:`11018`)
- Bug in ``DatetimeIndex`` cannot infer negative freq (:issue:`11018`)

2 changes: 1 addition & 1 deletion pandas/tests/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -3329,7 +3329,7 @@ def test_ufunc_coercions(self):
exp = TimedeltaIndex(['-2H', '-4H', '-6H', '-8H', '-10H'],
freq='-2H', name='x')
tm.assert_index_equal(result, exp)
self.assertEqual(result.freq, None)
self.assertEqual(result.freq, '-2H')

idx = TimedeltaIndex(['-2H', '-1H', '0H', '1H', '2H'],
freq='H', name='x')
Expand Down
8 changes: 4 additions & 4 deletions pandas/tseries/frequencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,8 @@ def __init__(self, index, warn=True):
if len(index) < 3:
raise ValueError('Need at least 3 dates to infer frequency')

self.is_monotonic = self.index.is_monotonic
self.is_monotonic = (self.index.is_monotonic_increasing or
self.index.is_monotonic_decreasing)

@cache_readonly
def deltas(self):
Expand Down Expand Up @@ -971,7 +972,6 @@ def month_position_check(self):

from calendar import monthrange
for y, m, d, wd in zip(years, months, days, weekdays):
wd = datetime(y, m, d).weekday()

if calendar_start:
calendar_start &= d == 1
Expand Down Expand Up @@ -1025,7 +1025,7 @@ def _infer_daily_rule(self):

monthly_rule = self._get_monthly_rule()
if monthly_rule:
return monthly_rule
return _maybe_add_count(monthly_rule, self.mdiffs[0])

if self.is_unique:
days = self.deltas[0] / _ONE_DAY
Expand Down Expand Up @@ -1111,7 +1111,7 @@ def _infer_daily_rule(self):


def _maybe_add_count(base, count):
if count > 1:
if count != 1:
return '%d%s' % (count, base)
else:
return base
Expand Down
27 changes: 18 additions & 9 deletions pandas/tseries/offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2615,15 +2615,24 @@ def generate_range(start=None, end=None, periods=None,
start = end - (periods - 1) * offset

cur = start

while cur <= end:
yield cur

# faster than cur + offset
next_date = offset.apply(cur)
if next_date <= cur:
raise ValueError('Offset %s did not increment date' % offset)
cur = next_date
if offset.n >= 0:
while cur <= end:
yield cur

# faster than cur + offset
next_date = offset.apply(cur)
if next_date <= cur:
raise ValueError('Offset %s did not increment date' % offset)
cur = next_date
else:
while cur >= end:
yield cur

# faster than cur + offset
next_date = offset.apply(cur)
if next_date >= cur:
raise ValueError('Offset %s did not decrement date' % offset)
cur = next_date

prefix_mapping = dict((offset._prefix, offset) for offset in [
YearBegin, # 'AS'
Expand Down
17 changes: 17 additions & 0 deletions pandas/tseries/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,15 @@ def test_take(self):
self.assert_index_equal(result, expected)
self.assertIsNone(result.freq)

def test_infer_freq(self):
# GH 11018
for freq in ['A', '2A', '-2A', 'Q', '-1Q', 'M', '-1M', 'D', '3D', '-3D',
'W', '-1W', 'H', '2H', '-2H', 'T', '2T', 'S', '-3S']:
idx = pd.date_range('2011-01-01 09:00:00', freq=freq, periods=10)
result = pd.DatetimeIndex(idx.asi8, freq='infer')
tm.assert_index_equal(idx, result)
self.assertEqual(result.freq, freq)


class TestTimedeltaIndexOps(Ops):

Expand Down Expand Up @@ -1108,6 +1117,14 @@ def test_take(self):
self.assert_index_equal(result, expected)
self.assertIsNone(result.freq)

def test_infer_freq(self):
# GH 11018
for freq in ['D', '3D', '-3D', 'H', '2H', '-2H', 'T', '2T', 'S', '-3S']:
idx = pd.timedelta_range('1', freq=freq, periods=10)
result = pd.TimedeltaIndex(idx.asi8, freq='infer')
tm.assert_index_equal(idx, result)
self.assertEqual(result.freq, freq)


class TestPeriodIndexOps(Ops):

Expand Down
2 changes: 1 addition & 1 deletion pandas/tseries/tests/test_frequencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ def test_infer_freq_businesshour(self):
def test_not_monotonic(self):
rng = _dti(['1/31/2000', '1/31/2001', '1/31/2002'])
rng = rng[::-1]
self.assertIsNone(rng.inferred_freq)
self.assertEqual(rng.inferred_freq, '-1A-JAN')

def test_non_datetimeindex(self):
rng = _dti(['1/31/2000', '1/31/2001', '1/31/2002'])
Expand Down
4 changes: 1 addition & 3 deletions pandas/tseries/tests/test_timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1539,16 +1539,14 @@ def test_tdi_ops_attributes(self):
result = - rng
exp = timedelta_range('-2 days', periods=5, freq='-2D', name='x')
tm.assert_index_equal(result, exp)
# tdi doesn't infer negative freq
self.assertEqual(result.freq, None)
self.assertEqual(result.freq, '-2D')

rng = pd.timedelta_range('-2 days', periods=5, freq='D', name='x')

result = abs(rng)
exp = TimedeltaIndex(['2 days', '1 days', '0 days', '1 days',
'2 days'], name='x')
tm.assert_index_equal(result, exp)
# tdi doesn't infer negative freq
self.assertEqual(result.freq, None)


Expand Down
12 changes: 12 additions & 0 deletions pandas/tseries/tests/test_timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,18 @@ def test_date_range_gen_error(self):
rng = date_range('1/1/2000 00:00', '1/1/2000 00:18', freq='5min')
self.assertEqual(len(rng), 4)

def test_date_range_negative_freq(self):
# GH 11018
rng = date_range('2011-12-31', freq='-2A', periods=3)
exp = pd.DatetimeIndex(['2011-12-31', '2009-12-31', '2007-12-31'], freq='-2A')
self.assert_index_equal(rng, exp)
self.assertEqual(rng.freq, '-2A')

rng = date_range('2011-01-31', freq='-2M', periods=3)
exp = pd.DatetimeIndex(['2011-01-31', '2010-11-30', '2010-09-30'], freq='-2M')
self.assert_index_equal(rng, exp)
self.assertEqual(rng.freq, '-2M')

def test_first_subset(self):
ts = _simple_ts('1/1/2000', '1/1/2010', freq='12h')
result = ts.first('10d')
Expand Down