diff --git a/pandas/_libs/period.pyx b/pandas/_libs/period.pyx index 8f89b812fec04..ab276f172c5fc 100644 --- a/pandas/_libs/period.pyx +++ b/pandas/_libs/period.pyx @@ -13,14 +13,12 @@ import numpy as np from libc.stdlib cimport free -from pandas import compat from pandas.compat import PY2 cimport cython from datetime cimport ( is_leapyear, - PyDateTime_IMPORT, pandas_datetimestruct, pandas_datetimestruct_to_datetime, pandas_datetime_to_datetimestruct, @@ -29,6 +27,7 @@ from datetime cimport ( cimport util, lib +from util cimport is_period_object, is_string_object from lib cimport is_null_datetimelike, is_period from pandas._libs import tslib, lib @@ -41,6 +40,8 @@ from tslib cimport ( _get_dst_info, _nat_scalar_rules) +from tslibs.frequencies cimport get_freq_code + from pandas.tseries import offsets from pandas.core.tools.datetimes import parse_time_string from pandas.tseries import frequencies @@ -329,8 +330,6 @@ cdef list str_extra_fmts = ["^`AB`^", "^`CD`^", "^`EF`^", "^`GH`^", "^`IJ`^", "^`KL`^"] cdef object _period_strftime(int64_t value, int freq, object fmt): - import sys - cdef: Py_ssize_t i date_info dinfo @@ -683,7 +682,7 @@ cdef class _Period(object): def _maybe_convert_freq(cls, object freq): if isinstance(freq, (int, tuple)): - code, stride = frequencies.get_freq_code(freq) + code, stride = get_freq_code(freq) freq = frequencies._get_freq_str(code, stride) freq = frequencies.to_offset(freq) @@ -707,7 +706,7 @@ cdef class _Period(object): return self def __richcmp__(self, other, op): - if isinstance(other, Period): + if is_period_object(other): if other.freq != self.freq: msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr) raise IncompatibleFrequency(msg) @@ -753,7 +752,7 @@ cdef class _Period(object): return NotImplemented def __add__(self, other): - if isinstance(self, Period): + if is_period_object(self): if isinstance(other, (timedelta, np.timedelta64, offsets.DateOffset, Timedelta)): @@ -765,13 +764,13 @@ cdef class _Period(object): return Period(ordinal=ordinal, freq=self.freq) else: # pragma: no cover return NotImplemented - elif isinstance(other, Period): + elif is_period_object(other): return other + self else: return NotImplemented def __sub__(self, other): - if isinstance(self, Period): + if is_period_object(self): if isinstance(other, (timedelta, np.timedelta64, offsets.DateOffset, Timedelta)): @@ -780,7 +779,7 @@ cdef class _Period(object): elif lib.is_integer(other): ordinal = self.ordinal - other * self.freq.n return Period(ordinal=ordinal, freq=self.freq) - elif isinstance(other, Period): + elif is_period_object(other): if other.freq != self.freq: msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr) raise IncompatibleFrequency(msg) @@ -789,7 +788,7 @@ cdef class _Period(object): return -other.__sub__(self) else: # pragma: no cover return NotImplemented - elif isinstance(other, Period): + elif is_period_object(other): if self is NaT: return NaT return NotImplemented @@ -813,8 +812,8 @@ cdef class _Period(object): """ freq = self._maybe_convert_freq(freq) how = _validate_end_alias(how) - base1, mult1 = frequencies.get_freq_code(self.freq) - base2, mult2 = frequencies.get_freq_code(freq) + base1, mult1 = get_freq_code(self.freq) + base2, mult2 = get_freq_code(freq) # mult1 can't be negative or 0 end = how == 'E' @@ -860,17 +859,17 @@ cdef class _Period(object): how = _validate_end_alias(how) if freq is None: - base, mult = frequencies.get_freq_code(self.freq) + base, mult = get_freq_code(self.freq) freq = frequencies.get_to_timestamp_base(base) - base, mult = frequencies.get_freq_code(freq) + base, mult = get_freq_code(freq) val = self.asfreq(freq, how) dt64 = period_ordinal_to_dt64(val.ordinal, base) return Timestamp(dt64, tz=tz) cdef _field(self, alias): - base, mult = frequencies.get_freq_code(self.freq) + base, mult = get_freq_code(self.freq) return get_period_field(alias, self.ordinal, base) property year: @@ -935,7 +934,7 @@ cdef class _Period(object): return self.freq.freqstr def __repr__(self): - base, mult = frequencies.get_freq_code(self.freq) + base, mult = get_freq_code(self.freq) formatted = period_format(self.ordinal, base) return "Period('%s', '%s')" % (formatted, self.freqstr) @@ -946,7 +945,7 @@ cdef class _Period(object): Invoked by unicode(df) in py2 only. Yields a Unicode String in both py2/py3. """ - base, mult = frequencies.get_freq_code(self.freq) + base, mult = get_freq_code(self.freq) formatted = period_format(self.ordinal, base) value = ("%s" % formatted) return value @@ -1096,7 +1095,7 @@ cdef class _Period(object): >>> a.strftime('%b. %d, %Y was a %A') 'Jan. 01, 2001 was a Monday' """ - base, mult = frequencies.get_freq_code(self.freq) + base, mult = get_freq_code(self.freq) return period_format(self.ordinal, base, fmt) @@ -1161,10 +1160,10 @@ class Period(_Period): ordinal = _ordinal_from_fields(year, month, quarter, day, hour, minute, second, freq) - elif isinstance(value, Period): + elif is_period_object(value): other = value - if freq is None or frequencies.get_freq_code( - freq) == frequencies.get_freq_code(other.freq): + if freq is None or get_freq_code( + freq) == get_freq_code(other.freq): ordinal = other.ordinal freq = other.freq else: @@ -1174,7 +1173,7 @@ class Period(_Period): elif is_null_datetimelike(value) or value in tslib._nat_strings: ordinal = iNaT - elif isinstance(value, compat.string_types) or lib.is_integer(value): + elif is_string_object(value) or lib.is_integer(value): if lib.is_integer(value): value = str(value) value = value.upper() @@ -1191,7 +1190,7 @@ class Period(_Period): dt = value if freq is None: raise ValueError('Must supply freq for datetime value') - elif isinstance(value, np.datetime64): + elif util.is_datetime64_object(value): dt = Timestamp(value) if freq is None: raise ValueError('Must supply freq for datetime value') @@ -1204,7 +1203,7 @@ class Period(_Period): raise ValueError(msg) if ordinal is None: - base, mult = frequencies.get_freq_code(freq) + base, mult = get_freq_code(freq) ordinal = get_period_ordinal(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, 0, base) @@ -1214,7 +1213,7 @@ class Period(_Period): def _ordinal_from_fields(year, month, quarter, day, hour, minute, second, freq): - base, mult = frequencies.get_freq_code(freq) + base, mult = get_freq_code(freq) if quarter is not None: year, month = _quarter_to_myear(year, quarter, freq) @@ -1227,8 +1226,7 @@ def _quarter_to_myear(year, quarter, freq): if quarter <= 0 or quarter > 4: raise ValueError('Quarter must be 1 <= q <= 4') - mnum = frequencies._month_numbers[ - frequencies._get_rule_month(freq)] + 1 + mnum = tslib._MONTH_NUMBERS[tslib._get_rule_month(freq)] + 1 month = (mnum + (quarter - 1) * 3) % 12 + 1 if month > mnum: year -= 1 diff --git a/pandas/_libs/tslibs/frequencies.pxd b/pandas/_libs/tslibs/frequencies.pxd new file mode 100644 index 0000000000000..974eb4ab45df0 --- /dev/null +++ b/pandas/_libs/tslibs/frequencies.pxd @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# cython: profile=False + +cpdef get_freq_code(freqstr) diff --git a/pandas/_libs/tslibs/frequencies.pyx b/pandas/_libs/tslibs/frequencies.pyx index 35429e8ae87f0..f7889d76abbc7 100644 --- a/pandas/_libs/tslibs/frequencies.pyx +++ b/pandas/_libs/tslibs/frequencies.pyx @@ -150,6 +150,9 @@ _period_code_map = { "N": 12000, # Nanosecondly } +_reverse_period_code_map = { + _period_code_map[key]: key for key in _period_code_map} + # Yearly aliases; careful not to put these in _reverse_period_code_map _period_code_map.update({'Y' + key[1:]: _period_code_map[key] for key in _period_code_map diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 6644a33245a84..085a3a784557b 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -20,7 +20,10 @@ from pandas._libs import lib, tslib from pandas._libs.tslib import Timedelta -from pandas._libs.tslibs.frequencies import get_freq_code, _base_and_stride +from pandas._libs.tslibs.frequencies import ( # noqa + get_freq_code, _base_and_stride, _period_str_to_code, + _INVALID_FREQ_ERROR, opattern, _lite_rule_alias, _dont_uppercase, + _period_code_map, _reverse_period_code_map) from pytz import AmbiguousTimeError @@ -375,27 +378,6 @@ def get_period_alias(offset_str): return _offset_to_period_map.get(offset_str, None) -_lite_rule_alias = { - 'W': 'W-SUN', - 'Q': 'Q-DEC', - - 'A': 'A-DEC', # YearEnd(month=12), - 'Y': 'A-DEC', - 'AS': 'AS-JAN', # YearBegin(month=1), - 'YS': 'AS-JAN', - 'BA': 'BA-DEC', # BYearEnd(month=12), - 'BY': 'BA-DEC', - 'BAS': 'BAS-JAN', # BYearBegin(month=1), - 'BYS': 'BAS-JAN', - - 'Min': 'T', - 'min': 'T', - 'ms': 'L', - 'us': 'U', - 'ns': 'N' -} - - _name_to_offset_map = {'days': Day(1), 'hours': Hour(1), 'minutes': Minute(1), @@ -405,9 +387,6 @@ def get_period_alias(offset_str): 'nanoseconds': Nano(1)} -_INVALID_FREQ_ERROR = "Invalid frequency: {0}" - - @deprecate_kwarg(old_arg_name='freqstr', new_arg_name='freq') def to_offset(freq): """ @@ -519,12 +498,6 @@ def to_offset(freq): return delta -# hack to handle WOM-1MON -opattern = re.compile( - r'([\-]?\d*|[\-]?\d*\.\d*)\s*([A-Za-z]+([\-][\dA-Za-z\-]+)?)' -) - - def get_base_alias(freqstr): """ Returns the base frequency alias, e.g., '5D' -> 'D' @@ -532,9 +505,6 @@ def get_base_alias(freqstr): return _base_and_stride(freqstr)[0] -_dont_uppercase = set(('MS', 'ms')) - - def get_offset(name): """ Return DateOffset object associated with rule name @@ -583,96 +553,6 @@ def get_standard_freq(freq): # --------------------------------------------------------------------- # Period codes -# period frequency constants corresponding to scikits timeseries -# originals -_period_code_map = { - # Annual freqs with various fiscal year ends. - # eg, 2005 for A-FEB runs Mar 1, 2004 to Feb 28, 2005 - "A-DEC": 1000, # Annual - December year end - "A-JAN": 1001, # Annual - January year end - "A-FEB": 1002, # Annual - February year end - "A-MAR": 1003, # Annual - March year end - "A-APR": 1004, # Annual - April year end - "A-MAY": 1005, # Annual - May year end - "A-JUN": 1006, # Annual - June year end - "A-JUL": 1007, # Annual - July year end - "A-AUG": 1008, # Annual - August year end - "A-SEP": 1009, # Annual - September year end - "A-OCT": 1010, # Annual - October year end - "A-NOV": 1011, # Annual - November year end - - # Quarterly frequencies with various fiscal year ends. - # eg, Q42005 for Q-OCT runs Aug 1, 2005 to Oct 31, 2005 - "Q-DEC": 2000, # Quarterly - December year end - "Q-JAN": 2001, # Quarterly - January year end - "Q-FEB": 2002, # Quarterly - February year end - "Q-MAR": 2003, # Quarterly - March year end - "Q-APR": 2004, # Quarterly - April year end - "Q-MAY": 2005, # Quarterly - May year end - "Q-JUN": 2006, # Quarterly - June year end - "Q-JUL": 2007, # Quarterly - July year end - "Q-AUG": 2008, # Quarterly - August year end - "Q-SEP": 2009, # Quarterly - September year end - "Q-OCT": 2010, # Quarterly - October year end - "Q-NOV": 2011, # Quarterly - November year end - - "M": 3000, # Monthly - - "W-SUN": 4000, # Weekly - Sunday end of week - "W-MON": 4001, # Weekly - Monday end of week - "W-TUE": 4002, # Weekly - Tuesday end of week - "W-WED": 4003, # Weekly - Wednesday end of week - "W-THU": 4004, # Weekly - Thursday end of week - "W-FRI": 4005, # Weekly - Friday end of week - "W-SAT": 4006, # Weekly - Saturday end of week - - "B": 5000, # Business days - "D": 6000, # Daily - "H": 7000, # Hourly - "T": 8000, # Minutely - "S": 9000, # Secondly - "L": 10000, # Millisecondly - "U": 11000, # Microsecondly - "N": 12000, # Nanosecondly -} - -_reverse_period_code_map = {} -for _k, _v in compat.iteritems(_period_code_map): - _reverse_period_code_map[_v] = _k - -# Yearly aliases -year_aliases = {} - -for k, v in compat.iteritems(_period_code_map): - if k.startswith("A-"): - alias = "Y" + k[1:] - year_aliases[alias] = v - -_period_code_map.update(**year_aliases) -del year_aliases - -_period_code_map.update({ - "Q": 2000, # Quarterly - December year end (default quarterly) - "A": 1000, # Annual - "W": 4000, # Weekly - "C": 5000, # Custom Business Day -}) - - -def _period_str_to_code(freqstr): - freqstr = _lite_rule_alias.get(freqstr, freqstr) - - if freqstr not in _dont_uppercase: - lower = freqstr.lower() - freqstr = _lite_rule_alias.get(lower, freqstr) - - if freqstr not in _dont_uppercase: - freqstr = freqstr.upper() - try: - return _period_code_map[freqstr] - except KeyError: - raise ValueError(_INVALID_FREQ_ERROR.format(freqstr)) - def infer_freq(index, warn=True): """