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 dt64arr_to_periodarr(data, freq, tz=None): if data.dtype != np.dtype('M8[ns]'): raise ValueError('Wrong dtype: %s' % data.dtype) freq = Period._maybe_convert_freq(freq) base, mult = frequencies.get_freq_code(freq) return libperiod.dt64arr_to_periodarr(data.view('i8'), base, tz)
def asfreq(self, freq=None, how='E'): """ Convert the PeriodIndex to the specified frequency `freq`. Parameters ---------- freq : str a frequency how : str {'E', 'S'} 'E', 'END', or 'FINISH' for end, 'S', 'START', or 'BEGIN' for start. Whether the elements should be aligned to the end or start within pa period. January 31st ('END') vs. Janury 1st ('START') for example. Returns ------- new : PeriodIndex with the new frequency Examples -------- >>> pidx = pd.period_range('2010-01-01', '2015-01-01', freq='A') >>> pidx <class 'pandas.core.indexes.period.PeriodIndex'> [2010, ..., 2015] Length: 6, Freq: A-DEC >>> pidx.asfreq('M') <class 'pandas.core.indexes.period.PeriodIndex'> [2010-12, ..., 2015-12] Length: 6, Freq: M >>> pidx.asfreq('M', how='S') <class 'pandas.core.indexes.period.PeriodIndex'> [2010-01, ..., 2015-01] Length: 6, Freq: M """ how = _validate_end_alias(how) freq = Period._maybe_convert_freq(freq) base1, mult1 = _gfc(self.freq) base2, mult2 = _gfc(freq) asi8 = self.asi8 # mult1 can't be negative or 0 end = how == 'E' if end: ordinal = asi8 + mult1 - 1 else: ordinal = asi8 new_data = period.period_asfreq_arr(ordinal, base1, base2, end) if self.hasnans: new_data[self._isnan] = tslib.iNaT return self._simple_new(new_data, self.name, freq=freq)
def to_timestamp(self, freq=None, how='start'): """ Cast to DatetimeIndex Parameters ---------- freq : string or DateOffset, optional Target frequency. The default is 'D' for week or longer, 'S' otherwise how : {'s', 'e', 'start', 'end'} Returns ------- DatetimeIndex """ how = _validate_end_alias(how) if freq is None: base, mult = _gfc(self.freq) freq = frequencies.get_to_timestamp_base(base) else: freq = Period._maybe_convert_freq(freq) base, mult = _gfc(freq) new_data = self.asfreq(freq, how) new_data = period.periodarr_to_dt64arr(new_data._values, base) return DatetimeIndex(new_data, freq='infer', name=self.name)
def dt64arr_to_periodarr(data, freq, tz): if data.dtype != np.dtype('M8[ns]'): raise ValueError('Wrong dtype: %s' % data.dtype) freq = Period._maybe_convert_freq(freq) base, mult = _gfc(freq) return period.dt64arr_to_periodarr(data.view('i8'), base, tz)
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
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")
def __init__(self, values, freq=None, dtype=None, copy=False): freq = dtl.validate_dtype_freq(dtype, freq) if freq is not None: freq = Period._maybe_convert_freq(freq) if isinstance(values, ABCSeries): values = values._values if not isinstance(values, type(self)): raise TypeError("Incorrect dtype") elif isinstance(values, ABCPeriodIndex): values = values._values if isinstance(values, type(self)): if freq is not None and freq != values.freq: msg = DIFFERENT_FREQ_INDEX.format(values.freq.freqstr, freq.freqstr) raise IncompatibleFrequency(msg) values, freq = values._data, values.freq values = np.array(values, dtype='int64', copy=copy) self._data = values if freq is None: raise ValueError('freq is not specified and cannot be inferred') self._dtype = PeriodDtype(freq)
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)
def freq(self, value): value = Period._maybe_convert_freq(value) 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 asfreq(self, freq=None, how='E'): """ Convert the Period Array/Index to the specified frequency `freq`. Parameters ---------- freq : str a frequency how : str {'E', 'S'} 'E', 'END', or 'FINISH' for end, 'S', 'START', or 'BEGIN' for start. Whether the elements should be aligned to the end or start within pa period. January 31st ('END') vs. January 1st ('START') for example. Returns ------- new : Period Array/Index with the new frequency Examples -------- >>> pidx = pd.period_range('2010-01-01', '2015-01-01', freq='A') >>> pidx PeriodIndex(['2010', '2011', '2012', '2013', '2014', '2015'], dtype='period[A-DEC]', freq='A-DEC') >>> pidx.asfreq('M') PeriodIndex(['2010-12', '2011-12', '2012-12', '2013-12', '2014-12', '2015-12'], dtype='period[M]', freq='M') >>> pidx.asfreq('M', how='S') PeriodIndex(['2010-01', '2011-01', '2012-01', '2013-01', '2014-01', '2015-01'], dtype='period[M]', freq='M') """ how = libperiod._validate_end_alias(how) freq = Period._maybe_convert_freq(freq) base1, mult1 = libfrequencies.get_freq_code(self.freq) base2, mult2 = libfrequencies.get_freq_code(freq) asi8 = self.asi8 # mult1 can't be negative or 0 end = how == 'E' if end: ordinal = asi8 + mult1 - 1 else: ordinal = asi8 new_data = period_asfreq_arr(ordinal, base1, base2, end) if self._hasnans: new_data[self._isnan] = iNaT return type(self)(new_data, freq=freq)
def _from_ordinals(cls, values, freq=None): """ Values should be int ordinals `__new__` & `_simple_new` cooerce to ordinals and call this method """ values = np.array(values, dtype='int64', copy=False) result = object.__new__(cls) result._data = values if freq is None: raise ValueError('freq is not specified and cannot be inferred') result._freq = Period._maybe_convert_freq(freq) return result
def _from_ordinals(cls, values, freq=None, **kwargs): """ Values should be int ordinals `__new__` & `_simple_new` cooerce to ordinals and call this method """ # **kwargs are included so that the signature matches PeriodIndex, # letting us share _simple_new values = np.array(values, dtype='int64', copy=False) result = object.__new__(cls) result._data = values if freq is None: raise ValueError('freq is not specified and cannot be inferred') result._freq = Period._maybe_convert_freq(freq) return result
def _generate_range(cls, start, end, periods, freq, fields): if freq is not None: freq = Period._maybe_convert_freq(freq) field_count = len(fields) if com._count_not_none(start, end) > 0: if field_count > 0: raise ValueError('Can either instantiate from fields ' 'or endpoints, but not both') subarr, freq = _get_ordinal_range(start, end, periods, freq) elif field_count > 0: subarr, freq = _range_from_fields(freq=freq, **fields) else: raise ValueError('Not enough parameters to construct ' 'Period range') return subarr, freq
def to_timestamp(self, freq=None, how='start'): """ Cast to DatetimeArray/Index Parameters ---------- freq : string or DateOffset, optional Target frequency. The default is 'D' for week or longer, 'S' otherwise how : {'s', 'e', 'start', 'end'} Returns ------- DatetimeArray/Index """ from pandas.core.arrays import DatetimeArrayMixin how = libperiod._validate_end_alias(how) end = how == 'E' if end: if freq == 'B': # roll forward to ensure we land on B date adjust = Timedelta(1, 'D') - Timedelta(1, 'ns') return self.to_timestamp(how='start') + adjust else: adjust = Timedelta(1, 'ns') return (self + 1).to_timestamp(how='start') - adjust if freq is None: base, mult = frequencies.get_freq_code(self.freq) freq = frequencies.get_to_timestamp_base(base) else: freq = Period._maybe_convert_freq(freq) base, mult = frequencies.get_freq_code(freq) new_data = self.asfreq(freq, how=how) new_data = libperiod.periodarr_to_dt64arr(new_data._ndarray_values, base) return DatetimeArrayMixin(new_data, freq='infer')
def dt64arr_to_periodarr(data, freq, tz=None): """ Convert an datetime-like array to values Period ordinals. Parameters ---------- data : Union[Series[datetime64[ns]], DatetimeIndex, ndarray[datetime64ns]] freq : Optional[Union[str, Tick]] Must match the `freq` on the `data` if `data` is a DatetimeIndex or Series. tz : Optional[tzinfo] Returns ------- ordinals : ndarray[int] freq : Tick The frequencey extracted from the Series or DatetimeIndex if that's used. """ if data.dtype != np.dtype('M8[ns]'): raise ValueError('Wrong dtype: %s' % data.dtype) if freq is None: if isinstance(data, ABCIndexClass): data, freq = data._values, data.freq elif isinstance(data, ABCSeries): data, freq = data._values, data.dt.freq freq = Period._maybe_convert_freq(freq) if isinstance(data, (ABCIndexClass, ABCSeries)): data = data._values base, mult = frequencies.get_freq_code(freq) return libperiod.dt64arr_to_periodarr(data.view('i8'), base, tz), freq
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 periods is not None: if is_float(periods): periods = int(periods) elif not is_integer(periods): msg = 'periods must be a number, got {periods}' raise TypeError(msg.format(periods=periods)) if name is None and hasattr(data, 'name'): name = data.name if dtype is not None: dtype = pandas_dtype(dtype) if not is_period_dtype(dtype): raise ValueError('dtype must be PeriodDtype') if freq is None: freq = dtype.freq elif freq != dtype.freq: msg = 'specified freq and dtype are different' raise IncompatibleFrequency(msg) # coerce freq to freq object, otherwise it can be coerced elementwise # which is slow if freq: freq = Period._maybe_convert_freq(freq) if data is None: if ordinal is not None: data = np.asarray(ordinal, dtype=np.int64) else: data, freq = cls._generate_range(start, end, periods, freq, fields) return cls._from_ordinals(data, name=name, freq=freq) if isinstance(data, PeriodIndex): if freq is None or freq == data.freq: # no freq change freq = data.freq data = data._ndarray_values else: base1, _ = _gfc(data.freq) base2, _ = _gfc(freq) data = period.period_asfreq_arr(data._ndarray_values, base1, base2, 1) return cls._simple_new(data, name=name, freq=freq) # not array / index if not isinstance( data, (np.ndarray, PeriodIndex, DatetimeIndex, Int64Index)): if is_scalar(data) or isinstance(data, Period): cls._scalar_data_error(data) # other iterable of some kind if not isinstance(data, (list, tuple)): data = list(data) data = np.asarray(data) # datetime other than period if is_datetime64_dtype(data.dtype): data = dt64arr_to_periodarr(data, freq, tz) return cls._from_ordinals(data, name=name, freq=freq) # check not floats if infer_dtype(data) == 'floating' and len(data) > 0: raise TypeError("PeriodIndex does not allow " "floating point in construction") # anything else, likely an array of strings or periods data = ensure_object(data) freq = freq or period.extract_freq(data) data = period.extract_ordinals(data, freq) return cls._from_ordinals(data, name=name, freq=freq)
def _box_func(self, x) -> Period | NaTType: return Period._from_ordinal(ordinal=x, freq=self.freq)
def _scalar_from_string(self, value): # type: (str) -> Period return Period(value, freq=self.freq)
def func(x): if isinstance(x, Period) or x is NaT: return x else: return Period._from_ordinal(ordinal=x, freq=self.freq)
def _format_coord(freq, t, y): time_period = Period(ordinal=int(t), freq=freq) return f"t = {time_period} y = {y:8f}"
def _box_func(self): return lambda x: Period._from_ordinal(ordinal=x, freq=self.freq)
def _box_func(self, x) -> Union[Period, NaTType]: return Period._from_ordinal(ordinal=x, freq=self.freq)
def _format_coord(freq, t, y): return "t = {0} y = {1:8f}".format(Period(ordinal=int(t), freq=freq), y)
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 __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None, periods=None, copy=False, name=None, tz=None, dtype=None, **kwargs): if periods is not None: if is_float(periods): periods = int(periods) elif not is_integer(periods): msg = 'periods must be a number, got {periods}' raise TypeError(msg.format(periods=periods)) if name is None and hasattr(data, 'name'): name = data.name if dtype is not None: dtype = pandas_dtype(dtype) if not is_period_dtype(dtype): raise ValueError('dtype must be PeriodDtype') if freq is None: freq = dtype.freq elif freq != dtype.freq: msg = 'specified freq and dtype are different' raise IncompatibleFrequency(msg) # coerce freq to freq object, otherwise it can be coerced elementwise # which is slow if freq: freq = Period._maybe_convert_freq(freq) if data is None: if ordinal is not None: data = np.asarray(ordinal, dtype=np.int64) else: data, freq = cls._generate_range(start, end, periods, freq, kwargs) return cls._from_ordinals(data, name=name, freq=freq) if isinstance(data, PeriodIndex): if freq is None or freq == data.freq: # no freq change freq = data.freq data = data._values else: base1, _ = _gfc(data.freq) base2, _ = _gfc(freq) data = period.period_asfreq_arr(data._values, base1, base2, 1) return cls._simple_new(data, name=name, freq=freq) # not array / index if not isinstance(data, (np.ndarray, PeriodIndex, DatetimeIndex, Int64Index)): if is_scalar(data) or isinstance(data, Period): cls._scalar_data_error(data) # other iterable of some kind if not isinstance(data, (list, tuple)): data = list(data) data = np.asarray(data) # datetime other than period if is_datetime64_dtype(data.dtype): data = dt64arr_to_periodarr(data, freq, tz) return cls._from_ordinals(data, name=name, freq=freq) # check not floats if infer_dtype(data) == 'floating' and len(data) > 0: raise TypeError("PeriodIndex does not allow " "floating point in construction") # anything else, likely an array of strings or periods data = _ensure_object(data) freq = freq or period.extract_freq(data) data = period.extract_ordinals(data, freq) return cls._from_ordinals(data, name=name, freq=freq)
def pnow(freq=None): # deprecation, xref #13790 warnings.warn("pd.pnow() and pandas.core.indexes.period.pnow() " "are deprecated. Please use Period.now()", FutureWarning, stacklevel=2) return Period.now(freq=freq)
def _scalar_from_string(self, value: str) -> Period: return Period(value, freq=self.freq)
def asfreq(self, freq=None, how: str = "E") -> PeriodArray: """ Convert the {klass} to the specified frequency `freq`. Equivalent to applying :meth:`pandas.Period.asfreq` with the given arguments to each :class:`~pandas.Period` in this {klass}. Parameters ---------- freq : str A frequency. how : str {{'E', 'S'}}, default 'E' Whether the elements should be aligned to the end or start within pa period. * 'E', 'END', or 'FINISH' for end, * 'S', 'START', or 'BEGIN' for start. January 31st ('END') vs. January 1st ('START') for example. Returns ------- {klass} The transformed {klass} with the new frequency. See Also -------- {other}.asfreq: Convert each Period in a {other_name} to the given frequency. Period.asfreq : Convert a :class:`~pandas.Period` object to the given frequency. Examples -------- >>> pidx = pd.period_range('2010-01-01', '2015-01-01', freq='A') >>> pidx PeriodIndex(['2010', '2011', '2012', '2013', '2014', '2015'], dtype='period[A-DEC]', freq='A-DEC') >>> pidx.asfreq('M') PeriodIndex(['2010-12', '2011-12', '2012-12', '2013-12', '2014-12', '2015-12'], dtype='period[M]', freq='M') >>> pidx.asfreq('M', how='S') PeriodIndex(['2010-01', '2011-01', '2012-01', '2013-01', '2014-01', '2015-01'], dtype='period[M]', freq='M') """ how = libperiod.validate_end_alias(how) freq = Period._maybe_convert_freq(freq) base1 = self.freq._period_dtype_code base2 = freq._period_dtype_code asi8 = self.asi8 # self.freq.n can't be negative or 0 end = how == "E" if end: ordinal = asi8 + self.freq.n - 1 else: ordinal = asi8 new_data = period_asfreq_arr(ordinal, base1, base2, end) if self._hasnans: new_data[self._isnan] = iNaT return type(self)(new_data, freq=freq)
def get_loc(self, key, method=None, tolerance=None): """ Get integer location for requested label. Parameters ---------- key : Period, NaT, str, or datetime String or datetime key must be parseable as Period. Returns ------- loc : int or ndarray[int64] Raises ------ KeyError Key is not present in the index. TypeError If key is listlike or otherwise not hashable. """ if not is_scalar(key): raise InvalidIndexError(key) if isinstance(key, str): try: loc = self._get_string_slice(key) return loc except (TypeError, ValueError): pass try: asdt, reso = parse_time_string(key, self.freq) except DateParseError: # A string with invalid format raise KeyError(f"Cannot interpret '{key}' as period") grp = resolution.Resolution.get_freq_group(reso) freqn = resolution.get_freq_group(self.freq) # _get_string_slice will handle cases where grp < freqn assert grp >= freqn if grp == freqn: key = Period(asdt, freq=self.freq) loc = self.get_loc(key, method=method, tolerance=tolerance) return loc elif method is None: raise KeyError(key) else: key = asdt elif is_integer(key): # Period constructor will cast to string, which we dont want raise KeyError(key) try: key = Period(key, freq=self.freq) except ValueError: # we cannot construct the Period raise KeyError(key) ordinal = key.ordinal if key is not NaT else key.value try: return self._engine.get_loc(ordinal) except KeyError: try: 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 __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])) periods = dtl.validate_periods(periods) if name is None and hasattr(data, 'name'): name = data.name freq = dtl.validate_dtype_freq(dtype, freq) # coerce freq to freq object, otherwise it can be coerced elementwise # which is slow if freq: freq = Period._maybe_convert_freq(freq) if data is None: if ordinal is not None: data = np.asarray(ordinal, dtype=np.int64) else: data, freq = cls._generate_range(start, end, periods, freq, fields) return cls._simple_new(data, name=name, freq=freq) if isinstance(data, PeriodIndex): if freq is None or freq == data.freq: # no freq change freq = data.freq data = data._ndarray_values else: base1, _ = _gfc(data.freq) base2, _ = _gfc(freq) data = period.period_asfreq_arr(data._ndarray_values, base1, base2, 1) return cls._simple_new(data, name=name, freq=freq) # not array / index if not isinstance(data, (np.ndarray, PeriodIndex, DatetimeIndex, Int64Index)): if is_scalar(data): cls._scalar_data_error(data) # other iterable of some kind if not isinstance(data, (list, tuple)): data = list(data) data = np.asarray(data) # datetime other than period if is_datetime64_dtype(data.dtype): data = dt64arr_to_periodarr(data, freq, tz) return cls._simple_new(data, name=name, freq=freq) # check not floats if infer_dtype(data) == 'floating' and len(data) > 0: raise TypeError("PeriodIndex does not allow " "floating point in construction") # anything else, likely an array of strings or periods data = ensure_object(data) freq = freq or period.extract_freq(data) data = period.extract_ordinals(data, freq) return cls._simple_new(data, name=name, freq=freq)