diff --git a/doc/source/timeseries.rst b/doc/source/timeseries.rst index 4394981abb8c3..29b955a55fcc9 100644 --- a/doc/source/timeseries.rst +++ b/doc/source/timeseries.rst @@ -591,7 +591,7 @@ various docstrings for the classes. These operations (``apply``, ``rollforward`` and ``rollback``) preserves time (hour, minute, etc) information by default. To reset time, use ``normalize=True`` keyword when creating the offset instance. If ``normalize=True``, result is normalized after the function is applied. - .. ipython:: python +.. ipython:: python day = Day() day.apply(Timestamp('2014-01-01 09:00')) @@ -1257,8 +1257,10 @@ be created with the convenience function ``period_range``. Period ~~~~~~ + A ``Period`` represents a span of time (e.g., a day, a month, a quarter, etc). -It can be created using a frequency alias: +You can specify the span via ``freq`` keyword using a frequency alias like below. +Because ``freq`` represents a span of ``Period``, it cannot be negative like "-3D". .. ipython:: python @@ -1268,11 +1270,10 @@ It can be created using a frequency alias: Period('2012-1-1 19:00', freq='H') -Unlike time stamped data, pandas does not support frequencies at multiples of -DateOffsets (e.g., '3Min') for periods. + Period('2012-1-1 19:00', freq='5H') Adding and subtracting integers from periods shifts the period by its own -frequency. +frequency. Arithmetic is not allowed between ``Period`` with different ``freq`` (span). .. ipython:: python @@ -1282,6 +1283,15 @@ frequency. p - 3 + p = Period('2012-01', freq='2M') + + p + 2 + + p - 1 + + p == Period('2012-01', freq='3M') + + If ``Period`` freq is daily or higher (``D``, ``H``, ``T``, ``S``, ``L``, ``U``, ``N``), ``offsets`` and ``timedelta``-like can be added if the result can have the same freq. Otherise, ``ValueError`` will be raised. .. ipython:: python @@ -1335,6 +1345,13 @@ The ``PeriodIndex`` constructor can also be used directly: PeriodIndex(['2011-1', '2011-2', '2011-3'], freq='M') +Passing multiplied frequency outputs a sequence of ``Period`` which +has multiplied span. + +.. ipython:: python + + PeriodIndex(start='2014-01', freq='3M', periods=4) + Just like ``DatetimeIndex``, a ``PeriodIndex`` can also be used to index pandas objects: diff --git a/doc/source/whatsnew/v0.17.0.txt b/doc/source/whatsnew/v0.17.0.txt index d0cef7996a8e0..42752112a64f7 100644 --- a/doc/source/whatsnew/v0.17.0.txt +++ b/doc/source/whatsnew/v0.17.0.txt @@ -120,6 +120,32 @@ We are now supporting a ``Series.dt.strftime`` method for datetime-likes to gene The string format is as the python standard library and details can be found `here `_ +.. _whatsnew_0170.periodfreq: + +Period Frequency Enhancement +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``Period``, ``PeriodIndex`` and ``period_range`` can now accept multiplied freq. Also, ``Period.freq`` and ``PeriodIndex.freq`` are now stored as ``DateOffset`` instance like ``DatetimeIndex``, not ``str`` (:issue:`7811`) + +Multiplied freq represents a span of corresponding length. Below example creates a period of 3 days. Addition and subtraction will shift the period by its span. + +.. ipython:: python + + p = pd.Period('2015-08-01', freq='3D') + p + p + 1 + p - 2 + p.to_timestamp() + p.to_timestamp(how='E') + +You can use multiplied freq in ``PeriodIndex`` and ``period_range``. + +.. ipython:: python + + idx = pd.period_range('2015-08-01', periods=4, freq='2D') + idx + idx + 1 + .. _whatsnew_0170.enhancements.sas_xport: Support for SAS XPORT files @@ -198,7 +224,6 @@ Other enhancements - ``pd.Timedelta.total_seconds()`` now returns Timedelta duration to ns precision (previously microsecond precision) (:issue: `10939`) - ``.as_blocks`` will now take a ``copy`` optional argument to return a copy of the data, default is to copy (no change in behavior from prior versions), (:issue:`9607`) - - ``regex`` argument to ``DataFrame.filter`` now handles numeric column names instead of raising ``ValueError`` (:issue:`10384`). - ``pd.read_stata`` will now read Stata 118 type files. (:issue:`9882`) diff --git a/pandas/io/tests/data/legacy_msgpack/0.16.0/0.16.0_x86_64_darwin_2.7.9.msgpack b/pandas/io/tests/data/legacy_msgpack/0.16.0/0.16.0_x86_64_darwin_2.7.9.msgpack new file mode 100644 index 0000000000000..554f8a6e0742a Binary files /dev/null and b/pandas/io/tests/data/legacy_msgpack/0.16.0/0.16.0_x86_64_darwin_2.7.9.msgpack differ diff --git a/pandas/io/tests/data/legacy_msgpack/0.16.2/0.16.2_x86_64_darwin_2.7.9.msgpack b/pandas/io/tests/data/legacy_msgpack/0.16.2/0.16.2_x86_64_darwin_2.7.9.msgpack new file mode 100644 index 0000000000000..000879f4cb2c2 Binary files /dev/null and b/pandas/io/tests/data/legacy_msgpack/0.16.2/0.16.2_x86_64_darwin_2.7.9.msgpack differ diff --git a/pandas/io/tests/data/legacy_pickle/0.16.0/0.16.0_x86_64_darwin_2.7.9.pickle b/pandas/io/tests/data/legacy_pickle/0.16.0/0.16.0_x86_64_darwin_2.7.9.pickle new file mode 100644 index 0000000000000..d45936baa1e00 Binary files /dev/null and b/pandas/io/tests/data/legacy_pickle/0.16.0/0.16.0_x86_64_darwin_2.7.9.pickle differ diff --git a/pandas/io/tests/data/legacy_pickle/0.16.2/0.16.2_x86_64_darwin_2.7.9.pickle b/pandas/io/tests/data/legacy_pickle/0.16.2/0.16.2_x86_64_darwin_2.7.9.pickle new file mode 100644 index 0000000000000..d45936baa1e00 Binary files /dev/null and b/pandas/io/tests/data/legacy_pickle/0.16.2/0.16.2_x86_64_darwin_2.7.9.pickle differ diff --git a/pandas/io/tests/test_pickle.py b/pandas/io/tests/test_pickle.py index 8f2079722c00e..1ade6ac0f8068 100644 --- a/pandas/io/tests/test_pickle.py +++ b/pandas/io/tests/test_pickle.py @@ -17,6 +17,8 @@ from pandas.compat import u from pandas.util.misc import is_little_endian import pandas +from pandas.tseries.offsets import Day, MonthEnd + class TestPickle(): """ @@ -90,6 +92,10 @@ def read_pickles(self, version): if 'ts' in data['series']: self._validate_timeseries(data['series']['ts'], self.data['series']['ts']) self._validate_frequency(data['series']['ts']) + if 'index' in data: + if 'period' in data['index']: + self._validate_periodindex(data['index']['period'], + self.data['index']['period']) n += 1 assert n > 0, 'Pickle files are not tested' @@ -162,7 +168,6 @@ def _validate_timeseries(self, pickled, current): def _validate_frequency(self, pickled): # GH 9291 - from pandas.tseries.offsets import Day freq = pickled.index.freq result = freq + Day(1) tm.assert_equal(result, Day(2)) @@ -175,6 +180,13 @@ def _validate_frequency(self, pickled): tm.assert_equal(isinstance(result, pandas.Timedelta), True) tm.assert_equal(result, pandas.Timedelta(days=1, nanoseconds=1)) + def _validate_periodindex(self, pickled, current): + tm.assert_index_equal(pickled, current) + tm.assertIsInstance(pickled.freq, MonthEnd) + tm.assert_equal(pickled.freq, MonthEnd()) + tm.assert_equal(pickled.freqstr, 'M') + tm.assert_index_equal(pickled.shift(2), current.shift(2)) + if __name__ == '__main__': import nose diff --git a/pandas/src/period.pyx b/pandas/src/period.pyx index 619d1a87a71e0..1dbf469a946b5 100644 --- a/pandas/src/period.pyx +++ b/pandas/src/period.pyx @@ -615,6 +615,9 @@ cdef ndarray[int64_t] localize_dt64arr_to_period(ndarray[int64_t] stamps, return result +_DIFFERENT_FREQ_ERROR = "Input has different freq={1} from Period(freq={0})" + + cdef class Period(object): """ Represents an period of time @@ -624,8 +627,7 @@ cdef class Period(object): value : Period or compat.string_types, default None The time period represented (e.g., '4Q2005') freq : str, default None - e.g., 'B' for businessday. Must be a singular rule-code (e.g. 5T is not - allowed). + One of pandas period strings or corresponding objects year : int, default None month : int, default 1 quarter : int, default None @@ -641,12 +643,33 @@ cdef class Period(object): _comparables = ['name','freqstr'] _typ = 'period' + @classmethod + def _maybe_convert_freq(cls, object freq): + + if isinstance(freq, compat.string_types): + from pandas.tseries.frequencies import _period_alias_dict + freq = _period_alias_dict.get(freq, freq) + elif isinstance(freq, (int, tuple)): + from pandas.tseries.frequencies import get_freq_code as _gfc + from pandas.tseries.frequencies import _get_freq_str + code, stride = _gfc(freq) + freq = _get_freq_str(code, stride) + + from pandas.tseries.frequencies import to_offset + freq = to_offset(freq) + + if freq.n <= 0: + raise ValueError('Frequency must be positive, because it' + ' represents span: {0}'.format(freq.freqstr)) + + return freq + @classmethod def _from_ordinal(cls, ordinal, freq): """ fast creation from an ordinal and freq that are already validated! """ self = Period.__new__(cls) self.ordinal = ordinal - self.freq = freq + self.freq = cls._maybe_convert_freq(freq) return self def __init__(self, value=None, freq=None, ordinal=None, @@ -659,8 +682,6 @@ cdef class Period(object): # periods such as A, Q, etc. Every five minutes would be, e.g., # ('T', 5) but may be passed in as a string like '5T' - self.freq = None - # ordinal is the period offset from the gregorian proleptic epoch if ordinal is not None and value is not None: @@ -675,9 +696,8 @@ cdef class Period(object): elif value is None: if freq is None: raise ValueError("If value is None, freq cannot be None") - ordinal = _ordinal_from_fields(year, month, quarter, day, - hour, minute, second, freq) + hour, minute, second, freq) elif isinstance(value, Period): other = value @@ -698,8 +718,8 @@ cdef class Period(object): if lib.is_integer(value): value = str(value) value = value.upper() - dt, _, reso = parse_time_string(value, freq) + if freq is None: try: freq = frequencies.Resolution.get_freq(reso) @@ -723,24 +743,22 @@ cdef class Period(object): raise ValueError(msg) base, mult = _gfc(freq) - if mult != 1: - # TODO: Better error message - this is slightly confusing - raise ValueError('Only mult == 1 supported') if ordinal is None: self.ordinal = get_period_ordinal(dt.year, dt.month, dt.day, - dt.hour, dt.minute, dt.second, dt.microsecond, 0, - base) + dt.hour, dt.minute, dt.second, + dt.microsecond, 0, base) else: self.ordinal = ordinal - self.freq = frequencies._get_freq_str(base) + self.freq = self._maybe_convert_freq(freq) def __richcmp__(self, other, op): if isinstance(other, Period): from pandas.tseries.frequencies import get_freq_code as _gfc if other.freq != self.freq: - raise ValueError("Cannot compare non-conforming periods") + msg = _DIFFERENT_FREQ_ERROR.format(self.freqstr, other.freqstr) + raise ValueError(msg) if self.ordinal == tslib.iNaT or other.ordinal == tslib.iNaT: return _nat_scalar_rules[op] return PyObject_RichCompareBool(self.ordinal, other.ordinal, op) @@ -758,7 +776,7 @@ cdef class Period(object): def _add_delta(self, other): from pandas.tseries import frequencies if isinstance(other, (timedelta, np.timedelta64, offsets.Tick, Timedelta)): - offset = frequencies.to_offset(self.freq) + offset = frequencies.to_offset(self.freq.rule_code) if isinstance(offset, offsets.Tick): nanos = tslib._delta_to_nanoseconds(other) offset_nanos = tslib._delta_to_nanoseconds(offset) @@ -769,18 +787,21 @@ cdef class Period(object): else: ordinal = self.ordinal + (nanos // offset_nanos) return Period(ordinal=ordinal, freq=self.freq) + msg = 'Input cannnot be converted to Period(freq={0})' + raise ValueError(msg) elif isinstance(other, offsets.DateOffset): freqstr = frequencies.get_standard_freq(other) base = frequencies.get_base_alias(freqstr) - - if base == self.freq: + if base == self.freq.rule_code: if self.ordinal == tslib.iNaT: ordinal = self.ordinal else: ordinal = self.ordinal + other.n return Period(ordinal=ordinal, freq=self.freq) - - raise ValueError("Input has different freq from Period(freq={0})".format(self.freq)) + msg = _DIFFERENT_FREQ_ERROR.format(self.freqstr, other.freqstr) + raise ValueError(msg) + else: # pragma no cover + return NotImplemented def __add__(self, other): if isinstance(other, (timedelta, np.timedelta64, @@ -790,7 +811,7 @@ cdef class Period(object): if self.ordinal == tslib.iNaT: ordinal = self.ordinal else: - ordinal = self.ordinal + other + ordinal = self.ordinal + other * self.freq.n return Period(ordinal=ordinal, freq=self.freq) else: # pragma: no cover return NotImplemented @@ -804,7 +825,7 @@ cdef class Period(object): if self.ordinal == tslib.iNaT: ordinal = self.ordinal else: - ordinal = self.ordinal - other + ordinal = self.ordinal - other * self.freq.n return Period(ordinal=ordinal, freq=self.freq) elif isinstance(other, Period): if other.freq != self.freq: @@ -836,13 +857,18 @@ cdef class Period(object): base1, mult1 = _gfc(self.freq) base2, mult2 = _gfc(freq) - if mult2 != 1: - raise ValueError('Only mult == 1 supported') - - end = how == 'E' - new_ordinal = period_asfreq(self.ordinal, base1, base2, end) + if self.ordinal == tslib.iNaT: + ordinal = self.ordinal + else: + # mult1 can't be negative or 0 + end = how == 'E' + if end: + ordinal = self.ordinal + mult1 - 1 + else: + ordinal = self.ordinal + ordinal = period_asfreq(ordinal, base1, base2, end) - return Period(ordinal=new_ordinal, freq=base2) + return Period(ordinal=ordinal, freq=freq) @property def start_time(self): @@ -853,7 +879,8 @@ cdef class Period(object): if self.ordinal == tslib.iNaT: ordinal = self.ordinal else: - ordinal = (self + 1).start_time.value - 1 + # freq.n can't be negative or 0 + ordinal = (self + self.freq.n).start_time.value - 1 return Timestamp(ordinal) def to_timestamp(self, freq=None, how='start', tz=None): @@ -947,14 +974,15 @@ cdef class Period(object): def __str__(self): return self.__unicode__() + @property + def freqstr(self): + return self.freq.freqstr + def __repr__(self): - from pandas.tseries import frequencies from pandas.tseries.frequencies import get_freq_code as _gfc base, mult = _gfc(self.freq) formatted = period_format(self.ordinal, base) - freqstr = frequencies._reverse_period_code_map[base] - - return "Period('%s', '%s')" % (formatted, freqstr) + return "Period('%s', '%s')" % (formatted, self.freqstr) def __unicode__(self): """ @@ -1123,9 +1151,6 @@ def _ordinal_from_fields(year, month, quarter, day, hour, minute, second, freq): from pandas.tseries.frequencies import get_freq_code as _gfc base, mult = _gfc(freq) - if mult != 1: - raise ValueError('Only mult == 1 supported') - if quarter is not None: year, month = _quarter_to_myear(year, quarter, freq) diff --git a/pandas/tseries/base.py b/pandas/tseries/base.py index 96c3883f7cbf3..912a0c3f88405 100644 --- a/pandas/tseries/base.py +++ b/pandas/tseries/base.py @@ -13,7 +13,7 @@ import pandas.lib as lib from pandas.core.index import Index from pandas.util.decorators import Appender, cache_readonly -from pandas.tseries.frequencies import infer_freq, to_offset, Resolution +import pandas.tseries.frequencies as frequencies import pandas.algos as _algos @@ -136,7 +136,7 @@ def inferred_freq(self): frequency. """ try: - return infer_freq(self) + return frequencies.infer_freq(self) except ValueError: return None @@ -260,7 +260,7 @@ def min(self, axis=None): if self.hasnans: mask = i8 == tslib.iNaT - min_stamp = self[~mask].asi8.min() + min_stamp = i8[~mask].min() else: min_stamp = i8.min() return self._box_func(min_stamp) @@ -303,7 +303,7 @@ def max(self, axis=None): if self.hasnans: mask = i8 == tslib.iNaT - max_stamp = self[~mask].asi8.max() + max_stamp = i8[~mask].max() else: max_stamp = i8.max() return self._box_func(max_stamp) @@ -352,15 +352,14 @@ def _format_attrs(self): @cache_readonly def _resolution(self): - from pandas.tseries.frequencies import Resolution - return Resolution.get_reso_from_freq(self.freqstr) + return frequencies.Resolution.get_reso_from_freq(self.freqstr) @cache_readonly def resolution(self): """ Returns day, hour, minute, second, millisecond or microsecond """ - return Resolution.get_str(self._resolution) + return frequencies.Resolution.get_str(self._resolution) def _convert_scalar_indexer(self, key, kind=None): """ @@ -509,7 +508,7 @@ def shift(self, n, freq=None): """ if freq is not None and freq != self.freq: if isinstance(freq, compat.string_types): - freq = to_offset(freq) + freq = frequencies.to_offset(freq) result = Index.shift(self, n, freq) if hasattr(self,'tz'): diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 7e5c3af43c861..9349e440eb9e9 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -175,7 +175,7 @@ def get_to_timestamp_base(base): def get_freq_group(freq): """ - Return frequency code group of given frequency str. + Return frequency code group of given frequency str or offset. Example ------- @@ -185,9 +185,16 @@ def get_freq_group(freq): >>> get_freq_group('W-FRI') 4000 """ + if isinstance(freq, offsets.DateOffset): + freq = freq.rule_code + if isinstance(freq, compat.string_types): base, mult = get_freq_code(freq) freq = base + elif isinstance(freq, int): + pass + else: + raise ValueError('input must be str, offset or int') return (freq // 1000) * 1000 @@ -592,7 +599,7 @@ def get_standard_freq(freq): return None if isinstance(freq, DateOffset): - return get_offset_name(freq) + return freq.rule_code code, stride = get_freq_code(freq) return _get_freq_str(code, stride) diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index ec416efe1079f..fb6929c77f6b0 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -444,7 +444,10 @@ def _beg_apply_index(self, i, freq): """Offsets index to beginning of Period frequency""" off = i.to_perioddelta('D') - base_period = i.to_period(freq) + + from pandas.tseries.frequencies import get_freq_code + base, mult = get_freq_code(freq) + base_period = i.to_period(base) if self.n < 0: # when subtracting, dates on start roll to prior roll = np.where(base_period.to_timestamp() == i - off, @@ -459,7 +462,11 @@ def _end_apply_index(self, i, freq): """Offsets index to end of Period frequency""" off = i.to_perioddelta('D') - base_period = i.to_period(freq) + + import pandas.tseries.frequencies as frequencies + from pandas.tseries.frequencies import get_freq_code + base, mult = get_freq_code(freq) + base_period = i.to_period(base) if self.n > 0: # when adding, dtates on end roll to next roll = np.where(base_period.to_timestamp(how='end') == i - off, diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py index 56d7d45120fdc..832791fc6933c 100644 --- a/pandas/tseries/period.py +++ b/pandas/tseries/period.py @@ -56,6 +56,8 @@ def dt64arr_to_periodarr(data, freq, tz): # --- Period index sketch +_DIFFERENT_FREQ_ERROR = "Input has different freq={1} from PeriodIndex(freq={0})" + def _period_index_cmp(opname, nat_result=False): """ Wrap comparison operations to convert datetime-like to datetime64 @@ -63,13 +65,16 @@ def _period_index_cmp(opname, nat_result=False): def wrapper(self, other): if isinstance(other, Period): func = getattr(self.values, opname) + other_base, _ = _gfc(other.freq) if other.freq != self.freq: - raise AssertionError("Frequencies must be equal") + msg = _DIFFERENT_FREQ_ERROR.format(self.freqstr, other.freqstr) + raise ValueError(msg) result = func(other.ordinal) elif isinstance(other, PeriodIndex): if other.freq != self.freq: - raise AssertionError("Frequencies must be equal") + msg = _DIFFERENT_FREQ_ERROR.format(self.freqstr, other.freqstr) + raise ValueError(msg) result = getattr(self.values, opname)(other.values) @@ -162,8 +167,6 @@ class PeriodIndex(DatelikeOps, DatetimeIndexOpsMixin, Int64Index): def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None, periods=None, copy=False, name=None, tz=None, **kwargs): - freq = frequencies.get_standard_freq(freq) - if periods is not None: if is_float(periods): periods = int(periods) @@ -237,8 +240,8 @@ def _from_arraylike(cls, data, freq, tz): else: base1, _ = _gfc(data.freq) base2, _ = _gfc(freq) - data = period.period_asfreq_arr(data.values, base1, - base2, 1) + data = period.period_asfreq_arr(data.values, + base1, base2, 1) else: if freq is None and len(data) > 0: freq = getattr(data[0], 'freq', None) @@ -269,11 +272,9 @@ def _simple_new(cls, values, name=None, freq=None, **kwargs): result = object.__new__(cls) result._data = values result.name = name - if freq is None: - raise ValueError('freq not specified') - result.freq = freq - + raise ValueError('freq is not specified') + result.freq = Period._maybe_convert_freq(freq) result._reset_identity() return result @@ -352,7 +353,8 @@ def astype(self, dtype): def searchsorted(self, key, side='left'): if isinstance(key, Period): if key.freq != self.freq: - raise ValueError("Different period frequency: %s" % key.freq) + msg = _DIFFERENT_FREQ_ERROR.format(self.freqstr, key.freqstr) + raise ValueError(msg) key = key.ordinal elif isinstance(key, compat.string_types): key = Period(key, freq=self.freq).ordinal @@ -375,10 +377,6 @@ def is_full(self): values = self.values return ((values[1:] - values[:-1]) < 2).all() - @property - def freqstr(self): - return self.freq - def asfreq(self, freq=None, how='E'): """ Convert the PeriodIndex to the specified frequency `freq`. @@ -425,11 +423,20 @@ def asfreq(self, freq=None, how='E'): base1, mult1 = _gfc(self.freq) base2, mult2 = _gfc(freq) - if mult2 != 1: - raise ValueError('Only mult == 1 supported') - + asi8 = self.asi8 + # mult1 can't be negative or 0 end = how == 'E' - new_data = period.period_asfreq_arr(self.values, base1, base2, end) + if end: + ordinal = asi8 + mult1 - 1 + else: + ordinal = asi8 + + new_data = period.period_asfreq_arr(ordinal, base1, base2, end) + + if self.hasnans: + mask = asi8 == tslib.iNaT + new_data[mask] = tslib.iNaT + return self._simple_new(new_data, self.name, freq=freq) def to_datetime(self, dayfirst=False): @@ -504,7 +511,7 @@ def to_timestamp(self, freq=None, how='start'): def _maybe_convert_timedelta(self, other): if isinstance(other, (timedelta, np.timedelta64, offsets.Tick, Timedelta)): - offset = frequencies.to_offset(self.freq) + offset = frequencies.to_offset(self.freq.rule_code) if isinstance(offset, offsets.Tick): nanos = tslib._delta_to_nanoseconds(other) offset_nanos = tslib._delta_to_nanoseconds(offset) @@ -513,8 +520,7 @@ def _maybe_convert_timedelta(self, other): elif isinstance(other, offsets.DateOffset): freqstr = frequencies.get_standard_freq(other) base = frequencies.get_base_alias(freqstr) - - if base == self.freq: + if base == self.freq.rule_code: return other.n raise ValueError("Input has different freq from PeriodIndex(freq={0})".format(self.freq)) @@ -536,7 +542,7 @@ def shift(self, n): shifted : PeriodIndex """ mask = self.values == tslib.iNaT - values = self.values + n + values = self.values + n * self.freq.n values[mask] = tslib.iNaT return PeriodIndex(data=values, name=self.name, freq=self.freq) @@ -616,7 +622,7 @@ def get_loc(self, key, method=None, tolerance=None): except TypeError: pass - key = Period(key, self.freq) + key = Period(key, freq=self.freq) try: return Index.get_loc(self, key.ordinal, method, tolerance) except KeyError: @@ -688,7 +694,6 @@ def _get_string_slice(self, key): 'ordered time series') key, parsed, reso = parse_time_string(key, self.freq) - grp = frequencies.Resolution.get_freq_group(reso) freqn = frequencies.get_freq_group(self.freq) if reso in ['day', 'hour', 'minute', 'second'] and not grp < freqn: @@ -723,8 +728,8 @@ def _assert_can_do_setop(self, other): raise ValueError('can only call with other PeriodIndex-ed objects') if self.freq != other.freq: - raise ValueError('Only like-indexed PeriodIndexes compatible ' - 'for join (for now)') + msg = _DIFFERENT_FREQ_ERROR.format(self.freqstr, other.freqstr) + raise ValueError(msg) def _wrap_union_result(self, other, result): name = self.name if self.name == other.name else None @@ -778,12 +783,12 @@ def __array_finalize__(self, obj): self.name = getattr(obj, 'name', None) self._reset_identity() - def take(self, indices, axis=None): + def take(self, indices, axis=0): """ Analogous to ndarray.take """ indices = com._ensure_platform_int(indices) - taken = self.values.take(indices, axis=axis) + taken = self.asi8.take(indices, axis=axis) return self._simple_new(taken, self.name, freq=self.freq) def append(self, other): @@ -850,10 +855,8 @@ def __setstate__(self, state): data = np.empty(nd_state[1], dtype=nd_state[2]) np.ndarray.__setstate__(data, nd_state) - try: # backcompat - self.freq = own_state[1] - except: - pass + # backcompat + self.freq = Period._maybe_convert_freq(own_state[1]) else: # pragma: no cover data = np.empty(state) @@ -863,6 +866,7 @@ def __setstate__(self, state): else: raise Exception("invalid pickle state") + _unpickle_compat = __setstate__ def tz_convert(self, tz): @@ -916,10 +920,13 @@ def tz_localize(self, tz, infer_dst=False): PeriodIndex._add_datetimelike_methods() -def _get_ordinal_range(start, end, periods, freq): +def _get_ordinal_range(start, end, periods, freq, mult=1): if com._count_not_none(start, end, periods) < 2: raise ValueError('Must specify 2 of start, end, periods') + if freq is not None: + _, mult = _gfc(freq) + if start is not None: start = Period(start, freq) if end is not None: @@ -943,15 +950,16 @@ def _get_ordinal_range(start, end, periods, freq): raise ValueError('Could not infer freq from start/end') if periods is not None: + periods = periods * mult if start is None: - data = np.arange(end.ordinal - periods + 1, - end.ordinal + 1, + data = np.arange(end.ordinal - periods + mult, + end.ordinal + 1, mult, dtype=np.int64) else: - data = np.arange(start.ordinal, start.ordinal + periods, + data = np.arange(start.ordinal, start.ordinal + periods, mult, dtype=np.int64) else: - data = np.arange(start.ordinal, end.ordinal + 1, dtype=np.int64) + data = np.arange(start.ordinal, end.ordinal + 1, mult, dtype=np.int64) return data, freq @@ -975,8 +983,6 @@ def _range_from_fields(year=None, month=None, quarter=None, day=None, base = frequencies.FreqGroup.FR_QTR else: base, mult = _gfc(freq) - if mult != 1: - raise ValueError('Only mult == 1 supported') if base != frequencies.FreqGroup.FR_QTR: raise AssertionError("base must equal FR_QTR") @@ -987,9 +993,6 @@ def _range_from_fields(year=None, month=None, quarter=None, day=None, ordinals.append(val) else: base, mult = _gfc(freq) - if mult != 1: - raise ValueError('Only mult == 1 supported') - arrays = _make_field_arrays(year, month, day, hour, minute, second) for y, mth, d, h, mn, s in zip(*arrays): ordinals.append(period.period_ordinal(y, mth, d, h, mn, s, 0, 0, base)) diff --git a/pandas/tseries/tests/test_base.py b/pandas/tseries/tests/test_base.py index 5741e9cf9c093..03c0e3f778e99 100644 --- a/pandas/tseries/tests/test_base.py +++ b/pandas/tseries/tests/test_base.py @@ -1535,10 +1535,10 @@ def _check_freq(index, expected_index): self.assertEqual(result.freq, 'D') def test_order(self): - idx1 = PeriodIndex(['2011-01-01', '2011-01-02', '2011-01-03'], - freq='D', name='idx') + for freq in ['D', '2D', '4D']: + idx = PeriodIndex(['2011-01-01', '2011-01-02', '2011-01-03'], + freq=freq, name='idx') - for idx in [idx1]: ordered = idx.sort_values() self.assert_index_equal(ordered, idx) self.assertEqual(ordered.freq, idx.freq) @@ -1546,18 +1546,21 @@ def test_order(self): ordered = idx.sort_values(ascending=False) expected = idx[::-1] self.assert_index_equal(ordered, expected) - self.assertEqual(ordered.freq, 'D') + self.assertEqual(ordered.freq, expected.freq) + self.assertEqual(ordered.freq, freq) ordered, indexer = idx.sort_values(return_indexer=True) self.assert_index_equal(ordered, idx) self.assert_numpy_array_equal(indexer, np.array([0, 1, 2])) - self.assertEqual(ordered.freq, 'D') + self.assertEqual(ordered.freq, idx.freq) + self.assertEqual(ordered.freq, freq) ordered, indexer = idx.sort_values(return_indexer=True, ascending=False) expected = idx[::-1] self.assert_index_equal(ordered, expected) self.assert_numpy_array_equal(indexer, np.array([2, 1, 0])) - self.assertEqual(ordered.freq, 'D') + self.assertEqual(ordered.freq, expected.freq) + self.assertEqual(ordered.freq, freq) idx1 = PeriodIndex(['2011-01-01', '2011-01-03', '2011-01-05', '2011-01-02', '2011-01-01'], freq='D', name='idx1') @@ -1610,6 +1613,7 @@ def test_getitem(self): name='idx') self.assert_index_equal(result, expected) self.assertEqual(result.freq, expected.freq) + self.assertEqual(result.freq, 'D') result = idx[0:10:2] expected = pd.PeriodIndex(['2011-01-01', '2011-01-03', '2011-01-05', @@ -1617,6 +1621,7 @@ def test_getitem(self): freq='D', name='idx') self.assert_index_equal(result, expected) self.assertEqual(result.freq, expected.freq) + self.assertEqual(result.freq, 'D') result = idx[-20:-5:3] expected = pd.PeriodIndex(['2011-01-12', '2011-01-15', '2011-01-18', @@ -1624,6 +1629,7 @@ def test_getitem(self): freq='D', name='idx') self.assert_index_equal(result, expected) self.assertEqual(result.freq, expected.freq) + self.assertEqual(result.freq, 'D') result = idx[4::-1] expected = PeriodIndex(['2011-01-05', '2011-01-04', '2011-01-03', @@ -1631,6 +1637,7 @@ def test_getitem(self): freq='D', name='idx') self.assert_index_equal(result, expected) self.assertEqual(result.freq, expected.freq) + self.assertEqual(result.freq, 'D') def test_take(self): #GH 10295 @@ -1647,6 +1654,7 @@ def test_take(self): expected = pd.period_range('2011-01-01', '2011-01-03', freq='D', name='idx') self.assert_index_equal(result, expected) + self.assertEqual(result.freq, 'D') self.assertEqual(result.freq, expected.freq) result = idx.take([0, 2, 4]) @@ -1654,24 +1662,28 @@ def test_take(self): freq='D', name='idx') self.assert_index_equal(result, expected) self.assertEqual(result.freq, expected.freq) + self.assertEqual(result.freq, 'D') result = idx.take([7, 4, 1]) expected = pd.PeriodIndex(['2011-01-08', '2011-01-05', '2011-01-02'], freq='D', name='idx') self.assert_index_equal(result, expected) self.assertEqual(result.freq, expected.freq) + self.assertEqual(result.freq, 'D') result = idx.take([3, 2, 5]) expected = PeriodIndex(['2011-01-04', '2011-01-03', '2011-01-06'], freq='D', name='idx') self.assert_index_equal(result, expected) self.assertEqual(result.freq, expected.freq) + self.assertEqual(result.freq, 'D') result = idx.take([-3, 2, 5]) expected = PeriodIndex(['2011-01-29', '2011-01-03', '2011-01-06'], freq='D', name='idx') self.assert_index_equal(result, expected) self.assertEqual(result.freq, expected.freq) + self.assertEqual(result.freq, 'D') if __name__ == '__main__': diff --git a/pandas/tseries/tests/test_frequencies.py b/pandas/tseries/tests/test_frequencies.py index 070363460f791..b783459cbfe95 100644 --- a/pandas/tseries/tests/test_frequencies.py +++ b/pandas/tseries/tests/test_frequencies.py @@ -129,9 +129,48 @@ def test_anchored_shortcuts(): expected = frequencies.to_offset('W-SUN') assert(result == expected) - result = frequencies.to_offset('Q') - expected = frequencies.to_offset('Q-DEC') - assert(result == expected) + result1 = frequencies.to_offset('Q') + result2 = frequencies.to_offset('Q-DEC') + expected = offsets.QuarterEnd(startingMonth=12) + assert(result1 == expected) + assert(result2 == expected) + + result1 = frequencies.to_offset('Q-MAY') + expected = offsets.QuarterEnd(startingMonth=5) + assert(result1 == expected) + + +def test_get_rule_month(): + result = frequencies._get_rule_month('W') + assert(result == 'DEC') + result = frequencies._get_rule_month(offsets.Week()) + assert(result == 'DEC') + + result = frequencies._get_rule_month('D') + assert(result == 'DEC') + result = frequencies._get_rule_month(offsets.Day()) + assert(result == 'DEC') + + result = frequencies._get_rule_month('Q') + assert(result == 'DEC') + result = frequencies._get_rule_month(offsets.QuarterEnd(startingMonth=12)) + print(result == 'DEC') + + result = frequencies._get_rule_month('Q-JAN') + assert(result == 'JAN') + result = frequencies._get_rule_month(offsets.QuarterEnd(startingMonth=1)) + assert(result == 'JAN') + + result = frequencies._get_rule_month('A-DEC') + assert(result == 'DEC') + result = frequencies._get_rule_month(offsets.YearEnd()) + assert(result == 'DEC') + + result = frequencies._get_rule_month('A-MAY') + assert(result == 'MAY') + result = frequencies._get_rule_month(offsets.YearEnd(month=5)) + assert(result == 'MAY') + class TestFrequencyCode(tm.TestCase): @@ -154,6 +193,23 @@ def test_freq_code(self): result = frequencies.get_freq_group(code) self.assertEqual(result, code // 1000 * 1000) + def test_freq_group(self): + self.assertEqual(frequencies.get_freq_group('A'), 1000) + self.assertEqual(frequencies.get_freq_group('3A'), 1000) + self.assertEqual(frequencies.get_freq_group('-1A'), 1000) + self.assertEqual(frequencies.get_freq_group('A-JAN'), 1000) + self.assertEqual(frequencies.get_freq_group('A-MAY'), 1000) + self.assertEqual(frequencies.get_freq_group(offsets.YearEnd()), 1000) + self.assertEqual(frequencies.get_freq_group(offsets.YearEnd(month=1)), 1000) + self.assertEqual(frequencies.get_freq_group(offsets.YearEnd(month=5)), 1000) + + self.assertEqual(frequencies.get_freq_group('W'), 4000) + self.assertEqual(frequencies.get_freq_group('W-MON'), 4000) + self.assertEqual(frequencies.get_freq_group('W-FRI'), 4000) + self.assertEqual(frequencies.get_freq_group(offsets.Week()), 4000) + self.assertEqual(frequencies.get_freq_group(offsets.Week(weekday=1)), 4000) + self.assertEqual(frequencies.get_freq_group(offsets.Week(weekday=5)), 4000) + def test_get_to_timestamp_base(self): tsb = frequencies.get_to_timestamp_base diff --git a/pandas/tseries/tests/test_period.py b/pandas/tseries/tests/test_period.py index cdd9d036fcadc..c828d6d7effb6 100644 --- a/pandas/tseries/tests/test_period.py +++ b/pandas/tseries/tests/test_period.py @@ -59,6 +59,10 @@ def test_period_cons_quarterly(self): p = Period(stamp, freq=freq) self.assertEqual(p, exp) + stamp = exp.to_timestamp('3D', how='end') + p = Period(stamp, freq=freq) + self.assertEqual(p, exp) + def test_period_cons_annual(self): # bugs in scikits.timeseries for month in MONTHS: @@ -82,28 +86,109 @@ def test_period_cons_nat(self): p = Period('NaT', freq='M') self.assertEqual(p.ordinal, tslib.iNaT) self.assertEqual(p.freq, 'M') + self.assertEqual((p + 1).ordinal, tslib.iNaT) p = Period('nat', freq='W-SUN') self.assertEqual(p.ordinal, tslib.iNaT) self.assertEqual(p.freq, 'W-SUN') + self.assertEqual((p + 1).ordinal, tslib.iNaT) p = Period(tslib.iNaT, freq='D') self.assertEqual(p.ordinal, tslib.iNaT) self.assertEqual(p.freq, 'D') + self.assertEqual((p + 1).ordinal, tslib.iNaT) + + p = Period(tslib.iNaT, freq='3D') + self.assertEqual(p.ordinal, tslib.iNaT) + self.assertEqual(p.freq, offsets.Day(3)) + self.assertEqual(p.freqstr, '3D') + self.assertEqual((p + 1).ordinal, tslib.iNaT) self.assertRaises(ValueError, Period, 'NaT') + def test_period_cons_mult(self): + p1 = Period('2011-01', freq='3M') + p2 = Period('2011-01', freq='M') + self.assertEqual(p1.ordinal, p2.ordinal) + + self.assertEqual(p1.freq, offsets.MonthEnd(3)) + self.assertEqual(p1.freqstr, '3M') + + self.assertEqual(p2.freq, offsets.MonthEnd()) + self.assertEqual(p2.freqstr, 'M') + + result = p1 + 1 + self.assertEqual(result.ordinal, (p2 + 3).ordinal) + self.assertEqual(result.freq, p1.freq) + self.assertEqual(result.freqstr, '3M') + + result = p1 - 1 + self.assertEqual(result.ordinal, (p2 - 3).ordinal) + self.assertEqual(result.freq, p1.freq) + self.assertEqual(result.freqstr, '3M') + + msg = ('Frequency must be positive, because it' + ' represents span: -3M') + with tm.assertRaisesRegexp(ValueError, msg): + Period('2011-01', freq='-3M') + + msg = ('Frequency must be positive, because it' + ' represents span: 0M') + with tm.assertRaisesRegexp(ValueError, msg): + Period('2011-01', freq='0M') + def test_timestamp_tz_arg(self): + tm._skip_if_no_pytz() import pytz - p = Period('1/1/2005', freq='M').to_timestamp(tz='Europe/Brussels') - self.assertEqual(p.tz, - pytz.timezone('Europe/Brussels').normalize(p).tzinfo) + for case in ['Europe/Brussels', 'Asia/Tokyo', 'US/Pacific']: + p = Period('1/1/2005', freq='M').to_timestamp(tz=case) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(case) + exp_zone = pytz.timezone(case).normalize(p) + + self.assertEqual(p, exp) + self.assertEqual(p.tz, exp_zone.tzinfo) + self.assertEqual(p.tz, exp.tz) + + p = Period('1/1/2005', freq='3H').to_timestamp(tz=case) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(case) + exp_zone = pytz.timezone(case).normalize(p) + + self.assertEqual(p, exp) + self.assertEqual(p.tz, exp_zone.tzinfo) + self.assertEqual(p.tz, exp.tz) + + p = Period('1/1/2005', freq='A').to_timestamp(freq='A', tz=case) + exp = Timestamp('31/12/2005', tz='UTC').tz_convert(case) + exp_zone = pytz.timezone(case).normalize(p) + + self.assertEqual(p, exp) + self.assertEqual(p.tz, exp_zone.tzinfo) + self.assertEqual(p.tz, exp.tz) + + p = Period('1/1/2005', freq='A').to_timestamp(freq='3H', tz=case) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(case) + exp_zone = pytz.timezone(case).normalize(p) + + self.assertEqual(p, exp) + self.assertEqual(p.tz, exp_zone.tzinfo) + self.assertEqual(p.tz, exp.tz) def test_timestamp_tz_arg_dateutil(self): from pandas.tslib import _dateutil_gettz as gettz from pandas.tslib import maybe_get_tz - p = Period('1/1/2005', freq='M').to_timestamp(tz=maybe_get_tz('dateutil/Europe/Brussels')) - self.assertEqual(p.tz, gettz('Europe/Brussels')) + for case in ['dateutil/Europe/Brussels', 'dateutil/Asia/Tokyo', + 'dateutil/US/Pacific']: + p = Period('1/1/2005', freq='M').to_timestamp(tz=maybe_get_tz(case)) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(case) + self.assertEqual(p, exp) + self.assertEqual(p.tz, gettz(case.split('/', 1)[1])) + self.assertEqual(p.tz, exp.tz) + + p = Period('1/1/2005', freq='M').to_timestamp(freq='3H', tz=maybe_get_tz(case)) + exp = Timestamp('1/1/2005', tz='UTC').tz_convert(case) + self.assertEqual(p, exp) + self.assertEqual(p.tz, gettz(case.split('/', 1)[1])) + self.assertEqual(p.tz, exp.tz) def test_timestamp_tz_arg_dateutil_from_string(self): from pandas.tslib import _dateutil_gettz as gettz @@ -117,6 +202,21 @@ def test_timestamp_nat_tz(self): t = Period('NaT', freq='M').to_timestamp(tz='Asia/Tokyo') self.assertTrue(t is tslib.NaT) + def test_timestamp_mult(self): + p = pd.Period('2011-01', freq='M') + self.assertEqual(p.to_timestamp(how='S'), pd.Timestamp('2011-01-01')) + self.assertEqual(p.to_timestamp(how='E'), pd.Timestamp('2011-01-31')) + + p = pd.Period('2011-01', freq='3M') + self.assertEqual(p.to_timestamp(how='S'), pd.Timestamp('2011-01-01')) + self.assertEqual(p.to_timestamp(how='E'), pd.Timestamp('2011-03-31')) + + def test_timestamp_nat_mult(self): + for freq in ['M', '3M']: + p = pd.Period('NaT', freq=freq) + self.assertTrue(p.to_timestamp(how='S') is pd.NaT) + self.assertTrue(p.to_timestamp(how='E') is pd.NaT) + def test_period_constructor(self): i1 = Period('1/1/2005', freq='M') i2 = Period('Jan 2005') @@ -252,9 +352,87 @@ def test_period_constructor(self): self.assertRaises(ValueError, Period, '2007-1-1', freq='X') + + def test_period_constructor_offsets(self): + self.assertEqual(Period('1/1/2005', freq=offsets.MonthEnd()), + Period('1/1/2005', freq='M')) + self.assertEqual(Period('2005', freq=offsets.YearEnd()), + Period('2005', freq='A')) + self.assertEqual(Period('2005', freq=offsets.MonthEnd()), + Period('2005', freq='M')) + self.assertEqual(Period('3/10/12', freq=offsets.BusinessDay()), + Period('3/10/12', freq='B')) + self.assertEqual(Period('3/10/12', freq=offsets.Day()), + Period('3/10/12', freq='D')) + + self.assertEqual(Period(year=2005, quarter=1, + freq=offsets.QuarterEnd(startingMonth=12)), + Period(year=2005, quarter=1, freq='Q')) + self.assertEqual(Period(year=2005, quarter=2, + freq=offsets.QuarterEnd(startingMonth=12)), + Period(year=2005, quarter=2, freq='Q')) + + self.assertEqual(Period(year=2005, month=3, day=1, freq=offsets.Day()), + Period(year=2005, month=3, day=1, freq='D')) + self.assertEqual(Period(year=2012, month=3, day=10, freq=offsets.BDay()), + Period(year=2012, month=3, day=10, freq='B')) + + expected = Period('2005-03-01', freq='3D') + self.assertEqual(Period(year=2005, month=3, day=1, freq=offsets.Day(3)), + expected) + self.assertEqual(Period(year=2005, month=3, day=1, freq='3D'), + expected) + + self.assertEqual(Period(year=2012, month=3, day=10, freq=offsets.BDay(3)), + Period(year=2012, month=3, day=10, freq='3B')) + + self.assertEqual(Period(200701, freq=offsets.MonthEnd()), + Period(200701, freq='M')) + + i1 = Period(ordinal=200701, freq=offsets.MonthEnd()) + i2 = Period(ordinal=200701, freq='M') + self.assertEqual(i1, i2) + self.assertEqual(i1.year, 18695) + self.assertEqual(i2.year, 18695) + + i1 = Period(datetime(2007, 1, 1), freq='M') + i2 = Period('200701', freq='M') + self.assertEqual(i1, i2) + + i1 = Period(date(2007, 1, 1), freq='M') + i2 = Period(datetime(2007, 1, 1), freq='M') + i3 = Period(np.datetime64('2007-01-01'), freq='M') + i4 = Period(np.datetime64('2007-01-01 00:00:00Z'), freq='M') + i5 = Period(np.datetime64('2007-01-01 00:00:00.000Z'), freq='M') + self.assertEqual(i1, i2) + self.assertEqual(i1, i3) + self.assertEqual(i1, i4) + self.assertEqual(i1, i5) + + i1 = Period('2007-01-01 09:00:00.001') + expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1000), freq='L') + self.assertEqual(i1, expected) + + expected = Period(np.datetime64('2007-01-01 09:00:00.001Z'), freq='L') + self.assertEqual(i1, expected) + + i1 = Period('2007-01-01 09:00:00.00101') + expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1010), freq='U') + self.assertEqual(i1, expected) + + expected = Period(np.datetime64('2007-01-01 09:00:00.00101Z'), + freq='U') + self.assertEqual(i1, expected) + + self.assertRaises(ValueError, Period, ordinal=200701) + + self.assertRaises(ValueError, Period, '2007-1-1', freq='X') + + def test_freq_str(self): i1 = Period('1982', freq='Min') - self.assertNotEqual(i1.freq[0], '1') + self.assertEqual(i1.freq, offsets.Minute()) + self.assertEqual(i1.freqstr, 'T') def test_repr(self): p = Period('Jan-2000') @@ -297,11 +475,14 @@ def test_to_timestamp(self): aliases = ['s', 'StarT', 'BEGIn'] for a in aliases: self.assertEqual(start_ts, p.to_timestamp('D', how=a)) + # freq with mult should not affect to the result + self.assertEqual(start_ts, p.to_timestamp('3D', how=a)) end_ts = p.to_timestamp(how='E') aliases = ['e', 'end', 'FINIsH'] for a in aliases: self.assertEqual(end_ts, p.to_timestamp('D', how=a)) + self.assertEqual(end_ts, p.to_timestamp('3D', how=a)) from_lst = ['A', 'Q', 'M', 'W', 'B', 'D', 'H', 'Min', 'S'] @@ -325,10 +506,15 @@ def _ex(p): result = p.to_timestamp('H', how='end') expected = datetime(1985, 12, 31, 23) self.assertEqual(result, expected) + result = p.to_timestamp('3H', how='end') + self.assertEqual(result, expected) result = p.to_timestamp('T', how='end') expected = datetime(1985, 12, 31, 23, 59) self.assertEqual(result, expected) + result = p.to_timestamp('2T', how='end') + self.assertEqual(result, expected) + result = p.to_timestamp(how='end') expected = datetime(1985, 12, 31) @@ -341,8 +527,10 @@ def _ex(p): self.assertEqual(result, expected) result = p.to_timestamp('S', how='start') self.assertEqual(result, expected) - - assertRaisesRegexp(ValueError, 'Only mult == 1', p.to_timestamp, '5t') + result = p.to_timestamp('3H', how='start') + self.assertEqual(result, expected) + result = p.to_timestamp('5S', how='start') + self.assertEqual(result, expected) p = Period('NaT', freq='W') self.assertTrue(p.to_timestamp() is tslib.NaT) @@ -354,9 +542,9 @@ def test_start_time(self): p = Period('2012', freq=f) self.assertEqual(p.start_time, xp) self.assertEqual(Period('2012', freq='B').start_time, - datetime(2012, 1, 2)) + datetime(2012, 1, 2)) self.assertEqual(Period('2012', freq='W').start_time, - datetime(2011, 12, 26)) + datetime(2011, 12, 26)) p = Period('NaT', freq='W') self.assertTrue(p.start_time is tslib.NaT) @@ -489,19 +677,20 @@ def test_properties_daily(self): def test_properties_hourly(self): # Test properties on Periods with hourly frequency. - h_date = Period(freq='H', year=2007, month=1, day=1, hour=0) - # - assert_equal(h_date.year, 2007) - assert_equal(h_date.quarter, 1) - assert_equal(h_date.month, 1) - assert_equal(h_date.day, 1) - assert_equal(h_date.weekday, 0) - assert_equal(h_date.dayofyear, 1) - assert_equal(h_date.hour, 0) - assert_equal(h_date.days_in_month, 31) - assert_equal(Period(freq='H', year=2012, month=2, day=1, - hour=0).days_in_month, 29) - # + h_date1 = Period(freq='H', year=2007, month=1, day=1, hour=0) + h_date2 = Period(freq='2H', year=2007, month=1, day=1, hour=0) + + for h_date in [h_date1, h_date2]: + assert_equal(h_date.year, 2007) + assert_equal(h_date.quarter, 1) + assert_equal(h_date.month, 1) + assert_equal(h_date.day, 1) + assert_equal(h_date.weekday, 0) + assert_equal(h_date.dayofyear, 1) + assert_equal(h_date.hour, 0) + assert_equal(h_date.days_in_month, 31) + assert_equal(Period(freq='H', year=2012, month=2, day=1, + hour=0).days_in_month, 29) def test_properties_minutely(self): # Test properties on Periods with minutely frequency. @@ -556,9 +745,15 @@ def test_pnow(self): exp = Period(dt, freq='D') self.assertEqual(val, exp) + val2 = period.pnow('2D') + exp2 = Period(dt, freq='2D') + self.assertEqual(val2, exp2) + self.assertEqual(val.ordinal, val2.ordinal) + self.assertEqual(val.ordinal, exp2.ordinal) + def test_constructor_corner(self): - self.assertRaises(ValueError, Period, year=2007, month=1, - freq='2M') + expected = Period('2007-01', freq='2M') + self.assertEqual(Period(year=2007, month=1, freq='2M'), expected) self.assertRaises(ValueError, Period, datetime.now()) self.assertRaises(ValueError, Period, datetime.now().date()) @@ -613,7 +808,13 @@ class TestFreqConversion(tm.TestCase): def test_asfreq_corner(self): val = Period(freq='A', year=2007) - self.assertRaises(ValueError, val.asfreq, '5t') + result1 = val.asfreq('5t') + result2 = val.asfreq('t') + expected = Period('2007-12-31 23:59', freq='t') + self.assertEqual(result1.ordinal, expected.ordinal) + self.assertEqual(result1.freqstr, '5T') + self.assertEqual(result2.ordinal, expected.ordinal) + self.assertEqual(result2.freqstr, 'T') def test_conv_annual(self): # frequency conversion tests: from Annual Frequency @@ -795,7 +996,6 @@ def test_conv_monthly(self): def test_conv_weekly(self): # frequency conversion tests: from Weekly Frequency - ival_W = Period(freq='W', year=2007, month=1, day=1) ival_WSUN = Period(freq='W', year=2007, month=1, day=7) @@ -1311,6 +1511,92 @@ def test_asfreq_nat(self): self.assertEqual(result.ordinal, tslib.iNaT) self.assertEqual(result.freq, 'M') + def test_asfreq_mult(self): + # normal freq to mult freq + p = Period(freq='A', year=2007) + # ordinal will not change + for freq in ['3A', offsets.YearEnd(3)]: + result = p.asfreq(freq) + expected = Period('2007', freq='3A') + + self.assertEqual(result, expected) + self.assertEqual(result.ordinal, expected.ordinal) + self.assertEqual(result.freq, expected.freq) + # ordinal will not change + for freq in ['3A', offsets.YearEnd(3)]: + result = p.asfreq(freq, how='S') + expected = Period('2007', freq='3A') + + self.assertEqual(result, expected) + self.assertEqual(result.ordinal, expected.ordinal) + self.assertEqual(result.freq, expected.freq) + + # mult freq to normal freq + p = Period(freq='3A', year=2007) + # ordinal will change because how=E is the default + for freq in ['A', offsets.YearEnd()]: + result = p.asfreq(freq) + expected = Period('2009', freq='A') + + self.assertEqual(result, expected) + self.assertEqual(result.ordinal, expected.ordinal) + self.assertEqual(result.freq, expected.freq) + # ordinal will not change + for freq in ['A', offsets.YearEnd()]: + result = p.asfreq(freq, how='S') + expected = Period('2007', freq='A') + + self.assertEqual(result, expected) + self.assertEqual(result.ordinal, expected.ordinal) + self.assertEqual(result.freq, expected.freq) + + p = Period(freq='A', year=2007) + for freq in ['2M', offsets.MonthEnd(2)]: + result = p.asfreq(freq) + expected = Period('2007-12', freq='2M') + + self.assertEqual(result, expected) + self.assertEqual(result.ordinal, expected.ordinal) + self.assertEqual(result.freq, expected.freq) + for freq in ['2M', offsets.MonthEnd(2)]: + result = p.asfreq(freq, how='S') + expected = Period('2007-01', freq='2M') + + self.assertEqual(result, expected) + self.assertEqual(result.ordinal, expected.ordinal) + self.assertEqual(result.freq, expected.freq) + + p = Period(freq='3A', year=2007) + for freq in ['2M', offsets.MonthEnd(2)]: + result = p.asfreq(freq) + expected = Period('2009-12', freq='2M') + + self.assertEqual(result, expected) + self.assertEqual(result.ordinal, expected.ordinal) + self.assertEqual(result.freq, expected.freq) + for freq in ['2M', offsets.MonthEnd(2)]: + result = p.asfreq(freq, how='S') + expected = Period('2007-01', freq='2M') + + self.assertEqual(result, expected) + self.assertEqual(result.ordinal, expected.ordinal) + self.assertEqual(result.freq, expected.freq) + + def test_asfreq_mult_nat(self): + # normal freq to mult freq + for p in [Period('NaT', freq='A'), Period('NaT', freq='3A'), + Period('NaT', freq='2M'), Period('NaT', freq='3D')]: + for freq in ['3A', offsets.YearEnd(3)]: + result = p.asfreq(freq) + expected = Period('NaT', freq='3A') + self.assertEqual(result.ordinal, pd.tslib.iNaT) + self.assertEqual(result.freq, expected.freq) + + result = p.asfreq(freq, how='S') + expected = Period('NaT', freq='3A') + self.assertEqual(result.ordinal, pd.tslib.iNaT) + self.assertEqual(result.freq, expected.freq) + class TestPeriodIndex(tm.TestCase): @@ -1352,9 +1638,8 @@ def test_constructor_field_arrays(self): expected = period_range('1990Q3', '2009Q2', freq='Q-DEC') self.assertTrue(index.equals(expected)) - self.assertRaises( - ValueError, PeriodIndex, year=years, quarter=quarters, - freq='2Q-DEC') + index2 = PeriodIndex(year=years, quarter=quarters, freq='2Q-DEC') + tm.assert_numpy_array_equal(index.asi8, index2.asi8) index = PeriodIndex(year=years, quarter=quarters) self.assertTrue(index.equals(expected)) @@ -1422,6 +1707,18 @@ def test_constructor_fromarraylike(self): result = PeriodIndex(idx, freq='M') self.assertTrue(result.equals(idx)) + result = PeriodIndex(idx, freq=offsets.MonthEnd()) + self.assertTrue(result.equals(idx)) + self.assertTrue(result.freq, 'M') + + result = PeriodIndex(idx, freq='2M') + self.assertTrue(result.equals(idx)) + self.assertTrue(result.freq, '2M') + + result = PeriodIndex(idx, freq=offsets.MonthEnd(2)) + self.assertTrue(result.equals(idx)) + self.assertTrue(result.freq, '2M') + result = PeriodIndex(idx, freq='D') exp = idx.asfreq('D', 'e') self.assertTrue(result.equals(exp)) @@ -1455,6 +1752,49 @@ def test_constructor_year_and_quarter(self): p = PeriodIndex(lops) tm.assert_index_equal(p, idx) + def test_constructor_freq_mult(self): + # GH #7811 + for func in [PeriodIndex, period_range]: + # must be the same, but for sure... + pidx = func(start='2014-01', freq='2M', periods=4) + expected = PeriodIndex(['2014-01', '2014-03', '2014-05', '2014-07'], freq='M') + tm.assert_index_equal(pidx, expected) + + pidx = func(start='2014-01-02', end='2014-01-15', freq='3D') + expected = PeriodIndex(['2014-01-02', '2014-01-05', '2014-01-08', '2014-01-11', + '2014-01-14'], freq='D') + tm.assert_index_equal(pidx, expected) + + pidx = func(end='2014-01-01 17:00', freq='4H', periods=3) + expected = PeriodIndex(['2014-01-01 09:00', '2014-01-01 13:00', + '2014-01-01 17:00'], freq='4H') + tm.assert_index_equal(pidx, expected) + + msg = ('Frequency must be positive, because it' + ' represents span: -1M') + with tm.assertRaisesRegexp(ValueError, msg): + PeriodIndex(['2011-01'], freq='-1M') + + msg = ('Frequency must be positive, because it' + ' represents span: 0M') + with tm.assertRaisesRegexp(ValueError, msg): + PeriodIndex(['2011-01'], freq='0M') + + msg = ('Frequency must be positive, because it' + ' represents span: 0M') + with tm.assertRaisesRegexp(ValueError, msg): + period_range('2011-01', periods=3, freq='0M') + + def test_constructor_freq_mult_dti_compat(self): + import itertools + mults = [1, 2, 3, 4, 5] + freqs = ['A', 'M', 'D', 'T', 'S'] + for mult, freq in itertools.product(mults, freqs): + freqstr = str(mult) + freq + pidx = PeriodIndex(start='2014-04-01', freq=freqstr, periods=10) + expected = date_range(start='2014-04-01', freq=freqstr, periods=10).to_period(freq) + tm.assert_index_equal(pidx, expected) + def test_is_(self): create_index = lambda: PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009') @@ -1563,6 +1903,13 @@ def test_slice_with_zero_step_raises(self): self.assertRaisesRegexp(ValueError, 'slice step cannot be zero', lambda: ts.ix[::0]) + def test_contains(self): + rng = period_range('2007-01', freq='M', periods=10) + + self.assertTrue(Period('2007-01', freq='M') in rng) + self.assertFalse(Period('2007-01', freq='D') in rng) + self.assertFalse(Period('2007-01', freq='2M') in rng) + def test_sub(self): rng = period_range('2007-01', periods=50) @@ -1614,8 +1961,6 @@ def _get_with_delta(delta, freq='A-DEC'): exp_index = _get_with_delta(delta) self.assertTrue(result.index.equals(exp_index)) - self.assertRaises(ValueError, index.to_timestamp, '5t') - index = PeriodIndex(freq='H', start='1/1/2001', end='1/2/2001') series = Series(1, index=index, name='foo') @@ -1651,7 +1996,7 @@ def test_to_timestamp_repr_is_code(self): for z in zs: self.assertEqual( eval(repr(z)), z) - def test_to_timestamp_period_nat(self): + def test_to_timestamp_pi_nat(self): # GH 7228 index = PeriodIndex(['NaT', '2011-01', '2011-02'], freq='M', name='idx') @@ -1665,6 +2010,25 @@ def test_to_timestamp_period_nat(self): self.assertTrue(result2.equals(index)) self.assertEqual(result2.name, 'idx') + result3 = result.to_period(freq='3M') + exp = PeriodIndex(['NaT', '2011-01', '2011-02'], freq='3M', name='idx') + self.assert_index_equal(result3, exp) + self.assertEqual(result3.freqstr, '3M') + + msg = ('Frequency must be positive, because it' + ' represents span: -2A') + with tm.assertRaisesRegexp(ValueError, msg): + result.to_period(freq='-2A') + + def test_to_timestamp_pi_mult(self): + idx = PeriodIndex(['2011-01', 'NaT', '2011-02'], freq='2M', name='idx') + result = idx.to_timestamp() + expected = DatetimeIndex(['2011-01-01', 'NaT', '2011-02-01'], name='idx') + self.assert_index_equal(result, expected) + result = idx.to_timestamp(how='E') + expected = DatetimeIndex(['2011-02-28', 'NaT', '2011-03-31'], name='idx') + self.assert_index_equal(result, expected) + def test_as_frame_columns(self): rng = period_range('1/1/2000', periods=5) df = DataFrame(randn(10, 5), columns=rng) @@ -1794,7 +2158,17 @@ def _get_with_delta(delta, freq='A-DEC'): # invalid axis assertRaisesRegexp(ValueError, 'axis', df.to_timestamp, axis=2) - assertRaisesRegexp(ValueError, 'Only mult == 1', df.to_timestamp, '5t', axis=1) + + result1 = df.to_timestamp('5t', axis=1) + result2 = df.to_timestamp('t', axis=1) + expected = pd.date_range('2001-01-01', '2009-01-01', freq='AS') + self.assertTrue(isinstance(result1.columns, DatetimeIndex)) + self.assertTrue(isinstance(result2.columns, DatetimeIndex)) + self.assert_numpy_array_equal(result1.columns.asi8, expected.asi8) + self.assert_numpy_array_equal(result2.columns.asi8, expected.asi8) + # PeriodIndex.to_timestamp always use 'infer' + self.assertEqual(result1.columns.freqstr, 'AS-JAN') + self.assertEqual(result2.columns.freqstr, 'AS-JAN') def test_index_duplicate_periods(self): # monotonic @@ -2007,7 +2381,13 @@ def test_asfreq(self): self.assertEqual(pi7.asfreq('Min', 'S'), pi6) self.assertRaises(ValueError, pi7.asfreq, 'T', 'foo') - self.assertRaises(ValueError, pi1.asfreq, '5t') + result1 = pi1.asfreq('3M') + result2 = pi1.asfreq('M') + expected = PeriodIndex(freq='M', start='2001-12', end='2001-12') + self.assert_numpy_array_equal(result1.asi8, expected.asi8) + self.assertEqual(result1.freqstr, '3M') + self.assert_numpy_array_equal(result2.asi8, expected.asi8) + self.assertEqual(result2.freqstr, 'M') def test_asfreq_nat(self): idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'], freq='M') @@ -2015,6 +2395,22 @@ def test_asfreq_nat(self): expected = PeriodIndex(['2011Q1', '2011Q1', 'NaT', '2011Q2'], freq='Q') self.assertTrue(result.equals(expected)) + def test_asfreq_mult_pi(self): + pi = PeriodIndex(['2001-01', '2001-02', 'NaT', '2001-03'], freq='2M') + + for freq in ['D', '3D']: + result = pi.asfreq(freq) + exp = PeriodIndex(['2001-02-28', '2001-03-31', 'NaT', + '2001-04-30'], freq=freq) + self.assert_index_equal(result, exp) + self.assertEqual(result.freq, exp.freq) + + result = pi.asfreq(freq, how='S') + exp = PeriodIndex(['2001-01-01', '2001-02-01', 'NaT', + '2001-03-01'], freq=freq) + self.assert_index_equal(result, exp) + self.assertEqual(result.freq, exp.freq) + def test_period_index_length(self): pi = PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009') assert_equal(len(pi), 9) @@ -2120,12 +2516,19 @@ def test_dti_to_period(self): dti = DatetimeIndex(start='1/1/2005', end='12/1/2005', freq='M') pi1 = dti.to_period() pi2 = dti.to_period(freq='D') + pi3 = dti.to_period(freq='3D') self.assertEqual(pi1[0], Period('Jan 2005', freq='M')) self.assertEqual(pi2[0], Period('1/31/2005', freq='D')) + self.assertEqual(pi3[0], Period('1/31/2005', freq='3D')) self.assertEqual(pi1[-1], Period('Nov 2005', freq='M')) self.assertEqual(pi2[-1], Period('11/30/2005', freq='D')) + self.assertEqual(pi3[-1], Period('11/30/2005', freq='3D')) + + tm.assert_index_equal(pi1, period_range('1/1/2005', '11/1/2005', freq='M')) + tm.assert_index_equal(pi2, period_range('1/1/2005', '11/1/2005', freq='M').asfreq('D')) + tm.assert_index_equal(pi3, period_range('1/1/2005', '11/1/2005', freq='M').asfreq('3D')) def test_pindex_slice_index(self): pi = PeriodIndex(start='1/1/10', end='12/31/12', freq='M') @@ -2217,7 +2620,6 @@ def test_getitem_seconds(self): continue s = Series(np.random.rand(len(idx)), index=idx) - assert_series_equal(s['2013/01/01 10:00'], s[3600:3660]) assert_series_equal(s['2013/01/01 9H'], s[:3600]) for d in ['2013/01/01', '2013/01', '2013']: @@ -2318,35 +2720,35 @@ def test_to_period_monthish(self): prng = rng.to_period() self.assertEqual(prng.freq, 'M') - def test_no_multiples(self): - self.assertRaises(ValueError, period_range, '1989Q3', periods=10, - freq='2Q') - - self.assertRaises(ValueError, period_range, '1989', periods=10, - freq='2A') - self.assertRaises(ValueError, Period, '1989', freq='2A') - - # def test_pindex_multiples(self): - # pi = PeriodIndex(start='1/1/10', end='12/31/12', freq='2M') - # self.assertEqual(pi[0], Period('1/1/10', '2M')) - # self.assertEqual(pi[1], Period('3/1/10', '2M')) - - # self.assertEqual(pi[0].asfreq('6M'), pi[2].asfreq('6M')) - # self.assertEqual(pi[0].asfreq('A'), pi[2].asfreq('A')) - - # self.assertEqual(pi[0].asfreq('M', how='S'), - # Period('Jan 2010', '1M')) - # self.assertEqual(pi[0].asfreq('M', how='E'), - # Period('Feb 2010', '1M')) - # self.assertEqual(pi[1].asfreq('M', how='S'), - # Period('Mar 2010', '1M')) - - # i = Period('1/1/2010 12:05:18', '5S') - # self.assertEqual(i, Period('1/1/2010 12:05:15', '5S')) - - # i = Period('1/1/2010 12:05:18', '5S') - # self.assertEqual(i.asfreq('1S', how='E'), - # Period('1/1/2010 12:05:19', '1S')) + def test_multiples(self): + result1 = Period('1989', freq='2A') + result2 = Period('1989', freq='A') + self.assertEqual(result1.ordinal, result2.ordinal) + self.assertEqual(result1.freqstr, '2A-DEC') + self.assertEqual(result2.freqstr, 'A-DEC') + self.assertEqual(result1.freq, offsets.YearEnd(2)) + self.assertEqual(result2.freq, offsets.YearEnd()) + + self.assertEqual((result1 + 1).ordinal, result1.ordinal + 2) + self.assertEqual((result1 - 1).ordinal, result2.ordinal - 2) + + def test_pindex_multiples(self): + pi = PeriodIndex(start='1/1/11', end='12/31/11', freq='2M') + expected = PeriodIndex(['2011-01', '2011-03', '2011-05', '2011-07', + '2011-09', '2011-11'], freq='M') + tm.assert_index_equal(pi, expected) + self.assertEqual(pi.freq, offsets.MonthEnd(2)) + self.assertEqual(pi.freqstr, '2M') + + pi = period_range(start='1/1/11', end='12/31/11', freq='2M') + tm.assert_index_equal(pi, expected) + self.assertEqual(pi.freq, offsets.MonthEnd(2)) + self.assertEqual(pi.freqstr, '2M') + + pi = period_range(start='1/1/11', periods=6, freq='2M') + tm.assert_index_equal(pi, expected) + self.assertEqual(pi.freq, offsets.MonthEnd(2)) + self.assertEqual(pi.freqstr, '2M') def test_iteration(self): index = PeriodIndex(start='1/1/10', periods=4, freq='B') @@ -2412,7 +2814,8 @@ def test_align_series(self): # it works! for kind in ['inner', 'outer', 'left', 'right']: ts.align(ts[::2], join=kind) - with assertRaisesRegexp(ValueError, 'Only like-indexed'): + msg = "Input has different freq=D from PeriodIndex\\(freq=A-DEC\\)" + with assertRaisesRegexp(ValueError, msg): ts + ts.asfreq('D', how="end") def test_align_frame(self): @@ -2444,6 +2847,9 @@ def test_union(self): self.assertRaises(ValueError, index.join, index.to_timestamp()) + index3 = period_range('1/1/2000', '1/20/2000', freq='2D') + self.assertRaises(ValueError, index.join, index3) + def test_intersection(self): index = period_range('1/1/2000', '1/20/2000', freq='D') @@ -2461,6 +2867,9 @@ def test_intersection(self): index2 = period_range('1/1/2000', '1/20/2000', freq='W-WED') self.assertRaises(ValueError, index.intersection, index2) + index3 = period_range('1/1/2000', '1/20/2000', freq='2D') + self.assertRaises(ValueError, index.intersection, index3) + def test_fields(self): # year, month, day, hour, minute # second, weekofyear, week, dayofweek, weekday, dayofyear, quarter @@ -2614,7 +3023,8 @@ def test_pickle_freq(self): # GH2891 prng = period_range('1/1/2011', '1/1/2012', freq='M') new_prng = self.round_trip_pickle(prng) - self.assertEqual(new_prng.freq,'M') + self.assertEqual(new_prng.freq, offsets.MonthEnd()) + self.assertEqual(new_prng.freqstr, 'M') def test_slice_keep_name(self): idx = period_range('20010101', periods=10, freq='D', name='bob') @@ -2669,12 +3079,24 @@ def test_combine_first(self): tm.assert_series_equal(result, expected) def test_searchsorted(self): - pidx = pd.period_range('2014-01-01', periods=10, freq='D') - self.assertEqual( - pidx.searchsorted(pd.Period('2014-01-01', freq='D')), 0) - self.assertRaisesRegexp( - ValueError, 'Different period frequency: H', - lambda: pidx.searchsorted(pd.Period('2014-01-01', freq='H'))) + for freq in ['D', '2D']: + pidx = pd.PeriodIndex(['2014-01-01', '2014-01-02', '2014-01-03', + '2014-01-04', '2014-01-05'], freq=freq) + + p1 = pd.Period('2014-01-01', freq=freq) + self.assertEqual(pidx.searchsorted(p1), 0) + + p2 = pd.Period('2014-01-04', freq=freq) + self.assertEqual(pidx.searchsorted(p2), 3) + + msg = "Input has different freq=H from PeriodIndex" + with self.assertRaisesRegexp(ValueError, msg): + pidx.searchsorted(pd.Period('2014-01-01', freq='H')) + + msg = "Input has different freq=5D from PeriodIndex" + with self.assertRaisesRegexp(ValueError, msg): + pidx.searchsorted(pd.Period('2014-01-01', freq='5D')) + def test_round_trip(self): @@ -2704,186 +3126,203 @@ def test_add(self): def test_add_offset(self): # freq is DateOffset - p = Period('2011', freq='A') - self.assertEqual(p + offsets.YearEnd(2), Period('2013', freq='A')) + for freq in ['A', '2A', '3A']: + p = Period('2011', freq=freq) + self.assertEqual(p + offsets.YearEnd(2), Period('2013', freq=freq)) - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(365, 'D'), timedelta(365)]: - with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): - p + o + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p + o - p = Period('2011-03', freq='M') - self.assertEqual(p + offsets.MonthEnd(2), Period('2011-05', freq='M')) - self.assertEqual(p + offsets.MonthEnd(12), Period('2012-03', freq='M')) + for freq in ['M', '2M', '3M']: + p = Period('2011-03', freq=freq) + self.assertEqual(p + offsets.MonthEnd(2), Period('2011-05', freq=freq)) + self.assertEqual(p + offsets.MonthEnd(12), Period('2012-03', freq=freq)) - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(365, 'D'), timedelta(365)]: - with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): - p + o + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p + o # freq is Tick - p = Period('2011-04-01', freq='D') - self.assertEqual(p + offsets.Day(5), Period('2011-04-06', freq='D')) - self.assertEqual(p + offsets.Hour(24), Period('2011-04-02', freq='D')) - self.assertEqual(p + np.timedelta64(2, 'D'), Period('2011-04-03', freq='D')) - self.assertEqual(p + np.timedelta64(3600 * 24, 's'), Period('2011-04-02', freq='D')) - self.assertEqual(p + timedelta(-2), Period('2011-03-30', freq='D')) - self.assertEqual(p + timedelta(hours=48), Period('2011-04-03', freq='D')) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(4, 'h'), timedelta(hours=23)]: - with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): - p + o - - p = Period('2011-04-01 09:00', freq='H') - self.assertEqual(p + offsets.Day(2), Period('2011-04-03 09:00', freq='H')) - self.assertEqual(p + offsets.Hour(3), Period('2011-04-01 12:00', freq='H')) - self.assertEqual(p + np.timedelta64(3, 'h'), Period('2011-04-01 12:00', freq='H')) - self.assertEqual(p + np.timedelta64(3600, 's'), Period('2011-04-01 10:00', freq='H')) - self.assertEqual(p + timedelta(minutes=120), Period('2011-04-01 11:00', freq='H')) - self.assertEqual(p + timedelta(days=4, minutes=180), Period('2011-04-05 12:00', freq='H')) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: - with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): - p + o + for freq in ['D', '2D', '3D']: + p = Period('2011-04-01', freq=freq) + self.assertEqual(p + offsets.Day(5), Period('2011-04-06', freq=freq)) + self.assertEqual(p + offsets.Hour(24), Period('2011-04-02', freq=freq)) + self.assertEqual(p + np.timedelta64(2, 'D'), Period('2011-04-03', freq=freq)) + self.assertEqual(p + np.timedelta64(3600 * 24, 's'), Period('2011-04-02', freq=freq)) + self.assertEqual(p + timedelta(-2), Period('2011-03-30', freq=freq)) + self.assertEqual(p + timedelta(hours=48), Period('2011-04-03', freq=freq)) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + with tm.assertRaises(ValueError): + p + o + + for freq in ['H', '2H', '3H']: + p = Period('2011-04-01 09:00', freq=freq) + self.assertEqual(p + offsets.Day(2), Period('2011-04-03 09:00', freq=freq)) + self.assertEqual(p + offsets.Hour(3), Period('2011-04-01 12:00', freq=freq)) + self.assertEqual(p + np.timedelta64(3, 'h'), Period('2011-04-01 12:00', freq=freq)) + self.assertEqual(p + np.timedelta64(3600, 's'), Period('2011-04-01 10:00', freq=freq)) + self.assertEqual(p + timedelta(minutes=120), Period('2011-04-01 11:00', freq=freq)) + self.assertEqual(p + timedelta(days=4, minutes=180), Period('2011-04-05 12:00', freq=freq)) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: + with tm.assertRaises(ValueError): + p + o def test_add_offset_nat(self): # freq is DateOffset - p = Period('NaT', freq='A') - for o in [offsets.YearEnd(2)]: - self.assertEqual((p + o).ordinal, tslib.iNaT) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(365, 'D'), timedelta(365)]: - with tm.assertRaises(ValueError): - p + o - - p = Period('NaT', freq='M') - for o in [offsets.MonthEnd(2), offsets.MonthEnd(12)]: - self.assertEqual((p + o).ordinal, tslib.iNaT) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(365, 'D'), timedelta(365)]: - with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): - p + o + for freq in ['A', '2A', '3A']: + p = Period('NaT', freq=freq) + for o in [offsets.YearEnd(2)]: + self.assertEqual((p + o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p + o + + for freq in ['M', '2M', '3M']: + p = Period('NaT', freq=freq) + for o in [offsets.MonthEnd(2), offsets.MonthEnd(12)]: + self.assertEqual((p + o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p + o # freq is Tick - p = Period('NaT', freq='D') - for o in [offsets.Day(5), offsets.Hour(24), np.timedelta64(2, 'D'), - np.timedelta64(3600 * 24, 's'), timedelta(-2), timedelta(hours=48)]: - self.assertEqual((p + o).ordinal, tslib.iNaT) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(4, 'h'), timedelta(hours=23)]: - with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): - p + o - - p = Period('NaT', freq='H') - for o in [offsets.Day(2), offsets.Hour(3), np.timedelta64(3, 'h'), - np.timedelta64(3600, 's'), timedelta(minutes=120), - timedelta(days=4, minutes=180)]: - self.assertEqual((p + o).ordinal, tslib.iNaT) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: - with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): - p + o + for freq in ['D', '2D', '3D']: + p = Period('NaT', freq=freq) + for o in [offsets.Day(5), offsets.Hour(24), np.timedelta64(2, 'D'), + np.timedelta64(3600 * 24, 's'), timedelta(-2), timedelta(hours=48)]: + self.assertEqual((p + o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + with tm.assertRaises(ValueError): + p + o + + for freq in ['H', '2H', '3H']: + p = Period('NaT', freq=freq) + for o in [offsets.Day(2), offsets.Hour(3), np.timedelta64(3, 'h'), + np.timedelta64(3600, 's'), timedelta(minutes=120), + timedelta(days=4, minutes=180)]: + self.assertEqual((p + o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: + with tm.assertRaises(ValueError): + p + o def test_sub_offset(self): # freq is DateOffset - p = Period('2011', freq='A') - self.assertEqual(p - offsets.YearEnd(2), Period('2009', freq='A')) + for freq in ['A', '2A', '3A']: + p = Period('2011', freq=freq) + self.assertEqual(p - offsets.YearEnd(2), Period('2009', freq=freq)) - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(365, 'D'), timedelta(365)]: - with tm.assertRaises(ValueError): - p - o + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p - o - p = Period('2011-03', freq='M') - self.assertEqual(p - offsets.MonthEnd(2), Period('2011-01', freq='M')) - self.assertEqual(p - offsets.MonthEnd(12), Period('2010-03', freq='M')) + for freq in ['M', '2M', '3M']: + p = Period('2011-03', freq=freq) + self.assertEqual(p - offsets.MonthEnd(2), Period('2011-01', freq=freq)) + self.assertEqual(p - offsets.MonthEnd(12), Period('2010-03', freq=freq)) - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(365, 'D'), timedelta(365)]: - with tm.assertRaises(ValueError): - p - o + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p - o # freq is Tick - p = Period('2011-04-01', freq='D') - self.assertEqual(p - offsets.Day(5), Period('2011-03-27', freq='D')) - self.assertEqual(p - offsets.Hour(24), Period('2011-03-31', freq='D')) - self.assertEqual(p - np.timedelta64(2, 'D'), Period('2011-03-30', freq='D')) - self.assertEqual(p - np.timedelta64(3600 * 24, 's'), Period('2011-03-31', freq='D')) - self.assertEqual(p - timedelta(-2), Period('2011-04-03', freq='D')) - self.assertEqual(p - timedelta(hours=48), Period('2011-03-30', freq='D')) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(4, 'h'), timedelta(hours=23)]: - with tm.assertRaises(ValueError): - p - o - - p = Period('2011-04-01 09:00', freq='H') - self.assertEqual(p - offsets.Day(2), Period('2011-03-30 09:00', freq='H')) - self.assertEqual(p - offsets.Hour(3), Period('2011-04-01 06:00', freq='H')) - self.assertEqual(p - np.timedelta64(3, 'h'), Period('2011-04-01 06:00', freq='H')) - self.assertEqual(p - np.timedelta64(3600, 's'), Period('2011-04-01 08:00', freq='H')) - self.assertEqual(p - timedelta(minutes=120), Period('2011-04-01 07:00', freq='H')) - self.assertEqual(p - timedelta(days=4, minutes=180), Period('2011-03-28 06:00', freq='H')) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: - with tm.assertRaises(ValueError): - p - o + for freq in ['D', '2D', '3D']: + p = Period('2011-04-01', freq=freq) + self.assertEqual(p - offsets.Day(5), Period('2011-03-27', freq=freq)) + self.assertEqual(p - offsets.Hour(24), Period('2011-03-31', freq=freq)) + self.assertEqual(p - np.timedelta64(2, 'D'), Period('2011-03-30', freq=freq)) + self.assertEqual(p - np.timedelta64(3600 * 24, 's'), Period('2011-03-31', freq=freq)) + self.assertEqual(p - timedelta(-2), Period('2011-04-03', freq=freq)) + self.assertEqual(p - timedelta(hours=48), Period('2011-03-30', freq=freq)) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + with tm.assertRaises(ValueError): + p - o + + for freq in ['H', '2H', '3H']: + p = Period('2011-04-01 09:00', freq=freq) + self.assertEqual(p - offsets.Day(2), Period('2011-03-30 09:00', freq=freq)) + self.assertEqual(p - offsets.Hour(3), Period('2011-04-01 06:00', freq=freq)) + self.assertEqual(p - np.timedelta64(3, 'h'), Period('2011-04-01 06:00', freq=freq)) + self.assertEqual(p - np.timedelta64(3600, 's'), Period('2011-04-01 08:00', freq=freq)) + self.assertEqual(p - timedelta(minutes=120), Period('2011-04-01 07:00', freq=freq)) + self.assertEqual(p - timedelta(days=4, minutes=180), Period('2011-03-28 06:00', freq=freq)) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: + with tm.assertRaises(ValueError): + p - o def test_sub_offset_nat(self): # freq is DateOffset - p = Period('NaT', freq='A') - for o in [offsets.YearEnd(2)]: - self.assertEqual((p - o).ordinal, tslib.iNaT) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(365, 'D'), timedelta(365)]: - with tm.assertRaises(ValueError): - p - o - - p = Period('NaT', freq='M') - for o in [offsets.MonthEnd(2), offsets.MonthEnd(12)]: - self.assertEqual((p - o).ordinal, tslib.iNaT) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(365, 'D'), timedelta(365)]: - with tm.assertRaises(ValueError): - p - o + for freq in ['A', '2A', '3A']: + p = Period('NaT', freq=freq) + for o in [offsets.YearEnd(2)]: + self.assertEqual((p - o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p - o + + for freq in ['M', '2M', '3M']: + p = Period('NaT', freq=freq) + for o in [offsets.MonthEnd(2), offsets.MonthEnd(12)]: + self.assertEqual((p - o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p - o # freq is Tick - p = Period('NaT', freq='D') - for o in [offsets.Day(5), offsets.Hour(24), np.timedelta64(2, 'D'), - np.timedelta64(3600 * 24, 's'), timedelta(-2), timedelta(hours=48)]: - self.assertEqual((p - o).ordinal, tslib.iNaT) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(4, 'h'), timedelta(hours=23)]: - with tm.assertRaises(ValueError): - p - o - - p = Period('NaT', freq='H') - for o in [offsets.Day(2), offsets.Hour(3), np.timedelta64(3, 'h'), - np.timedelta64(3600, 's'), timedelta(minutes=120), - timedelta(days=4, minutes=180)]: - self.assertEqual((p - o).ordinal, tslib.iNaT) - - for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), - np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: - with tm.assertRaises(ValueError): - p - o + for freq in ['D', '2D', '3D']: + p = Period('NaT', freq=freq) + for o in [offsets.Day(5), offsets.Hour(24), np.timedelta64(2, 'D'), + np.timedelta64(3600 * 24, 's'), timedelta(-2), timedelta(hours=48)]: + self.assertEqual((p - o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + with tm.assertRaises(ValueError): + p - o + + for freq in ['H', '2H', '3H']: + p = Period('NaT', freq=freq) + for o in [offsets.Day(2), offsets.Hour(3), np.timedelta64(3, 'h'), + np.timedelta64(3600, 's'), timedelta(minutes=120), + timedelta(days=4, minutes=180)]: + self.assertEqual((p - o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: + with tm.assertRaises(ValueError): + p - o def test_nat_ops(self): - p = Period('NaT', freq='M') - self.assertEqual((p + 1).ordinal, tslib.iNaT) - self.assertEqual((p - 1).ordinal, tslib.iNaT) - self.assertEqual((p - Period('2011-01', freq='M')).ordinal, tslib.iNaT) - self.assertEqual((Period('2011-01', freq='M') - p).ordinal, tslib.iNaT) + for freq in ['M', '2M', '3M']: + p = Period('NaT', freq=freq) + self.assertEqual((p + 1).ordinal, tslib.iNaT) + self.assertEqual((p - 1).ordinal, tslib.iNaT) + self.assertEqual((p - Period('2011-01', freq=freq)).ordinal, tslib.iNaT) + self.assertEqual((Period('2011-01', freq=freq) - p).ordinal, tslib.iNaT) def test_pi_ops_nat(self): idx = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-04'], freq='M', name='idx') @@ -3042,27 +3481,112 @@ def test_period_nat_comp(self): self.assertEqual(left <= right, False) self.assertEqual(left >= right, False) - def test_pi_nat_comp(self): - idx1 = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-05'], freq='M') + def test_pi_pi_comp(self): + + for freq in ['M', '2M', '3M']: + base = PeriodIndex(['2011-01', '2011-02', + '2011-03', '2011-04'], freq=freq) + p = Period('2011-02', freq=freq) + + exp = np.array([False, True, False, False]) + self.assert_numpy_array_equal(base == p, exp) + + exp = np.array([True, False, True, True]) + self.assert_numpy_array_equal(base != p, exp) + + exp = np.array([False, False, True, True]) + self.assert_numpy_array_equal(base > p, exp) + + exp = np.array([True, False, False, False]) + self.assert_numpy_array_equal(base < p, exp) + + exp = np.array([False, True, True, True]) + self.assert_numpy_array_equal(base >= p, exp) - result = idx1 > Period('2011-02', freq='M') - self.assert_numpy_array_equal(result, np.array([False, False, False, True])) + exp = np.array([True, True, False, False]) + self.assert_numpy_array_equal(base <= p, exp) - result = idx1 == Period('NaT', freq='M') - self.assert_numpy_array_equal(result, np.array([False, False, False, False])) + idx = PeriodIndex(['2011-02', '2011-01', '2011-03', '2011-05'], freq=freq) - result = idx1 != Period('NaT', freq='M') - self.assert_numpy_array_equal(result, np.array([True, True, True, True])) + exp = np.array([False, False, True, False]) + self.assert_numpy_array_equal(base == idx, exp) - idx2 = PeriodIndex(['2011-02', '2011-01', '2011-04', 'NaT'], freq='M') - result = idx1 < idx2 - self.assert_numpy_array_equal(result, np.array([True, False, False, False])) + exp = np.array([True, True, False, True]) + self.assert_numpy_array_equal(base != idx, exp) - result = idx1 == idx1 - self.assert_numpy_array_equal(result, np.array([True, True, False, True])) + exp = np.array([False, True, False, False]) + self.assert_numpy_array_equal(base > idx, exp) - result = idx1 != idx1 - self.assert_numpy_array_equal(result, np.array([False, False, True, False])) + exp = np.array([True, False, False, True]) + self.assert_numpy_array_equal(base < idx, exp) + + exp = np.array([False, True, True, False]) + self.assert_numpy_array_equal(base >= idx, exp) + + exp = np.array([True, False, True, True]) + self.assert_numpy_array_equal(base <= idx, exp) + + # different base freq + msg = "Input has different freq=A-DEC from PeriodIndex" + with tm.assertRaisesRegexp(ValueError, msg): + base <= Period('2011', freq='A') + + with tm.assertRaisesRegexp(ValueError, msg): + idx = PeriodIndex(['2011', '2012', '2013', '2014'], freq='A') + base <= idx + + # different mult + msg = "Input has different freq=4M from PeriodIndex" + with tm.assertRaisesRegexp(ValueError, msg): + base <= Period('2011', freq='4M') + + with tm.assertRaisesRegexp(ValueError, msg): + idx = PeriodIndex(['2011', '2012', '2013', '2014'], freq='4M') + base <= idx + + def test_pi_nat_comp(self): + for freq in ['M', '2M', '3M']: + idx1 = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-05'], freq=freq) + + result = idx1 > Period('2011-02', freq=freq) + exp = np.array([False, False, False, True]) + self.assert_numpy_array_equal(result, exp) + + result = idx1 == Period('NaT', freq=freq) + exp = np.array([False, False, False, False]) + self.assert_numpy_array_equal(result, exp) + + result = idx1 != Period('NaT', freq=freq) + exp = np.array([True, True, True, True]) + self.assert_numpy_array_equal(result, exp) + + idx2 = PeriodIndex(['2011-02', '2011-01', '2011-04', 'NaT'], freq=freq) + result = idx1 < idx2 + exp = np.array([True, False, False, False]) + self.assert_numpy_array_equal(result, exp) + + result = idx1 == idx2 + exp = np.array([False, False, False, False]) + self.assert_numpy_array_equal(result, exp) + + result = idx1 != idx2 + exp = np.array([True, True, True, True]) + self.assert_numpy_array_equal(result, exp) + + result = idx1 == idx1 + exp = np.array([True, True, False, True]) + self.assert_numpy_array_equal(result, exp) + + result = idx1 != idx1 + exp = np.array([False, False, True, False]) + self.assert_numpy_array_equal(result, exp) + + diff = PeriodIndex(['2011-02', '2011-01', '2011-04', 'NaT'], freq='4M') + msg = "Input has different freq=4M from PeriodIndex" + with tm.assertRaisesRegexp(ValueError, msg): + idx1 > diff + with tm.assertRaisesRegexp(ValueError, msg): + idx1 == diff if __name__ == '__main__': diff --git a/pandas/tseries/tests/test_plotting.py b/pandas/tseries/tests/test_plotting.py index 08a4056c1fce2..d9b31c0a1d620 100644 --- a/pandas/tseries/tests/test_plotting.py +++ b/pandas/tseries/tests/test_plotting.py @@ -9,7 +9,7 @@ from pandas import Index, Series, DataFrame from pandas.tseries.index import date_range, bdate_range -from pandas.tseries.offsets import DateOffset +from pandas.tseries.offsets import DateOffset, Week from pandas.tseries.period import period_range, Period, PeriodIndex from pandas.tseries.resample import DatetimeIndex @@ -758,7 +758,7 @@ def test_to_weekly_resampling(self): high.plot() ax = low.plot() for l in ax.get_lines(): - self.assertTrue(PeriodIndex(data=l.get_xdata()).freq.startswith('W')) + self.assertEqual(PeriodIndex(data=l.get_xdata()).freq, idxh.freq) # tsplot from pandas.tseries.plotting import tsplot @@ -767,7 +767,7 @@ def test_to_weekly_resampling(self): tsplot(high, plt.Axes.plot) lines = tsplot(low, plt.Axes.plot) for l in lines: - self.assertTrue(PeriodIndex(data=l.get_xdata()).freq.startswith('W')) + self.assertTrue(PeriodIndex(data=l.get_xdata()).freq, idxh.freq) @slow def test_from_weekly_resampling(self): @@ -782,7 +782,7 @@ def test_from_weekly_resampling(self): expected_l = np.array([1514, 1519, 1523, 1527, 1531, 1536, 1540, 1544, 1549, 1553, 1558, 1562]) for l in ax.get_lines(): - self.assertTrue(PeriodIndex(data=l.get_xdata()).freq.startswith('W')) + self.assertTrue(PeriodIndex(data=l.get_xdata()).freq, idxh.freq) xdata = l.get_xdata(orig=False) if len(xdata) == 12: # idxl lines self.assert_numpy_array_equal(xdata, expected_l) @@ -796,9 +796,8 @@ def test_from_weekly_resampling(self): tsplot(low, plt.Axes.plot) lines = tsplot(high, plt.Axes.plot) - for l in lines: - self.assertTrue(PeriodIndex(data=l.get_xdata()).freq.startswith('W')) + self.assertTrue(PeriodIndex(data=l.get_xdata()).freq, idxh.freq) xdata = l.get_xdata(orig=False) if len(xdata) == 12: # idxl lines self.assert_numpy_array_equal(xdata, expected_l) @@ -825,7 +824,7 @@ def test_from_resampling_area_line_mixed(self): expected_y = np.zeros(len(expected_x)) for i in range(3): l = ax.lines[i] - self.assertTrue(PeriodIndex(data=l.get_xdata()).freq.startswith('W')) + self.assertEqual(PeriodIndex(l.get_xdata()).freq, idxh.freq) self.assert_numpy_array_equal(l.get_xdata(orig=False), expected_x) # check stacked values are correct expected_y += low[i].values @@ -836,7 +835,7 @@ def test_from_resampling_area_line_mixed(self): expected_y = np.zeros(len(expected_x)) for i in range(3): l = ax.lines[3 + i] - self.assertTrue(PeriodIndex(data=l.get_xdata()).freq.startswith('W')) + self.assertEqual(PeriodIndex(data=l.get_xdata()).freq, idxh.freq) self.assert_numpy_array_equal(l.get_xdata(orig=False), expected_x) expected_y += high[i].values self.assert_numpy_array_equal(l.get_ydata(orig=False), expected_y) @@ -851,7 +850,7 @@ def test_from_resampling_area_line_mixed(self): expected_y = np.zeros(len(expected_x)) for i in range(3): l = ax.lines[i] - self.assertTrue(PeriodIndex(data=l.get_xdata()).freq.startswith('W')) + self.assertEqual(PeriodIndex(data=l.get_xdata()).freq, idxh.freq) self.assert_numpy_array_equal(l.get_xdata(orig=False), expected_x) expected_y += high[i].values self.assert_numpy_array_equal(l.get_ydata(orig=False), expected_y) @@ -862,7 +861,7 @@ def test_from_resampling_area_line_mixed(self): expected_y = np.zeros(len(expected_x)) for i in range(3): l = ax.lines[3 + i] - self.assertTrue(PeriodIndex(data=l.get_xdata()).freq.startswith('W')) + self.assertEqual(PeriodIndex(data=l.get_xdata()).freq, idxh.freq) self.assert_numpy_array_equal(l.get_xdata(orig=False), expected_x) expected_y += low[i].values self.assert_numpy_array_equal(l.get_ydata(orig=False), expected_y) diff --git a/pandas/tseries/tools.py b/pandas/tseries/tools.py index efd1ff9ba34fd..521679f21dc93 100644 --- a/pandas/tseries/tools.py +++ b/pandas/tseries/tools.py @@ -449,6 +449,10 @@ def parse_time_string(arg, freq=None, dayfirst=None, yearfirst=None): if not isinstance(arg, compat.string_types): return arg + from pandas.tseries.offsets import DateOffset + if isinstance(freq, DateOffset): + freq = freq.rule_code + if dayfirst is None: dayfirst = get_option("display.date_dayfirst") if yearfirst is None: diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index e37dbbbf63e21..a8b573ab6788e 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -1803,6 +1803,19 @@ _MONTH_ALIASES = dict((k + 1, v) for k, v in enumerate(_MONTHS)) cpdef object _get_rule_month(object source, object default='DEC'): + """ + Return starting month of given freq, default is December. + + Example + ------- + >>> _get_rule_month('D') + 'DEC' + + >>> _get_rule_month('A-JAN') + 'JAN' + """ + if hasattr(source, 'freqstr'): + source = source.freqstr source = source.upper() if '-' not in source: return default