diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 0d1c5241c5a93..d7dfa73c53d8d 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from datetime import timedelta +from datetime import datetime, timedelta import warnings import numpy as np @@ -22,6 +22,8 @@ _ensure_int64) from pandas.core.dtypes.dtypes import DatetimeTZDtype +from pandas.core.algorithms import checked_add_with_arr + from pandas.tseries.frequencies import to_offset, DateOffset from pandas.tseries.offsets import Tick @@ -281,6 +283,39 @@ def _add_offset(self, offset): return type(self)(result, freq='infer') + def _sub_datelike(self, other): + # subtract a datetime from myself, yielding a ndarray[timedelta64[ns]] + if isinstance(other, (DatetimeArrayMixin, np.ndarray)): + if isinstance(other, np.ndarray): + # if other is an ndarray, we assume it is datetime64-dtype + other = type(self)(other) + if not self._has_same_tz(other): + # require tz compat + raise TypeError("{cls} subtraction must have the same " + "timezones or no timezones" + .format(cls=type(self).__name__)) + result = self._sub_datelike_dti(other) + elif isinstance(other, (datetime, np.datetime64)): + assert other is not NaT + other = Timestamp(other) + if other is NaT: + return self - NaT + # require tz compat + elif not self._has_same_tz(other): + raise TypeError("Timestamp subtraction must have the same " + "timezones or no timezones") + else: + i8 = self.asi8 + result = checked_add_with_arr(i8, -other.value, + arr_mask=self._isnan) + result = self._maybe_mask_results(result, + fill_value=iNaT) + else: + raise TypeError("cannot subtract {cls} and {typ}" + .format(cls=type(self).__name__, + typ=type(other).__name__)) + return result.view('timedelta64[ns]') + def _add_delta(self, delta): """ Add a timedelta-like, DateOffset, or TimedeltaIndex-like object @@ -517,6 +552,47 @@ def to_pydatetime(self): """ return tslib.ints_to_pydatetime(self.asi8, tz=self.tz) + def normalize(self): + """ + Convert times to midnight. + + The time component of the date-time is converted to midnight i.e. + 00:00:00. This is useful in cases, when the time does not matter. + Length is unaltered. The timezones are unaffected. + + This method is available on Series with datetime values under + the ``.dt`` accessor, and directly on Datetime Array/Index. + + Returns + ------- + DatetimeArray, DatetimeIndex or Series + The same type as the original data. Series will have the same + name and index. DatetimeIndex will have the same name. + + See Also + -------- + floor : Floor the datetimes to the specified freq. + ceil : Ceil the datetimes to the specified freq. + round : Round the datetimes to the specified freq. + + Examples + -------- + >>> idx = pd.DatetimeIndex(start='2014-08-01 10:00', freq='H', + ... periods=3, tz='Asia/Calcutta') + >>> idx + DatetimeIndex(['2014-08-01 10:00:00+05:30', + '2014-08-01 11:00:00+05:30', + '2014-08-01 12:00:00+05:30'], + dtype='datetime64[ns, Asia/Calcutta]', freq='H') + >>> idx.normalize() + DatetimeIndex(['2014-08-01 00:00:00+05:30', + '2014-08-01 00:00:00+05:30', + '2014-08-01 00:00:00+05:30'], + dtype='datetime64[ns, Asia/Calcutta]', freq=None) + """ + new_values = conversion.normalize_i8_timestamps(self.asi8, self.tz) + return type(self)(new_values, freq='infer').tz_localize(self.tz) + # ----------------------------------------------------------------- # Properties - Vectorized Timestamp Properties/Methods diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 35baa3262d3dd..000775361061e 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -8,7 +8,7 @@ from pandas._libs.tslib import NaT, iNaT from pandas._libs.tslibs.period import ( Period, IncompatibleFrequency, DIFFERENT_FREQ_INDEX, - get_period_field_arr, period_asfreq_arr) + get_period_field_arr, period_asfreq_arr, _quarter_to_myear) from pandas._libs.tslibs import period as libperiod from pandas._libs.tslibs.timedeltas import delta_to_nanoseconds from pandas._libs.tslibs.fields import isleapyear_arr @@ -19,6 +19,9 @@ from pandas.core.dtypes.common import ( is_integer_dtype, is_float_dtype, is_period_dtype) from pandas.core.dtypes.dtypes import PeriodDtype +from pandas.core.dtypes.generic import ABCSeries + +import pandas.core.common as com from pandas.tseries import frequencies from pandas.tseries.offsets import Tick, DateOffset @@ -157,6 +160,25 @@ def _from_ordinals(cls, values, freq=None): result._freq = Period._maybe_convert_freq(freq) return result + @classmethod + 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 + # -------------------------------------------------------------------- # Vectorized analogues of Period properties @@ -371,3 +393,102 @@ def _add_comparison_methods(cls): PeriodArrayMixin._add_comparison_methods() + + +# ------------------------------------------------------------------- +# Constructor Helpers + +def _get_ordinal_range(start, end, periods, freq, mult=1): + 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 not None: + _, mult = frequencies.get_freq_code(freq) + + if start is not None: + start = Period(start, freq) + if end is not None: + end = Period(end, freq) + + is_start_per = isinstance(start, Period) + is_end_per = isinstance(end, Period) + + if is_start_per and is_end_per and start.freq != end.freq: + raise ValueError('start and end must have same freq') + if (start is NaT or end is NaT): + raise ValueError('start and end must not be NaT') + + if freq is None: + if is_start_per: + freq = start.freq + elif is_end_per: + freq = end.freq + else: # pragma: no cover + 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 + mult, + end.ordinal + 1, mult, + dtype=np.int64) + else: + data = np.arange(start.ordinal, start.ordinal + periods, mult, + dtype=np.int64) + else: + data = np.arange(start.ordinal, end.ordinal + 1, mult, dtype=np.int64) + + return data, freq + + +def _range_from_fields(year=None, month=None, quarter=None, day=None, + hour=None, minute=None, second=None, freq=None): + if hour is None: + hour = 0 + if minute is None: + minute = 0 + if second is None: + second = 0 + if day is None: + day = 1 + + ordinals = [] + + if quarter is not None: + if freq is None: + freq = 'Q' + base = frequencies.FreqGroup.FR_QTR + else: + base, mult = frequencies.get_freq_code(freq) + if base != frequencies.FreqGroup.FR_QTR: + raise AssertionError("base must equal FR_QTR") + + year, quarter = _make_field_arrays(year, quarter) + for y, q in compat.zip(year, quarter): + y, m = _quarter_to_myear(y, q, freq) + val = libperiod.period_ordinal(y, m, 1, 1, 1, 1, 0, 0, base) + ordinals.append(val) + else: + base, mult = frequencies.get_freq_code(freq) + arrays = _make_field_arrays(year, month, day, hour, minute, second) + for y, mth, d, h, mn, s in compat.zip(*arrays): + ordinals.append(libperiod.period_ordinal( + y, mth, d, h, mn, s, 0, 0, base)) + + return np.array(ordinals, dtype=np.int64), freq + + +def _make_field_arrays(*fields): + length = None + for x in fields: + if isinstance(x, (list, np.ndarray, ABCSeries)): + if length is not None and len(x) != length: + raise ValueError('Mismatched Period array lengths') + elif length is None: + length = len(x) + + arrays = [np.asarray(x) if isinstance(x, (np.ndarray, list, ABCSeries)) + else np.repeat(x, length) for x in fields] + + return arrays diff --git a/pandas/core/arrays/timedelta.py b/pandas/core/arrays/timedelta.py index f093cadec5a38..dbd481aae4f37 100644 --- a/pandas/core/arrays/timedelta.py +++ b/pandas/core/arrays/timedelta.py @@ -3,7 +3,7 @@ import numpy as np -from pandas._libs import tslibs +from pandas._libs import tslibs, lib from pandas._libs.tslibs import Timedelta, NaT from pandas._libs.tslibs.fields import get_timedelta_field from pandas._libs.tslibs.timedeltas import array_to_timedelta64 @@ -15,6 +15,8 @@ from pandas.core.dtypes.generic import ABCSeries from pandas.core.dtypes.missing import isna +import pandas.core.common as com + from pandas.tseries.offsets import Tick, DateOffset from pandas.tseries.frequencies import to_offset @@ -70,11 +72,27 @@ def _simple_new(cls, values, freq=None, **kwargs): result._freq = freq return result - def __new__(cls, values, freq=None): + def __new__(cls, values, freq=None, start=None, end=None, periods=None, + closed=None): if (freq is not None and not isinstance(freq, DateOffset) and freq != 'infer'): freq = to_offset(freq) + if periods is not None: + if lib.is_float(periods): + periods = int(periods) + elif not lib.is_integer(periods): + raise TypeError('`periods` must be a number, got {periods}' + .format(periods=periods)) + + if values is None: + if freq is None and com._any_none(periods, start, end): + raise ValueError('Must provide freq argument if no data is ' + 'supplied') + else: + return cls._generate(start, end, periods, freq, + closed=closed) + result = cls._simple_new(values, freq=freq) if freq == 'infer': inferred = result.inferred_freq @@ -83,6 +101,52 @@ def __new__(cls, values, freq=None): return result + @classmethod + def _generate(cls, start, end, periods, freq, closed=None, **kwargs): + # **kwargs are for compat with TimedeltaIndex, which includes `name` + if com._count_not_none(start, end, periods, freq) != 3: + raise ValueError('Of the four parameters: start, end, periods, ' + 'and freq, exactly three must be specified') + + if start is not None: + start = Timedelta(start) + + if end is not None: + end = Timedelta(end) + + left_closed = False + right_closed = False + + if start is None and end is None: + if closed is not None: + raise ValueError("Closed has to be None if not both of start" + "and end are defined") + + if closed is None: + left_closed = True + right_closed = True + elif closed == "left": + left_closed = True + elif closed == "right": + right_closed = True + else: + raise ValueError("Closed has to be either 'left', 'right' or None") + + if freq is not None: + index = _generate_regular_range(start, end, periods, freq) + index = cls._simple_new(index, freq=freq, **kwargs) + else: + index = np.linspace(start.value, end.value, periods).astype('i8') + # TODO: shouldn't we pass `name` here? (via **kwargs) + index = cls._simple_new(index, freq=freq) + + if not left_closed: + index = index[1:] + if not right_closed: + index = index[:-1] + + return index + # ---------------------------------------------------------------- # Arithmetic Methods @@ -173,6 +237,45 @@ def total_seconds(self): the return type is a Float64Index. When the calling object is a Series, the return type is Series of type `float64` whose index is the same as the original. + + See Also + -------- + datetime.timedelta.total_seconds : Standard library version + of this method. + TimedeltaIndex.components : Return a DataFrame with components of + each Timedelta. + + Examples + -------- + **Series** + + >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit='d')) + >>> s + 0 0 days + 1 1 days + 2 2 days + 3 3 days + 4 4 days + dtype: timedelta64[ns] + + >>> s.dt.total_seconds() + 0 0.0 + 1 86400.0 + 2 172800.0 + 3 259200.0 + 4 345600.0 + dtype: float64 + + **TimedeltaIndex** + + >>> idx = pd.to_timedelta(np.arange(5), unit='d') + >>> idx + TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], + dtype='timedelta64[ns]', freq=None) + + >>> idx.total_seconds() + Float64Index([0.0, 86400.0, 172800.0, 259200.00000000003, 345600.0], + dtype='float64') """ return self._maybe_mask_results(1e-9 * self.asi8) @@ -198,3 +301,55 @@ def to_pytimedelta(self): nanoseconds = _field_accessor("nanoseconds", "nanoseconds", "\nNumber of nanoseconds (>= 0 and less " "than 1 microsecond) for each\nelement.\n") + + @property + def components(self): + """ + Return a dataframe of the components (days, hours, minutes, + seconds, milliseconds, microseconds, nanoseconds) of the Timedeltas. + + Returns + ------- + a DataFrame + """ + from pandas import DataFrame + + columns = ['days', 'hours', 'minutes', 'seconds', + 'milliseconds', 'microseconds', 'nanoseconds'] + hasnans = self.hasnans + if hasnans: + def f(x): + if isna(x): + return [np.nan] * len(columns) + return x.components + else: + def f(x): + return x.components + + result = DataFrame([f(x) for x in self], columns=columns) + if not hasnans: + result = result.astype('int64') + return result + + +# --------------------------------------------------------------------- +# Constructor Helpers + +def _generate_regular_range(start, end, periods, offset): + stride = offset.nanos + if periods is None: + b = Timedelta(start).value + e = Timedelta(end).value + e += stride - e % stride + elif start is not None: + b = Timedelta(start).value + e = b + periods * stride + elif end is not None: + e = Timedelta(end).value + stride + b = e - periods * stride + else: + raise ValueError("at least 'start' or 'end' should be specified " + "if a 'period' is given.") + + data = np.arange(b, e, stride, dtype=np.int64) + return data diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index b8a89ac26c9d9..bc0185bfaaafe 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -31,7 +31,6 @@ from pandas.core.dtypes.missing import isna import pandas.core.dtypes.concat as _concat -from pandas.core.algorithms import checked_add_with_arr from pandas.core.arrays.datetimes import DatetimeArrayMixin from pandas.core.indexes.base import Index, _index_shared_docs @@ -786,38 +785,6 @@ def __setstate__(self, state): raise Exception("invalid pickle state") _unpickle_compat = __setstate__ - def _sub_datelike(self, other): - # subtract a datetime from myself, yielding a ndarray[timedelta64[ns]] - if isinstance(other, (DatetimeIndex, np.ndarray)): - # if other is an ndarray, we assume it is datetime64-dtype - other = DatetimeIndex(other) - # require tz compat - if not self._has_same_tz(other): - raise TypeError("{cls} subtraction must have the same " - "timezones or no timezones" - .format(cls=type(self).__name__)) - result = self._sub_datelike_dti(other) - elif isinstance(other, (datetime, np.datetime64)): - assert other is not tslibs.NaT - other = Timestamp(other) - if other is tslibs.NaT: - return self - tslibs.NaT - # require tz compat - elif not self._has_same_tz(other): - raise TypeError("Timestamp subtraction must have the same " - "timezones or no timezones") - else: - i8 = self.asi8 - result = checked_add_with_arr(i8, -other.value, - arr_mask=self._isnan) - result = self._maybe_mask_results(result, - fill_value=tslibs.iNaT) - else: - raise TypeError("cannot subtract {cls} and {typ}" - .format(cls=type(self).__name__, - typ=type(other).__name__)) - return result.view('timedelta64[ns]') - def _maybe_update_attributes(self, attrs): """ Update Index attributes (e.g. freq) depending on op """ freq = attrs.get('freq', None) @@ -1585,48 +1552,11 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): is_year_end = _wrap_field_accessor('is_year_end') is_leap_year = _wrap_field_accessor('is_leap_year') + @Appender(DatetimeArrayMixin.normalize.__doc__) def normalize(self): - """ - Convert times to midnight. - - The time component of the date-time is converted to midnight i.e. - 00:00:00. This is useful in cases, when the time does not matter. - Length is unaltered. The timezones are unaffected. - - This method is available on Series with datetime values under - the ``.dt`` accessor, and directly on DatetimeIndex. - - Returns - ------- - DatetimeIndex or Series - The same type as the original data. Series will have the same - name and index. DatetimeIndex will have the same name. - - See Also - -------- - floor : Floor the datetimes to the specified freq. - ceil : Ceil the datetimes to the specified freq. - round : Round the datetimes to the specified freq. - - Examples - -------- - >>> idx = pd.DatetimeIndex(start='2014-08-01 10:00', freq='H', - ... periods=3, tz='Asia/Calcutta') - >>> idx - DatetimeIndex(['2014-08-01 10:00:00+05:30', - '2014-08-01 11:00:00+05:30', - '2014-08-01 12:00:00+05:30'], - dtype='datetime64[ns, Asia/Calcutta]', freq='H') - >>> idx.normalize() - DatetimeIndex(['2014-08-01 00:00:00+05:30', - '2014-08-01 00:00:00+05:30', - '2014-08-01 00:00:00+05:30'], - dtype='datetime64[ns, Asia/Calcutta]', freq=None) - """ - new_values = conversion.normalize_i8_timestamps(self.asi8, self.tz) - return DatetimeIndex(new_values, - freq='infer', - name=self.name).tz_localize(self.tz) + result = DatetimeArrayMixin.normalize(self) + result.name = self.name + return result @Substitution(klass='DatetimeIndex') @Appender(_shared_docs['searchsorted']) diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index a531a57eb031f..a8e0c7f1aaa6a 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -16,7 +16,6 @@ is_bool_dtype, pandas_dtype, _ensure_object) -from pandas.core.dtypes.generic import ABCSeries import pandas.tseries.frequencies as frequencies from pandas.tseries.frequencies import get_freq_code as _gfc @@ -29,7 +28,7 @@ from pandas._libs import tslib, index as libindex from pandas._libs.tslibs.period import (Period, IncompatibleFrequency, DIFFERENT_FREQ_INDEX, - _validate_end_alias, _quarter_to_myear) + _validate_end_alias) from pandas._libs.tslibs import resolution, period from pandas.core.arrays.period import PeriodArrayMixin @@ -39,7 +38,6 @@ from pandas import compat from pandas.util._decorators import (Appender, Substitution, cache_readonly, deprecate_kwarg) -from pandas.compat import zip import pandas.core.indexes.base as ibase _index_doc_kwargs = dict(ibase._index_doc_kwargs) @@ -266,25 +264,6 @@ def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None, def _engine(self): return self._engine_type(lambda: self, len(self)) - @classmethod - 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 - @classmethod def _simple_new(cls, values, name=None, freq=None, **kwargs): """ @@ -877,102 +856,6 @@ def tz_localize(self, tz, ambiguous='raise'): PeriodIndex._add_datetimelike_methods() -def _get_ordinal_range(start, end, periods, freq, mult=1): - 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 not None: - _, mult = _gfc(freq) - - if start is not None: - start = Period(start, freq) - if end is not None: - end = Period(end, freq) - - is_start_per = isinstance(start, Period) - is_end_per = isinstance(end, Period) - - if is_start_per and is_end_per and start.freq != end.freq: - raise ValueError('start and end must have same freq') - if (start is tslib.NaT or end is tslib.NaT): - raise ValueError('start and end must not be NaT') - - if freq is None: - if is_start_per: - freq = start.freq - elif is_end_per: - freq = end.freq - else: # pragma: no cover - 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 + mult, - end.ordinal + 1, mult, - dtype=np.int64) - else: - data = np.arange(start.ordinal, start.ordinal + periods, mult, - dtype=np.int64) - else: - data = np.arange(start.ordinal, end.ordinal + 1, mult, dtype=np.int64) - - return data, freq - - -def _range_from_fields(year=None, month=None, quarter=None, day=None, - hour=None, minute=None, second=None, freq=None): - if hour is None: - hour = 0 - if minute is None: - minute = 0 - if second is None: - second = 0 - if day is None: - day = 1 - - ordinals = [] - - if quarter is not None: - if freq is None: - freq = 'Q' - base = frequencies.FreqGroup.FR_QTR - else: - base, mult = _gfc(freq) - if base != frequencies.FreqGroup.FR_QTR: - raise AssertionError("base must equal FR_QTR") - - year, quarter = _make_field_arrays(year, quarter) - for y, q in zip(year, quarter): - y, m = _quarter_to_myear(y, q, freq) - val = period.period_ordinal(y, m, 1, 1, 1, 1, 0, 0, base) - ordinals.append(val) - else: - base, mult = _gfc(freq) - 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)) - - return np.array(ordinals, dtype=np.int64), freq - - -def _make_field_arrays(*fields): - length = None - for x in fields: - if isinstance(x, (list, np.ndarray, ABCSeries)): - if length is not None and len(x) != length: - raise ValueError('Mismatched Period array lengths') - elif length is None: - length = len(x) - - arrays = [np.asarray(x) if isinstance(x, (np.ndarray, list, ABCSeries)) - else np.repeat(x, length) for x in fields] - - return arrays - - def pnow(freq=None): # deprecation, xref #13790 warnings.warn("pd.pnow() and pandas.core.indexes.period.pnow() " diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 3af825455caac..eb1171c45b1e5 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -1,7 +1,5 @@ """ implement the TimedeltaIndex """ -from datetime import timedelta - import numpy as np from pandas.core.dtypes.common import ( _TD_DTYPE, @@ -17,7 +15,8 @@ from pandas.core.dtypes.missing import isna from pandas.core.dtypes.generic import ABCSeries -from pandas.core.arrays.timedelta import TimedeltaArrayMixin +from pandas.core.arrays.timedelta import ( + TimedeltaArrayMixin, _is_convertible_to_td) from pandas.core.indexes.base import Index from pandas.core.indexes.numeric import Int64Index import pandas.compat as compat @@ -33,7 +32,7 @@ TimelikeOps, DatetimeIndexOpsMixin) from pandas.core.tools.timedeltas import ( to_timedelta, _coerce_scalar_to_timedelta_type) -from pandas.tseries.offsets import Tick, DateOffset +from pandas.tseries.offsets import DateOffset from pandas._libs import (lib, index as libindex, join as libjoin, Timedelta, NaT, iNaT) @@ -268,46 +267,11 @@ def __new__(cls, data=None, unit=None, freq=None, start=None, end=None, @classmethod def _generate(cls, start, end, periods, name, freq, closed=None): - if com._count_not_none(start, end, periods, freq) != 3: - raise ValueError('Of the four parameters: start, end, periods, ' - 'and freq, exactly three must be specified') - - if start is not None: - start = Timedelta(start) - - if end is not None: - end = Timedelta(end) - - left_closed = False - right_closed = False - - if start is None and end is None: - if closed is not None: - raise ValueError("Closed has to be None if not both of start" - "and end are defined") - - if closed is None: - left_closed = True - right_closed = True - elif closed == "left": - left_closed = True - elif closed == "right": - right_closed = True - else: - raise ValueError("Closed has to be either 'left', 'right' or None") - - if freq is not None: - index = _generate_regular_range(start, end, periods, freq) - index = cls._simple_new(index, name=name, freq=freq) - else: - index = to_timedelta(np.linspace(start.value, end.value, periods)) - - if not left_closed: - index = index[1:] - if not right_closed: - index = index[:-1] - - return index + # TimedeltaArray gets `name` via **kwargs, so we need to explicitly + # override it if name is passed as a positional argument + return super(TimedeltaIndex, cls)._generate(start, end, + periods, freq, + name=name, closed=closed) @classmethod def _simple_new(cls, values, name=None, freq=None, **kwargs): @@ -383,90 +347,8 @@ def _format_native_types(self, na_rep=u'NaT', date_format=None, **kwargs): microseconds = _wrap_field_accessor("microseconds") nanoseconds = _wrap_field_accessor("nanoseconds") - @property - def components(self): - """ - Return a dataframe of the components (days, hours, minutes, - seconds, milliseconds, microseconds, nanoseconds) of the Timedeltas. - - Returns - ------- - a DataFrame - """ - from pandas import DataFrame - - columns = ['days', 'hours', 'minutes', 'seconds', - 'milliseconds', 'microseconds', 'nanoseconds'] - hasnans = self.hasnans - if hasnans: - def f(x): - if isna(x): - return [np.nan] * len(columns) - return x.components - else: - def f(x): - return x.components - - result = DataFrame([f(x) for x in self]) - result.columns = columns - if not hasnans: - result = result.astype('int64') - return result - + @Appender(TimedeltaArrayMixin.total_seconds.__doc__) def total_seconds(self): - """ - Return total duration of each element expressed in seconds. - - This method is available directly on TimedeltaIndex and on Series - containing timedelta values under the ``.dt`` namespace. - - Returns - ------- - seconds : Float64Index or Series - When the calling object is a TimedeltaIndex, the return type is a - Float64Index. When the calling object is a Series, the return type - is Series of type `float64` whose index is the same as the - original. - - See Also - -------- - datetime.timedelta.total_seconds : Standard library version - of this method. - TimedeltaIndex.components : Return a DataFrame with components of - each Timedelta. - - Examples - -------- - **Series** - - >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit='d')) - >>> s - 0 0 days - 1 1 days - 2 2 days - 3 3 days - 4 4 days - dtype: timedelta64[ns] - - >>> s.dt.total_seconds() - 0 0.0 - 1 86400.0 - 2 172800.0 - 3 259200.0 - 4 345600.0 - dtype: float64 - - **TimedeltaIndex** - - >>> idx = pd.to_timedelta(np.arange(5), unit='d') - >>> idx - TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], - dtype='timedelta64[ns]', freq=None) - - >>> idx.total_seconds() - Float64Index([0.0, 86400.0, 172800.0, 259200.00000000003, 345600.0], - dtype='float64') - """ result = TimedeltaArrayMixin.total_seconds(self) return Index(result, name=self.name) @@ -915,11 +797,6 @@ def _is_convertible_to_index(other): return False -def _is_convertible_to_td(key): - return isinstance(key, (Tick, timedelta, - np.timedelta64, compat.string_types)) - - def _to_m8(key): """ Timedelta-like => dt64 @@ -932,28 +809,6 @@ def _to_m8(key): return np.int64(key.value).view(_TD_DTYPE) -def _generate_regular_range(start, end, periods, offset): - stride = offset.nanos - if periods is None: - b = Timedelta(start).value - e = Timedelta(end).value - e += stride - e % stride - elif start is not None: - b = Timedelta(start).value - e = b + periods * stride - elif end is not None: - e = Timedelta(end).value + stride - b = e - periods * stride - else: - raise ValueError("at least 'start' or 'end' should be specified " - "if a 'period' is given.") - - data = np.arange(b, e, stride, dtype=np.int64) - data = TimedeltaIndex._simple_new(data, None) - - return data - - def timedelta_range(start=None, end=None, periods=None, freq=None, name=None, closed=None): """