123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966 |
- # pylint: disable=E1101,E1103,W0232
- from datetime import datetime, timedelta
- import warnings
- import numpy as np
- from pandas._libs import index as libindex
- from pandas._libs.tslibs import (
- NaT, frequencies as libfrequencies, iNaT, resolution)
- from pandas._libs.tslibs.period import (
- DIFFERENT_FREQ, IncompatibleFrequency, Period)
- from pandas.util._decorators import Appender, Substitution, cache_readonly
- from pandas.core.dtypes.common import (
- is_bool_dtype, is_datetime64_any_dtype, is_float, is_float_dtype,
- is_integer, is_integer_dtype, pandas_dtype)
- from pandas import compat
- from pandas.core import common as com
- from pandas.core.accessor import delegate_names
- from pandas.core.algorithms import unique1d
- from pandas.core.arrays.period import (
- PeriodArray, period_array, validate_dtype_freq)
- from pandas.core.base import _shared_docs
- import pandas.core.indexes.base as ibase
- from pandas.core.indexes.base import _index_shared_docs, ensure_index
- from pandas.core.indexes.datetimelike import (
- DatetimeIndexOpsMixin, DatetimelikeDelegateMixin)
- from pandas.core.indexes.datetimes import DatetimeIndex, Index, Int64Index
- from pandas.core.missing import isna
- from pandas.core.ops import get_op_result_name
- from pandas.core.tools.datetimes import DateParseError, parse_time_string
- from pandas.tseries import frequencies
- from pandas.tseries.offsets import DateOffset, Tick
- _index_doc_kwargs = dict(ibase._index_doc_kwargs)
- _index_doc_kwargs.update(
- dict(target_klass='PeriodIndex or list of Periods'))
- # --- Period index sketch
- def _new_PeriodIndex(cls, **d):
- # GH13277 for unpickling
- values = d.pop('data')
- if values.dtype == 'int64':
- freq = d.pop('freq', None)
- values = PeriodArray(values, freq=freq)
- return cls._simple_new(values, **d)
- else:
- return cls(values, **d)
- class PeriodDelegateMixin(DatetimelikeDelegateMixin):
- """
- Delegate from PeriodIndex to PeriodArray.
- """
- _delegate_class = PeriodArray
- _delegated_properties = PeriodArray._datetimelike_ops
- _delegated_methods = (
- set(PeriodArray._datetimelike_methods) | {'_addsub_int_array'}
- )
- _raw_properties = {'is_leap_year'}
- @delegate_names(PeriodArray,
- PeriodDelegateMixin._delegated_properties,
- typ='property')
- @delegate_names(PeriodArray,
- PeriodDelegateMixin._delegated_methods,
- typ="method",
- overwrite=True)
- class PeriodIndex(DatetimeIndexOpsMixin, Int64Index, PeriodDelegateMixin):
- """
- Immutable ndarray holding ordinal values indicating regular periods in
- time such as particular years, quarters, months, etc.
- Index keys are boxed to Period objects which carries the metadata (eg,
- frequency information).
- Parameters
- ----------
- data : array-like (1-dimensional), optional
- Optional period-like data to construct index with
- copy : bool
- Make a copy of input ndarray
- freq : string or period object, optional
- One of pandas period strings or corresponding objects
- start : starting value, period-like, optional
- If data is None, used as the start point in generating regular
- period data.
- .. deprecated:: 0.24.0
- periods : int, optional, > 0
- Number of periods to generate, if generating index. Takes precedence
- over end argument
- .. deprecated:: 0.24.0
- end : end value, period-like, optional
- If periods is none, generated index will extend to first conforming
- period on or just past end argument
- .. deprecated:: 0.24.0
- year : int, array, or Series, default None
- month : int, array, or Series, default None
- quarter : int, array, or Series, default None
- day : int, array, or Series, default None
- hour : int, array, or Series, default None
- minute : int, array, or Series, default None
- second : int, array, or Series, default None
- tz : object, default None
- Timezone for converting datetime64 data to Periods
- dtype : str or PeriodDtype, default None
- Attributes
- ----------
- day
- dayofweek
- dayofyear
- days_in_month
- daysinmonth
- end_time
- freq
- freqstr
- hour
- is_leap_year
- minute
- month
- quarter
- qyear
- second
- start_time
- week
- weekday
- weekofyear
- year
- Methods
- -------
- asfreq
- strftime
- to_timestamp
- Notes
- -----
- Creating a PeriodIndex based on `start`, `periods`, and `end` has
- been deprecated in favor of :func:`period_range`.
- Examples
- --------
- >>> idx = pd.PeriodIndex(year=year_arr, quarter=q_arr)
- See Also
- ---------
- Index : The base pandas Index type.
- Period : Represents a period of time.
- DatetimeIndex : Index with datetime64 data.
- TimedeltaIndex : Index of timedelta64 data.
- period_range : Create a fixed-frequency PeriodIndex.
- """
- _typ = 'periodindex'
- _attributes = ['name', 'freq']
- # define my properties & methods for delegation
- _is_numeric_dtype = False
- _infer_as_myclass = True
- _data = None # type: PeriodArray
- _engine_type = libindex.PeriodEngine
- # ------------------------------------------------------------------------
- # Index Constructors
- def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
- periods=None, tz=None, dtype=None, copy=False, name=None,
- **fields):
- valid_field_set = {'year', 'month', 'day', 'quarter',
- 'hour', 'minute', 'second'}
- if not set(fields).issubset(valid_field_set):
- raise TypeError('__new__() got an unexpected keyword argument {}'.
- format(list(set(fields) - valid_field_set)[0]))
- if name is None and hasattr(data, 'name'):
- name = data.name
- if data is None and ordinal is None:
- # range-based.
- data, freq2 = PeriodArray._generate_range(start, end, periods,
- freq, fields)
- # PeriodArray._generate range does validate that fields is
- # empty when really using the range-based constructor.
- if not fields:
- msg = ("Creating a PeriodIndex by passing range "
- "endpoints is deprecated. Use "
- "`pandas.period_range` instead.")
- # period_range differs from PeriodIndex for cases like
- # start="2000", periods=4
- # PeriodIndex interprets that as A-DEC freq.
- # period_range interprets it as 'D' freq.
- cond = (
- freq is None and (
- (start and not isinstance(start, Period)) or
- (end and not isinstance(end, Period))
- )
- )
- if cond:
- msg += (
- " Note that the default `freq` may differ. Pass "
- "'freq=\"{}\"' to ensure the same output."
- ).format(freq2.freqstr)
- warnings.warn(msg, FutureWarning, stacklevel=2)
- freq = freq2
- data = PeriodArray(data, freq=freq)
- else:
- freq = validate_dtype_freq(dtype, freq)
- # PeriodIndex allow PeriodIndex(period_index, freq=different)
- # Let's not encourage that kind of behavior in PeriodArray.
- if freq and isinstance(data, cls) and data.freq != freq:
- # TODO: We can do some of these with no-copy / coercion?
- # e.g. D -> 2D seems to be OK
- data = data.asfreq(freq)
- if data is None and ordinal is not None:
- # we strangely ignore `ordinal` if data is passed.
- ordinal = np.asarray(ordinal, dtype=np.int64)
- data = PeriodArray(ordinal, freq)
- else:
- # don't pass copy here, since we copy later.
- data = period_array(data=data, freq=freq)
- if copy:
- data = data.copy()
- return cls._simple_new(data, name=name)
- @classmethod
- def _simple_new(cls, values, name=None, freq=None, **kwargs):
- """
- Create a new PeriodIndex.
- Parameters
- ----------
- values : PeriodArray, PeriodIndex, Index[int64], ndarray[int64]
- Values that can be converted to a PeriodArray without inference
- or coercion.
- """
- # TODO: raising on floats is tested, but maybe not useful.
- # Should the callers know not to pass floats?
- # At the very least, I think we can ensure that lists aren't passed.
- if isinstance(values, list):
- values = np.asarray(values)
- if is_float_dtype(values):
- raise TypeError("PeriodIndex._simple_new does not accept floats.")
- if freq:
- freq = Period._maybe_convert_freq(freq)
- values = PeriodArray(values, freq=freq)
- if not isinstance(values, PeriodArray):
- raise TypeError("PeriodIndex._simple_new only accepts PeriodArray")
- result = object.__new__(cls)
- result._data = values
- # For groupby perf. See note in indexes/base about _index_data
- result._index_data = values._data
- result.name = name
- result._reset_identity()
- return result
- # ------------------------------------------------------------------------
- # Data
- @property
- def values(self):
- return np.asarray(self)
- @property
- def freq(self):
- return self._data.freq
- @freq.setter
- def freq(self, value):
- value = Period._maybe_convert_freq(value)
- # TODO: When this deprecation is enforced, PeriodIndex.freq can
- # be removed entirely, and we'll just inherit.
- msg = ('Setting {cls}.freq has been deprecated and will be '
- 'removed in a future version; use {cls}.asfreq instead. '
- 'The {cls}.freq setter is not guaranteed to work.')
- warnings.warn(msg.format(cls=type(self).__name__),
- FutureWarning, stacklevel=2)
- # PeriodArray._freq isn't actually mutable. We set the private _freq
- # here, but people shouldn't be doing this anyway.
- self._data._freq = value
- def _shallow_copy(self, values=None, **kwargs):
- # TODO: simplify, figure out type of values
- if values is None:
- values = self._data
- if isinstance(values, type(self)):
- values = values._values
- if not isinstance(values, PeriodArray):
- if (isinstance(values, np.ndarray) and
- is_integer_dtype(values.dtype)):
- values = PeriodArray(values, freq=self.freq)
- else:
- # in particular, I would like to avoid period_array here.
- # Some people seem to be calling use with unexpected types
- # Index.difference -> ndarray[Period]
- # DatetimelikeIndexOpsMixin.repeat -> ndarray[ordinal]
- # I think that once all of Datetime* are EAs, we can simplify
- # this quite a bit.
- values = period_array(values, freq=self.freq)
- # We don't allow changing `freq` in _shallow_copy.
- validate_dtype_freq(self.dtype, kwargs.get('freq'))
- attributes = self._get_attributes_dict()
- attributes.update(kwargs)
- if not len(values) and 'dtype' not in kwargs:
- attributes['dtype'] = self.dtype
- return self._simple_new(values, **attributes)
- def _shallow_copy_with_infer(self, values=None, **kwargs):
- """ we always want to return a PeriodIndex """
- return self._shallow_copy(values=values, **kwargs)
- @property
- def _box_func(self):
- """Maybe box an ordinal or Period"""
- # TODO(DatetimeArray): Avoid double-boxing
- # PeriodArray takes care of boxing already, so we need to check
- # whether we're given an ordinal or a Period. It seems like some
- # places outside of indexes/period.py are calling this _box_func,
- # but passing data that's already boxed.
- def func(x):
- if isinstance(x, Period) or x is NaT:
- return x
- else:
- return Period._from_ordinal(ordinal=x, freq=self.freq)
- return func
- def _maybe_convert_timedelta(self, other):
- """
- Convert timedelta-like input to an integer multiple of self.freq
- Parameters
- ----------
- other : timedelta, np.timedelta64, DateOffset, int, np.ndarray
- Returns
- -------
- converted : int, np.ndarray[int64]
- Raises
- ------
- IncompatibleFrequency : if the input cannot be written as a multiple
- of self.freq. Note IncompatibleFrequency subclasses ValueError.
- """
- if isinstance(
- other, (timedelta, np.timedelta64, Tick, np.ndarray)):
- offset = frequencies.to_offset(self.freq.rule_code)
- if isinstance(offset, Tick):
- # _check_timedeltalike_freq_compat will raise if incompatible
- delta = self._data._check_timedeltalike_freq_compat(other)
- return delta
- elif isinstance(other, DateOffset):
- freqstr = other.rule_code
- base = libfrequencies.get_base_alias(freqstr)
- if base == self.freq.rule_code:
- return other.n
- msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
- own_freq=self.freqstr,
- other_freq=other.freqstr)
- raise IncompatibleFrequency(msg)
- elif is_integer(other):
- # integer is passed to .shift via
- # _add_datetimelike_methods basically
- # but ufunc may pass integer to _add_delta
- return other
- # raise when input doesn't have freq
- msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
- own_freq=self.freqstr,
- other_freq=None)
- raise IncompatibleFrequency(msg)
- # ------------------------------------------------------------------------
- # Rendering Methods
- def _format_native_types(self, na_rep=u'NaT', quoting=None, **kwargs):
- # just dispatch, return ndarray
- return self._data._format_native_types(na_rep=na_rep,
- quoting=quoting,
- **kwargs)
- def _mpl_repr(self):
- # how to represent ourselves to matplotlib
- return self.astype(object).values
- @property
- def _formatter_func(self):
- return self.array._formatter(boxed=False)
- # ------------------------------------------------------------------------
- # Indexing
- @cache_readonly
- def _engine(self):
- return self._engine_type(lambda: self, len(self))
- @Appender(_index_shared_docs['contains'])
- def __contains__(self, key):
- if isinstance(key, Period):
- if key.freq != self.freq:
- return False
- else:
- return key.ordinal in self._engine
- else:
- try:
- self.get_loc(key)
- return True
- except Exception:
- return False
- contains = __contains__
- @cache_readonly
- def _int64index(self):
- return Int64Index._simple_new(self.asi8, name=self.name)
- # ------------------------------------------------------------------------
- # Index Methods
- def _coerce_scalar_to_index(self, item):
- """
- we need to coerce a scalar to a compat for our index type
- Parameters
- ----------
- item : scalar item to coerce
- """
- return PeriodIndex([item], **self._get_attributes_dict())
- def __array__(self, dtype=None):
- if is_integer_dtype(dtype):
- return self.asi8
- else:
- return self.astype(object).values
- def __array_wrap__(self, result, context=None):
- """
- Gets called after a ufunc. Needs additional handling as
- PeriodIndex stores internal data as int dtype
- Replace this to __numpy_ufunc__ in future version
- """
- if isinstance(context, tuple) and len(context) > 0:
- func = context[0]
- if func is np.add:
- pass
- elif func is np.subtract:
- name = self.name
- left = context[1][0]
- right = context[1][1]
- if (isinstance(left, PeriodIndex) and
- isinstance(right, PeriodIndex)):
- name = left.name if left.name == right.name else None
- return Index(result, name=name)
- elif isinstance(left, Period) or isinstance(right, Period):
- return Index(result, name=name)
- elif isinstance(func, np.ufunc):
- if 'M->M' not in func.types:
- msg = "ufunc '{0}' not supported for the PeriodIndex"
- # This should be TypeError, but TypeError cannot be raised
- # from here because numpy catches.
- raise ValueError(msg.format(func.__name__))
- if is_bool_dtype(result):
- return result
- # the result is object dtype array of Period
- # cannot pass _simple_new as it is
- return type(self)(result, freq=self.freq, name=self.name)
- def asof_locs(self, where, mask):
- """
- where : array of timestamps
- mask : array of booleans where data is not NA
- """
- where_idx = where
- if isinstance(where_idx, DatetimeIndex):
- where_idx = PeriodIndex(where_idx.values, freq=self.freq)
- locs = self._ndarray_values[mask].searchsorted(
- where_idx._ndarray_values, side='right')
- locs = np.where(locs > 0, locs - 1, 0)
- result = np.arange(len(self))[mask].take(locs)
- first = mask.argmax()
- result[(locs == 0) & (where_idx._ndarray_values <
- self._ndarray_values[first])] = -1
- return result
- @Appender(_index_shared_docs['astype'])
- def astype(self, dtype, copy=True, how='start'):
- dtype = pandas_dtype(dtype)
- if is_datetime64_any_dtype(dtype):
- # 'how' is index-specific, isn't part of the EA interface.
- tz = getattr(dtype, 'tz', None)
- return self.to_timestamp(how=how).tz_localize(tz)
- # TODO: should probably raise on `how` here, so we don't ignore it.
- return super(PeriodIndex, self).astype(dtype, copy=copy)
- @Substitution(klass='PeriodIndex')
- @Appender(_shared_docs['searchsorted'])
- def searchsorted(self, value, side='left', sorter=None):
- if isinstance(value, Period):
- if value.freq != self.freq:
- msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
- own_freq=self.freqstr,
- other_freq=value.freqstr)
- raise IncompatibleFrequency(msg)
- value = value.ordinal
- elif isinstance(value, compat.string_types):
- try:
- value = Period(value, freq=self.freq).ordinal
- except DateParseError:
- raise KeyError("Cannot interpret '{}' as period".format(value))
- return self._ndarray_values.searchsorted(value, side=side,
- sorter=sorter)
- @property
- def is_all_dates(self):
- return True
- @property
- def is_full(self):
- """
- Returns True if this PeriodIndex is range-like in that all Periods
- between start and end are present, in order.
- """
- if len(self) == 0:
- return True
- if not self.is_monotonic:
- raise ValueError('Index is not monotonic')
- values = self.asi8
- return ((values[1:] - values[:-1]) < 2).all()
- @property
- def inferred_type(self):
- # b/c data is represented as ints make sure we can't have ambiguous
- # indexing
- return 'period'
- def get_value(self, series, key):
- """
- Fast lookup of value from 1-dimensional ndarray. Only use this if you
- know what you're doing
- """
- s = com.values_from_object(series)
- try:
- return com.maybe_box(self,
- super(PeriodIndex, self).get_value(s, key),
- series, key)
- except (KeyError, IndexError):
- try:
- asdt, parsed, reso = parse_time_string(key, self.freq)
- grp = resolution.Resolution.get_freq_group(reso)
- freqn = resolution.get_freq_group(self.freq)
- vals = self._ndarray_values
- # if our data is higher resolution than requested key, slice
- if grp < freqn:
- iv = Period(asdt, freq=(grp, 1))
- ord1 = iv.asfreq(self.freq, how='S').ordinal
- ord2 = iv.asfreq(self.freq, how='E').ordinal
- if ord2 < vals[0] or ord1 > vals[-1]:
- raise KeyError(key)
- pos = np.searchsorted(self._ndarray_values, [ord1, ord2])
- key = slice(pos[0], pos[1] + 1)
- return series[key]
- elif grp == freqn:
- key = Period(asdt, freq=self.freq).ordinal
- return com.maybe_box(self, self._engine.get_value(s, key),
- series, key)
- else:
- raise KeyError(key)
- except TypeError:
- pass
- period = Period(key, self.freq)
- key = period.value if isna(period) else period.ordinal
- return com.maybe_box(self, self._engine.get_value(s, key),
- series, key)
- @Appender(_index_shared_docs['get_indexer'] % _index_doc_kwargs)
- def get_indexer(self, target, method=None, limit=None, tolerance=None):
- target = ensure_index(target)
- if hasattr(target, 'freq') and target.freq != self.freq:
- msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
- own_freq=self.freqstr,
- other_freq=target.freqstr)
- raise IncompatibleFrequency(msg)
- if isinstance(target, PeriodIndex):
- target = target.asi8
- if tolerance is not None:
- tolerance = self._convert_tolerance(tolerance, target)
- return Index.get_indexer(self._int64index, target, method,
- limit, tolerance)
- def _get_unique_index(self, dropna=False):
- """
- wrap Index._get_unique_index to handle NaT
- """
- res = super(PeriodIndex, self)._get_unique_index(dropna=dropna)
- if dropna:
- res = res.dropna()
- return res
- @Appender(Index.unique.__doc__)
- def unique(self, level=None):
- # override the Index.unique method for performance GH#23083
- if level is not None:
- # this should never occur, but is retained to make the signature
- # match Index.unique
- self._validate_index_level(level)
- values = self._ndarray_values
- result = unique1d(values)
- return self._shallow_copy(result)
- def get_loc(self, key, method=None, tolerance=None):
- """
- Get integer location for requested label
- Returns
- -------
- loc : int
- """
- try:
- return self._engine.get_loc(key)
- except KeyError:
- if is_integer(key):
- raise
- try:
- asdt, parsed, reso = parse_time_string(key, self.freq)
- key = asdt
- except TypeError:
- pass
- except DateParseError:
- # A string with invalid format
- raise KeyError("Cannot interpret '{}' as period".format(key))
- try:
- key = Period(key, freq=self.freq)
- except ValueError:
- # we cannot construct the Period
- # as we have an invalid type
- raise KeyError(key)
- try:
- ordinal = iNaT if key is NaT else key.ordinal
- if tolerance is not None:
- tolerance = self._convert_tolerance(tolerance,
- np.asarray(key))
- return self._int64index.get_loc(ordinal, method, tolerance)
- except KeyError:
- raise KeyError(key)
- def _maybe_cast_slice_bound(self, label, side, kind):
- """
- If label is a string or a datetime, cast it to Period.ordinal according
- to resolution.
- Parameters
- ----------
- label : object
- side : {'left', 'right'}
- kind : {'ix', 'loc', 'getitem'}
- Returns
- -------
- bound : Period or object
- Notes
- -----
- Value of `side` parameter should be validated in caller.
- """
- assert kind in ['ix', 'loc', 'getitem']
- if isinstance(label, datetime):
- return Period(label, freq=self.freq)
- elif isinstance(label, compat.string_types):
- try:
- _, parsed, reso = parse_time_string(label, self.freq)
- bounds = self._parsed_string_to_bounds(reso, parsed)
- return bounds[0 if side == 'left' else 1]
- except Exception:
- raise KeyError(label)
- elif is_integer(label) or is_float(label):
- self._invalid_indexer('slice', label)
- return label
- def _parsed_string_to_bounds(self, reso, parsed):
- if reso == 'year':
- t1 = Period(year=parsed.year, freq='A')
- elif reso == 'month':
- t1 = Period(year=parsed.year, month=parsed.month, freq='M')
- elif reso == 'quarter':
- q = (parsed.month - 1) // 3 + 1
- t1 = Period(year=parsed.year, quarter=q, freq='Q-DEC')
- elif reso == 'day':
- t1 = Period(year=parsed.year, month=parsed.month, day=parsed.day,
- freq='D')
- elif reso == 'hour':
- t1 = Period(year=parsed.year, month=parsed.month, day=parsed.day,
- hour=parsed.hour, freq='H')
- elif reso == 'minute':
- t1 = Period(year=parsed.year, month=parsed.month, day=parsed.day,
- hour=parsed.hour, minute=parsed.minute, freq='T')
- elif reso == 'second':
- t1 = Period(year=parsed.year, month=parsed.month, day=parsed.day,
- hour=parsed.hour, minute=parsed.minute,
- second=parsed.second, freq='S')
- else:
- raise KeyError(reso)
- return (t1.asfreq(self.freq, how='start'),
- t1.asfreq(self.freq, how='end'))
- def _get_string_slice(self, key):
- if not self.is_monotonic:
- raise ValueError('Partial indexing only valid for '
- 'ordered time series')
- key, parsed, reso = parse_time_string(key, self.freq)
- grp = resolution.Resolution.get_freq_group(reso)
- freqn = resolution.get_freq_group(self.freq)
- if reso in ['day', 'hour', 'minute', 'second'] and not grp < freqn:
- raise KeyError(key)
- t1, t2 = self._parsed_string_to_bounds(reso, parsed)
- return slice(self.searchsorted(t1.ordinal, side='left'),
- self.searchsorted(t2.ordinal, side='right'))
- def _convert_tolerance(self, tolerance, target):
- tolerance = DatetimeIndexOpsMixin._convert_tolerance(self, tolerance,
- target)
- if target.size != tolerance.size and tolerance.size > 1:
- raise ValueError('list-like tolerance size must match '
- 'target index size')
- return self._maybe_convert_timedelta(tolerance)
- def insert(self, loc, item):
- if not isinstance(item, Period) or self.freq != item.freq:
- return self.astype(object).insert(loc, item)
- idx = np.concatenate((self[:loc].asi8, np.array([item.ordinal]),
- self[loc:].asi8))
- return self._shallow_copy(idx)
- def join(self, other, how='left', level=None, return_indexers=False,
- sort=False):
- """
- See Index.join
- """
- self._assert_can_do_setop(other)
- result = Int64Index.join(self, other, how=how, level=level,
- return_indexers=return_indexers,
- sort=sort)
- if return_indexers:
- result, lidx, ridx = result
- return self._apply_meta(result), lidx, ridx
- return self._apply_meta(result)
- def _assert_can_do_setop(self, other):
- super(PeriodIndex, self)._assert_can_do_setop(other)
- if not isinstance(other, PeriodIndex):
- raise ValueError('can only call with other PeriodIndex-ed objects')
- if self.freq != other.freq:
- msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
- own_freq=self.freqstr,
- other_freq=other.freqstr)
- raise IncompatibleFrequency(msg)
- def _wrap_setop_result(self, other, result):
- name = get_op_result_name(self, other)
- result = self._apply_meta(result)
- result.name = name
- return result
- def _apply_meta(self, rawarr):
- if not isinstance(rawarr, PeriodIndex):
- rawarr = PeriodIndex._simple_new(rawarr, freq=self.freq,
- name=self.name)
- return rawarr
- def __setstate__(self, state):
- """Necessary for making this object picklable"""
- if isinstance(state, dict):
- super(PeriodIndex, self).__setstate__(state)
- elif isinstance(state, tuple):
- # < 0.15 compat
- if len(state) == 2:
- nd_state, own_state = state
- data = np.empty(nd_state[1], dtype=nd_state[2])
- np.ndarray.__setstate__(data, nd_state)
- # backcompat
- freq = Period._maybe_convert_freq(own_state[1])
- else: # pragma: no cover
- data = np.empty(state)
- np.ndarray.__setstate__(self, state)
- freq = None # ?
- data = PeriodArray(data, freq=freq)
- self._data = data
- else:
- raise Exception("invalid pickle state")
- _unpickle_compat = __setstate__
- @property
- def flags(self):
- """ return the ndarray.flags for the underlying data """
- warnings.warn("{obj}.flags is deprecated and will be removed "
- "in a future version".format(obj=type(self).__name__),
- FutureWarning, stacklevel=2)
- return self._ndarray_values.flags
- def item(self):
- """
- return the first element of the underlying data as a python
- scalar
- """
- # TODO(DatetimeArray): remove
- if len(self) == 1:
- return self[0]
- else:
- # copy numpy's message here because Py26 raises an IndexError
- raise ValueError('can only convert an array of size 1 to a '
- 'Python scalar')
- @property
- def data(self):
- """ return the data pointer of the underlying data """
- warnings.warn("{obj}.data is deprecated and will be removed "
- "in a future version".format(obj=type(self).__name__),
- FutureWarning, stacklevel=2)
- return np.asarray(self._data).data
- @property
- def base(self):
- """ return the base object if the memory of the underlying data is
- shared
- """
- warnings.warn("{obj}.base is deprecated and will be removed "
- "in a future version".format(obj=type(self).__name__),
- FutureWarning, stacklevel=2)
- return np.asarray(self._data)
- PeriodIndex._add_comparison_ops()
- PeriodIndex._add_numeric_methods_disabled()
- PeriodIndex._add_logical_methods_disabled()
- PeriodIndex._add_datetimelike_methods()
- def period_range(start=None, end=None, periods=None, freq=None, name=None):
- """
- Return a fixed frequency PeriodIndex, with day (calendar) as the default
- frequency
- Parameters
- ----------
- start : string or period-like, default None
- Left bound for generating periods
- end : string or period-like, default None
- Right bound for generating periods
- periods : integer, default None
- Number of periods to generate
- freq : string or DateOffset, optional
- Frequency alias. By default the freq is taken from `start` or `end`
- if those are Period objects. Otherwise, the default is ``"D"`` for
- daily frequency.
- name : string, default None
- Name of the resulting PeriodIndex
- Returns
- -------
- prng : PeriodIndex
- Notes
- -----
- Of the three parameters: ``start``, ``end``, and ``periods``, exactly two
- must be specified.
- To learn more about the frequency strings, please see `this link
- <http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- Examples
- --------
- >>> pd.period_range(start='2017-01-01', end='2018-01-01', freq='M')
- PeriodIndex(['2017-01', '2017-02', '2017-03', '2017-04', '2017-05',
- '2017-06', '2017-06', '2017-07', '2017-08', '2017-09',
- '2017-10', '2017-11', '2017-12', '2018-01'],
- dtype='period[M]', freq='M')
- If ``start`` or ``end`` are ``Period`` objects, they will be used as anchor
- endpoints for a ``PeriodIndex`` with frequency matching that of the
- ``period_range`` constructor.
- >>> pd.period_range(start=pd.Period('2017Q1', freq='Q'),
- ... end=pd.Period('2017Q2', freq='Q'), freq='M')
- PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'],
- dtype='period[M]', freq='M')
- """
- if com.count_not_none(start, end, periods) != 2:
- raise ValueError('Of the three parameters: start, end, and periods, '
- 'exactly two must be specified')
- if freq is None and (not isinstance(start, Period)
- and not isinstance(end, Period)):
- freq = 'D'
- data, freq = PeriodArray._generate_range(start, end, periods, freq,
- fields={})
- data = PeriodArray(data, freq=freq)
- return PeriodIndex(data, name=name)
|