Skip to content

Commit 4c0a181

Browse files
Adam GleaveAdam Gleave
Adam Gleave
authored and
Adam Gleave
committed
Fixes GH16624
1 parent b72519e commit 4c0a181

File tree

4 files changed

+22
-5
lines changed

4 files changed

+22
-5
lines changed

doc/source/whatsnew/v0.20.3.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Plotting
6666
Groupby/Resample/Rolling
6767
^^^^^^^^^^^^^^^^^^^^^^^^
6868

69-
69+
- Bug in ``infer_freq`` causing indices with 2-day gaps during the working week to be wrongly inferred as business daily (:issue:`16624`)
7070

7171
Sparse
7272
^^^^^^

pandas/tests/indexes/timedeltas/test_timedelta.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ class TestSlicing(object):
567567

568568
def test_timedelta(self):
569569
# this is valid too
570-
index = date_range('1/1/2000', periods=50, freq='B')
570+
index = date_range('1/1/2000', periods=50, freq='D')
571571
shifted = index + timedelta(1)
572572
back = shifted + timedelta(-1)
573573
assert tm.equalContents(index, back)

pandas/tests/tseries/test_frequencies.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -504,9 +504,14 @@ def test_raise_if_too_few(self):
504504
pytest.raises(ValueError, frequencies.infer_freq, index)
505505

506506
def test_business_daily(self):
507-
index = _dti(['12/31/1998', '1/3/1999', '1/4/1999'])
507+
index = _dti(['01/01/1999', '1/4/1999', '1/5/1999'])
508508
assert frequencies.infer_freq(index) == 'B'
509509

510+
def test_business_daily_look_alike(self):
511+
# 'weekend' (2-day gap) in wrong place
512+
index = _dti(['12/31/1998', '1/3/1999', '1/4/1999'])
513+
assert frequencies.infer_freq(index) is None
514+
510515
def test_day(self):
511516
self._check_tick(timedelta(1), 'D')
512517

pandas/tseries/frequencies.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -975,8 +975,7 @@ def _infer_daily_rule(self):
975975
else:
976976
return _maybe_add_count('D', days)
977977

978-
# Business daily. Maybe
979-
if self.day_deltas == [1, 3]:
978+
if self._is_business_daily():
980979
return 'B'
981980

982981
wom_rule = self._get_wom_rule()
@@ -1012,6 +1011,19 @@ def _get_monthly_rule(self):
10121011
return {'cs': 'MS', 'bs': 'BMS',
10131012
'ce': 'M', 'be': 'BM'}.get(pos_check)
10141013

1014+
WORKING_DAY_SHIFTS = set([(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)])
1015+
1016+
def _is_business_daily(self):
1017+
if self.day_deltas != [1, 3]: # quick check: cannot be business daily
1018+
return False
1019+
# probably business daily, but need to confirm
1020+
first_weekday = self.index[0].weekday()
1021+
shifts = np.diff(np.asarray(self.index).view('i8'))
1022+
shifts = np.floor_divide(shifts, _ONE_DAY)
1023+
weekdays = np.mod(first_weekday + np.cumsum(shifts), 7)
1024+
return np.all(((weekdays == 0) & (shifts == 3)) |
1025+
((weekdays > 0) & (weekdays <= 4) & (shifts == 1)))
1026+
10151027
def _get_wom_rule(self):
10161028
# wdiffs = unique(np.diff(self.index.week))
10171029
# We also need -47, -49, -48 to catch index spanning year boundary

0 commit comments

Comments
 (0)