Skip to content

Commit 2eb568a

Browse files
jbrockmendeljreback
authored andcommitted
Bitesize offsets (#17318)
1 parent b555613 commit 2eb568a

File tree

3 files changed

+58
-76
lines changed

3 files changed

+58
-76
lines changed

asv_bench/benchmarks/timeseries.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def setup(self):
5656
self.no_freq = self.rng7[:50000].append(self.rng7[50002:])
5757
self.d_freq = self.rng7[:50000].append(self.rng7[50000:])
5858

59-
self.rng8 = date_range(start='1/1/1700', freq='B', periods=100000)
59+
self.rng8 = date_range(start='1/1/1700', freq='B', periods=75000)
6060
self.b_freq = self.rng8[:50000].append(self.rng8[50000:])
6161

6262
def time_add_timedelta(self):

pandas/tseries/frequencies.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
from datetime import timedelta
23
from pandas.compat import long, zip
34
from pandas import compat

pandas/tseries/offsets.py

+56-75
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
from datetime import date, datetime, timedelta
23
from pandas.compat import range
34
from pandas import compat
@@ -323,37 +324,42 @@ def _params(self):
323324

324325
def __repr__(self):
325326
className = getattr(self, '_outputName', type(self).__name__)
327+
328+
if abs(self.n) != 1:
329+
plural = 's'
330+
else:
331+
plural = ''
332+
333+
n_str = ""
334+
if self.n != 1:
335+
n_str = "%s * " % self.n
336+
337+
out = '<%s' % n_str + className + plural + self._repr_attrs() + '>'
338+
return out
339+
340+
# TODO: Combine this with BusinessMixin version by defining a whitelisted
341+
# set of attributes on each object rather than the existing behavior of
342+
# iterating over internal ``__dict__``
343+
def _repr_attrs(self):
326344
exclude = set(['n', 'inc', 'normalize'])
327345
attrs = []
328346
for attr in sorted(self.__dict__):
329-
if ((attr == 'kwds' and len(self.kwds) == 0) or
330-
attr.startswith('_')):
347+
if attr.startswith('_'):
331348
continue
332-
elif attr == 'kwds':
349+
elif attr == 'kwds': # TODO: get rid of this
333350
kwds_new = {}
334351
for key in self.kwds:
335352
if not hasattr(self, key):
336353
kwds_new[key] = self.kwds[key]
337354
if len(kwds_new) > 0:
338-
attrs.append('='.join((attr, repr(kwds_new))))
339-
else:
340-
if attr not in exclude:
341-
attrs.append('='.join((attr, repr(getattr(self, attr)))))
342-
343-
plural = ''
344-
if abs(self.n) != 1:
345-
plural = 's'
346-
347-
n_str = ''
348-
if self.n != 1:
349-
n_str = '{n} * '.format(n=self.n)
355+
attrs.append('kwds=%s' % (kwds_new))
356+
elif attr not in exclude:
357+
value = getattr(self, attr)
358+
attrs.append('%s=%s' % (attr, value))
350359

351-
attrs_str = ''
360+
out = ''
352361
if attrs:
353-
attrs_str = ': ' + ', '.join(attrs)
354-
355-
repr_content = ''.join([n_str, className, plural, attrs_str])
356-
out = '<{content}>'.format(content=repr_content)
362+
out += ': ' + ', '.join(attrs)
357363
return out
358364

359365
@property
@@ -507,8 +513,18 @@ def freqstr(self):
507513
else:
508514
fstr = code
509515

516+
try:
517+
if self._offset:
518+
fstr += self._offset_str()
519+
except AttributeError:
520+
# TODO: standardize `_offset` vs `offset` naming convention
521+
pass
522+
510523
return fstr
511524

525+
def _offset_str(self):
526+
return ''
527+
512528
@property
513529
def nanos(self):
514530
raise ValueError("{name} is a non-fixed frequency".format(name=self))
@@ -527,23 +543,11 @@ def _from_name(cls, suffix=None):
527543
class BusinessMixin(object):
528544
""" mixin to business types to provide related functions """
529545

530-
# TODO: Combine this with DateOffset by defining a whitelisted set of
531-
# attributes on each object rather than the existing behavior of iterating
532-
# over internal ``__dict__``
533-
def __repr__(self):
534-
className = getattr(self, '_outputName', self.__class__.__name__)
535-
536-
plural = ''
537-
if abs(self.n) != 1:
538-
plural = 's'
539-
540-
n_str = ''
541-
if self.n != 1:
542-
n_str = '{n} * '.format(n=self.n)
543-
544-
repr_content = ''.join([n_str, className, plural, self._repr_attrs()])
545-
out = '<{content}>'.format(content=repr_content)
546-
return out
546+
@property
547+
def offset(self):
548+
"""Alias for self._offset"""
549+
# Alias for backward compat
550+
return self._offset
547551

548552
def _repr_attrs(self):
549553
if self.offset:
@@ -572,6 +576,11 @@ def __getstate__(self):
572576

573577
def __setstate__(self, state):
574578
"""Reconstruct an instance from a pickled state"""
579+
if 'offset' in state:
580+
# Older versions have offset attribute instead of _offset
581+
if '_offset' in state: # pragma: no cover
582+
raise ValueError('Unexpected key `_offset`')
583+
state['_offset'] = state.pop('offset')
575584
self.__dict__ = state
576585
if 'weekmask' in state and 'holidays' in state:
577586
calendar, holidays = _get_calendar(weekmask=self.weekmask,
@@ -593,24 +602,7 @@ def __init__(self, n=1, normalize=False, **kwds):
593602
self.n = int(n)
594603
self.normalize = normalize
595604
self.kwds = kwds
596-
self.offset = kwds.get('offset', timedelta(0))
597-
598-
@property
599-
def freqstr(self):
600-
try:
601-
code = self.rule_code
602-
except NotImplementedError:
603-
return repr(self)
604-
605-
if self.n != 1:
606-
fstr = '{n}{code}'.format(n=self.n, code=code)
607-
else:
608-
fstr = code
609-
610-
if self.offset:
611-
fstr += self._offset_str()
612-
613-
return fstr
605+
self._offset = kwds.get('offset', timedelta(0))
614606

615607
def _offset_str(self):
616608
def get_str(td):
@@ -643,9 +635,6 @@ def get_str(td):
643635
else:
644636
return '+' + repr(self.offset)
645637

646-
def isAnchored(self):
647-
return (self.n == 1)
648-
649638
@apply_wraps
650639
def apply(self, other):
651640
if isinstance(other, datetime):
@@ -709,7 +698,7 @@ def __init__(self, **kwds):
709698
kwds['start'] = self._validate_time(kwds.get('start', '09:00'))
710699
kwds['end'] = self._validate_time(kwds.get('end', '17:00'))
711700
self.kwds = kwds
712-
self.offset = kwds.get('offset', timedelta(0))
701+
self._offset = kwds.get('offset', timedelta(0))
713702
self.start = kwds.get('start', '09:00')
714703
self.end = kwds.get('end', '17:00')
715704

@@ -776,7 +765,7 @@ def _get_business_hours_by_sec(self):
776765
Return business hours in a day by seconds.
777766
"""
778767
if self._get_daytime_flag():
779-
# create dummy datetime to calcurate businesshours in a day
768+
# create dummy datetime to calculate businesshours in a day
780769
dtstart = datetime(2014, 4, 1, self.start.hour, self.start.minute)
781770
until = datetime(2014, 4, 1, self.end.hour, self.end.minute)
782771
return (until - dtstart).total_seconds()
@@ -811,7 +800,7 @@ def rollforward(self, dt):
811800

812801
@apply_wraps
813802
def apply(self, other):
814-
# calcurate here because offset is not immutable
803+
# calculate here because offset is not immutable
815804
daytime = self._get_daytime_flag()
816805
businesshours = self._get_business_hours_by_sec()
817806
bhdelta = timedelta(seconds=businesshours)
@@ -860,7 +849,7 @@ def apply(self, other):
860849
if n >= 0:
861850
bday_edge = self._prev_opening_time(other)
862851
bday_edge = bday_edge + bhdelta
863-
# calcurate remainder
852+
# calculate remainder
864853
bday_remain = result - bday_edge
865854
result = self._next_opening_time(other)
866855
result += bday_remain
@@ -898,7 +887,7 @@ def onOffset(self, dt):
898887

899888
def _onOffset(self, dt, businesshours):
900889
"""
901-
Slight speedups using calcurated values
890+
Slight speedups using calculated values
902891
"""
903892
# if self.normalize and not _is_normalized(dt):
904893
# return False
@@ -975,7 +964,8 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
975964
self.n = int(n)
976965
self.normalize = normalize
977966
self.kwds = kwds
978-
self.offset = kwds.get('offset', timedelta(0))
967+
self._offset = kwds.get('offset', timedelta(0))
968+
979969
calendar, holidays = _get_calendar(weekmask=weekmask,
980970
holidays=holidays,
981971
calendar=calendar)
@@ -1337,9 +1327,6 @@ def _apply_index_days(self, i, roll):
13371327
class BusinessMonthEnd(MonthOffset):
13381328
"""DateOffset increments between business EOM dates"""
13391329

1340-
def isAnchored(self):
1341-
return (self.n == 1)
1342-
13431330
@apply_wraps
13441331
def apply(self, other):
13451332
n = self.n
@@ -1425,7 +1412,7 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
14251412
self.n = int(n)
14261413
self.normalize = normalize
14271414
self.kwds = kwds
1428-
self.offset = kwds.get('offset', timedelta(0))
1415+
self._offset = kwds.get('offset', timedelta(0))
14291416

14301417
calendar, holidays = _get_calendar(weekmask=weekmask,
14311418
holidays=holidays,
@@ -1495,7 +1482,7 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
14951482
self.n = int(n)
14961483
self.normalize = normalize
14971484
self.kwds = kwds
1498-
self.offset = kwds.get('offset', timedelta(0))
1485+
self._offset = kwds.get('offset', timedelta(0))
14991486

15001487
# _get_calendar does validation and possible transformation
15011488
# of calendar and holidays.
@@ -1966,9 +1953,6 @@ class QuarterEnd(QuarterOffset):
19661953
_default_startingMonth = 3
19671954
_prefix = 'Q'
19681955

1969-
def isAnchored(self):
1970-
return (self.n == 1 and self.startingMonth is not None)
1971-
19721956
@apply_wraps
19731957
def apply(self, other):
19741958
n = self.n
@@ -2004,9 +1988,6 @@ class QuarterBegin(QuarterOffset):
20041988
_from_name_startingMonth = 1
20051989
_prefix = 'QS'
20061990

2007-
def isAnchored(self):
2008-
return (self.n == 1 and self.startingMonth is not None)
2009-
20101991
@apply_wraps
20111992
def apply(self, other):
20121993
n = self.n

0 commit comments

Comments
 (0)