Skip to content

Commit 1aa4fce

Browse files
committed
Merge branch 'offset_cache' of https://github.com/cancan101/pandas into cancan101-offset_cache
Conflicts: doc/source/release.rst
2 parents d53e4fd + 25cfcaf commit 1aa4fce

File tree

4 files changed

+78
-22
lines changed

4 files changed

+78
-22
lines changed

doc/source/release.rst

+2
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ See :ref:`Internal Refactoring<whatsnew_0130.refactoring>`
275275
have internal setitem_with_indexer in core/indexing to use Block.setitem
276276
- Fixed bug where thousands operator was not handled correctly for floating point numbers
277277
in csv_import (:issue:`4322`)
278+
- Fix an issue with CacheableOffset not properly being used by many DateOffset; this prevented
279+
the DateOffset from being cached (:issue:`4609`)
278280

279281
pandas 0.12
280282
===========

pandas/tseries/index.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,7 @@ def _generate(cls, start, end, periods, name, offset,
346346
if end.tz is None and start.tz is not None:
347347
end = end.tz_localize(start.tz)
348348

349-
if (offset._should_cache() and
350-
not (offset._normalize_cache and not _normalized) and
351-
_naive_in_cache_range(start, end)):
349+
if _use_cached_range(offset, _normalized, start, end):
352350
index = cls._cached_range(start, end, periods=periods,
353351
offset=offset, name=name)
354352
else:
@@ -371,9 +369,7 @@ def _generate(cls, start, end, periods, name, offset,
371369
if end.tz is None and start.tz is not None:
372370
start = start.replace(tzinfo=None)
373371

374-
if (offset._should_cache() and
375-
not (offset._normalize_cache and not _normalized) and
376-
_naive_in_cache_range(start, end)):
372+
if _use_cached_range(offset, _normalized, start, end):
377373
index = cls._cached_range(start, end, periods=periods,
378374
offset=offset, name=name)
379375
else:
@@ -1851,6 +1847,10 @@ def _naive_in_cache_range(start, end):
18511847
def _in_range(start, end, rng_start, rng_end):
18521848
return start > rng_start and end < rng_end
18531849

1850+
def _use_cached_range(offset, _normalized, start, end):
1851+
return (offset._should_cache() and
1852+
not (offset._normalize_cache and not _normalized) and
1853+
_naive_in_cache_range(start, end))
18541854

18551855
def _time_to_micros(time):
18561856
seconds = time.hour * 60 * 60 + 60 * time.minute + time.second

pandas/tseries/offsets.py

+14-14
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ def onOffset(self, dt):
477477
return np.is_busday(day64, busdaycal=self.busdaycalendar)
478478

479479

480-
class MonthEnd(DateOffset, CacheableOffset):
480+
class MonthEnd(CacheableOffset, DateOffset):
481481
"""DateOffset of one month end"""
482482

483483
def apply(self, other):
@@ -502,7 +502,7 @@ def rule_code(self):
502502
return 'M'
503503

504504

505-
class MonthBegin(DateOffset, CacheableOffset):
505+
class MonthBegin(CacheableOffset, DateOffset):
506506
"""DateOffset of one month at beginning"""
507507

508508
def apply(self, other):
@@ -553,7 +553,7 @@ def rule_code(self):
553553
return 'BM'
554554

555555

556-
class BusinessMonthBegin(DateOffset, CacheableOffset):
556+
class BusinessMonthBegin(CacheableOffset, DateOffset):
557557
"""DateOffset of one business month at beginning"""
558558

559559
def apply(self, other):
@@ -590,7 +590,7 @@ def rule_code(self):
590590
return 'BMS'
591591

592592

593-
class Week(DateOffset, CacheableOffset):
593+
class Week(CacheableOffset, DateOffset):
594594
"""
595595
Weekly offset
596596
@@ -656,7 +656,7 @@ def rule_code(self):
656656
}
657657

658658

659-
class WeekOfMonth(DateOffset, CacheableOffset):
659+
class WeekOfMonth(CacheableOffset, DateOffset):
660660
"""
661661
Describes monthly dates like "the Tuesday of the 2nd week of each month"
662662
@@ -729,7 +729,7 @@ def rule_code(self):
729729
return 'WOM' + suffix
730730

731731

732-
class BQuarterEnd(DateOffset, CacheableOffset):
732+
class BQuarterEnd(CacheableOffset, DateOffset):
733733
"""DateOffset increments between business Quarter dates
734734
startingMonth = 1 corresponds to dates like 1/31/2007, 4/30/2007, ...
735735
startingMonth = 2 corresponds to dates like 2/28/2007, 5/31/2007, ...
@@ -796,7 +796,7 @@ def rule_code(self):
796796
}
797797

798798

799-
class BQuarterBegin(DateOffset, CacheableOffset):
799+
class BQuarterBegin(CacheableOffset, DateOffset):
800800
_outputName = "BusinessQuarterBegin"
801801

802802
def __init__(self, n=1, **kwds):
@@ -843,7 +843,7 @@ def rule_code(self):
843843
return 'BQS' + suffix
844844

845845

846-
class QuarterEnd(DateOffset, CacheableOffset):
846+
class QuarterEnd(CacheableOffset, DateOffset):
847847
"""DateOffset increments between business Quarter dates
848848
startingMonth = 1 corresponds to dates like 1/31/2007, 4/30/2007, ...
849849
startingMonth = 2 corresponds to dates like 2/28/2007, 5/31/2007, ...
@@ -887,7 +887,7 @@ def rule_code(self):
887887
return 'Q' + suffix
888888

889889

890-
class QuarterBegin(DateOffset, CacheableOffset):
890+
class QuarterBegin(CacheableOffset, DateOffset):
891891
_outputName = 'QuarterBegin'
892892

893893
def __init__(self, n=1, **kwds):
@@ -924,7 +924,7 @@ def rule_code(self):
924924
return 'QS' + suffix
925925

926926

927-
class BYearEnd(DateOffset, CacheableOffset):
927+
class BYearEnd(CacheableOffset, DateOffset):
928928
"""DateOffset increments between business EOM dates"""
929929
_outputName = 'BusinessYearEnd'
930930

@@ -971,7 +971,7 @@ def rule_code(self):
971971
return 'BA' + suffix
972972

973973

974-
class BYearBegin(DateOffset, CacheableOffset):
974+
class BYearBegin(CacheableOffset, DateOffset):
975975
"""DateOffset increments between business year begin dates"""
976976
_outputName = 'BusinessYearBegin'
977977

@@ -1013,7 +1013,7 @@ def rule_code(self):
10131013
return 'BAS' + suffix
10141014

10151015

1016-
class YearEnd(DateOffset, CacheableOffset):
1016+
class YearEnd(CacheableOffset, DateOffset):
10171017
"""DateOffset increments between calendar year ends"""
10181018

10191019
def __init__(self, n=1, **kwds):
@@ -1080,7 +1080,7 @@ def rule_code(self):
10801080
return 'A' + suffix
10811081

10821082

1083-
class YearBegin(DateOffset, CacheableOffset):
1083+
class YearBegin(CacheableOffset, DateOffset):
10841084
"""DateOffset increments between calendar year begin dates"""
10851085

10861086
def __init__(self, n=1, **kwds):
@@ -1251,7 +1251,7 @@ def _delta_to_nanoseconds(delta):
12511251
+ delta.microseconds) * 1000
12521252

12531253

1254-
class Day(Tick, CacheableOffset):
1254+
class Day(CacheableOffset, Tick):
12551255
_inc = timedelta(1)
12561256
_rule_base = 'D'
12571257

pandas/tseries/tests/test_offsets.py

+56-2
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@
1717
get_standard_freq)
1818

