Skip to content

perf improvements in tslibs.period #21447

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 12 commits into from
Jun 15, 2018
15 changes: 15 additions & 0 deletions pandas/_libs/src/util.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,18 @@ cdef inline bint _checknull(object val):

cdef inline bint is_period_object(object val):
return getattr(val, '_typ', '_typ') == 'period'


cdef inline bint is_offset_object(object val):
"""
Check if an object is a DateOffset object.
Parameters
----------
val : object
Returns
-------
is_date_offset : bool
"""
return getattr(val, '_typ', None) == "dateoffset"
2 changes: 1 addition & 1 deletion pandas/_libs/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def ints_to_pydatetime(ndarray[int64_t] arr, tz=None, freq=None,
elif box == "datetime":
func_create = create_datetime_from_ts
else:
raise ValueError("box must be one of 'datetime', 'date', 'time' or" +
raise ValueError("box must be one of 'datetime', 'date', 'time' or"
" 'timestamp'")

if tz is not None:
Expand Down
20 changes: 12 additions & 8 deletions pandas/_libs/tslibs/period.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ from pandas.compat import PY2

cimport cython

from cpython.datetime cimport PyDateTime_Check, PyDateTime_IMPORT
from cpython.datetime cimport (PyDateTime_Check, PyDelta_Check,
PyDateTime_IMPORT)
# import datetime C API
PyDateTime_IMPORT

Expand Down Expand Up @@ -1058,18 +1059,21 @@ cdef class _Period(object):
return hash((self.ordinal, self.freqstr))

def _add_delta(self, other):
if isinstance(other, (timedelta, np.timedelta64, offsets.Tick)):
cdef:
int64_t nanos, offset_nanos

if (PyDelta_Check(other) or util.is_timedelta64_object(other) or
isinstance(other, offsets.Tick)):
offset = frequencies.to_offset(self.freq.rule_code)
if isinstance(offset, offsets.Tick):
nanos = delta_to_nanoseconds(other)
offset_nanos = delta_to_nanoseconds(offset)

if nanos % offset_nanos == 0:
ordinal = self.ordinal + (nanos // offset_nanos)
return Period(ordinal=ordinal, freq=self.freq)
msg = 'Input cannot be converted to Period(freq={0})'
raise IncompatibleFrequency(msg.format(self.freqstr))
elif isinstance(other, offsets.DateOffset):
elif util.is_offset_object(other):
freqstr = other.rule_code
base = get_base_alias(freqstr)
if base == self.freq.rule_code:
Expand All @@ -1082,8 +1086,8 @@ cdef class _Period(object):

def __add__(self, other):
if is_period_object(self):
if isinstance(other, (timedelta, np.timedelta64,
offsets.DateOffset)):
if (PyDelta_Check(other) or util.is_timedelta64_object(other) or
util.is_offset_object(other)):
return self._add_delta(other)
elif other is NaT:
return NaT
Expand All @@ -1109,8 +1113,8 @@ cdef class _Period(object):

def __sub__(self, other):
if is_period_object(self):
if isinstance(other, (timedelta, np.timedelta64,
offsets.DateOffset)):
if (PyDelta_Check(other) or util.is_timedelta64_object(other) or
util.is_offset_object(other)):
neg_other = -other
return self + neg_other
elif util.is_integer_object(other):
Expand Down