1919
from pandas.tseries.frequencies import _offset_map
20-
from pandas.tseries.index import _to_m8
20+
from pandas.tseries.index import _to_m8, DatetimeIndex, _daterange_cache
2121
from pandas.tseries.tools import parse_time_string
2222
import pandas.tseries.offsets as offsets
2323

2424
from pandas.tslib import monthrange
2525
from pandas.lib import Timestamp
2626
from pandas.util.testing import assertRaisesRegexp
2727
import pandas.util.testing as tm
28+
from pandas.tseries.offsets import BusinessMonthEnd, CacheableOffset
2829

2930
_multiprocess_can_split_ = True
3031

@@ -1789,7 +1790,60 @@ def test_freq_offsets():
17891790

17901791
off = BDay(1, offset=timedelta(0, -1800))
17911792
assert(off.freqstr == 'B-30Min')
1792-
1793+
1794+
def get_all_subclasses(cls):
1795+
ret = set()
1796+
this_subclasses = cls.__subclasses__()
1797+
ret = ret | set(this_subclasses)
1798+
for this_subclass in this_subclasses:
1799+
ret | get_all_subclasses(this_subclass)
1800+
return ret
1801+
1802+
class TestCaching(unittest.TestCase):
1803+
def test_should_cache_month_end(self):
1804+
self.assertTrue(MonthEnd()._should_cache())
1805+
1806+
def test_should_cache_bmonth_end(self):
1807+
self.assertTrue(BusinessMonthEnd()._should_cache())
1808+
1809+
def test_should_cache_week_month(self):
1810+
self.assertTrue(WeekOfMonth(weekday=1, week=2)._should_cache())
1811+
1812+
def test_all_cacheableoffsets(self):
1813+
for subclass in get_all_subclasses(CacheableOffset):
1814+
if subclass in [WeekOfMonth]:
1815+
continue
1816+
self.run_X_index_creation(subclass)
1817+
1818+
def setUp(self):
1819+
_daterange_cache.clear()
1820+
1821+
def run_X_index_creation(self, cls):
1822+
inst1 = cls()
1823+
if not inst1.isAnchored():
1824+
self.assertFalse(inst1._should_cache(), cls)
1825+
return
1826+
1827+
self.assertTrue(inst1._should_cache(), cls)
1828+
1829+
DatetimeIndex(start=datetime(2013,1,31), end=datetime(2013,3,31), freq=inst1, normalize=True)
1830+
self.assertTrue(cls() in _daterange_cache, cls)
1831+
1832+
def test_month_end_index_creation(self):
1833+
DatetimeIndex(start=datetime(2013,1,31), end=datetime(2013,3,31), freq=MonthEnd(), normalize=True)
1834+
self.assertTrue(MonthEnd() in _daterange_cache)
1835+
1836+
def test_bmonth_end_index_creation(self):
1837+
DatetimeIndex(start=datetime(2013,1,31), end=datetime(2013,3,29), freq=BusinessMonthEnd(), normalize=True)
1838+
self.assertTrue(BusinessMonthEnd() in _daterange_cache)
1839+
1840+
def test_week_of_month_index_creation(self):
1841+
inst1 = WeekOfMonth(weekday=1, week=2)
1842+
DatetimeIndex(start=datetime(2013,1,31), end=datetime(2013,3,29), freq=inst1, normalize=True)
1843+
inst2 = WeekOfMonth(weekday=1, week=2)
1844+
self.assertTrue(inst2 in _daterange_cache)
1845+
1846+
17931847
if __name__ == '__main__':
17941848
import nose
17951849
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],

0 commit comments

Comments
 (0)