1""" implement the TimedeltaIndex """
2from __future__ import annotations
3
4from typing import TYPE_CHECKING
5import warnings
6
7from pandas._libs import (
8 index as libindex,
9 lib,
10)
11from pandas._libs.tslibs import (
12 Resolution,
13 Timedelta,
14 to_offset,
15)
16from pandas._libs.tslibs.timedeltas import disallow_ambiguous_unit
17from pandas.util._exceptions import find_stack_level
18
19from pandas.core.dtypes.common import (
20 is_scalar,
21 pandas_dtype,
22)
23from pandas.core.dtypes.generic import ABCSeries
24
25from pandas.core.arrays.timedeltas import TimedeltaArray
26import pandas.core.common as com
27from pandas.core.indexes.base import (
28 Index,
29 maybe_extract_name,
30)
31from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin
32from pandas.core.indexes.extension import inherit_names
33
34if TYPE_CHECKING:
35 from pandas._typing import DtypeObj
36
37
38@inherit_names(
39 ["__neg__", "__pos__", "__abs__", "total_seconds", "round", "floor", "ceil"]
40 + TimedeltaArray._field_ops,
41 TimedeltaArray,
42 wrap=True,
43)
44@inherit_names(
45 [
46 "components",
47 "to_pytimedelta",
48 "sum",
49 "std",
50 "median",
51 ],
52 TimedeltaArray,
53)
54class TimedeltaIndex(DatetimeTimedeltaMixin):
55 """
56 Immutable Index of timedelta64 data.
57
58 Represented internally as int64, and scalars returned Timedelta objects.
59
60 Parameters
61 ----------
62 data : array-like (1-dimensional), optional
63 Optional timedelta-like data to construct index with.
64 unit : {'D', 'h', 'm', 's', 'ms', 'us', 'ns'}, optional
65 The unit of ``data``.
66
67 .. deprecated:: 2.2.0
68 Use ``pd.to_timedelta`` instead.
69
70 freq : str or pandas offset object, optional
71 One of pandas date offset strings or corresponding objects. The string
72 ``'infer'`` can be passed in order to set the frequency of the index as
73 the inferred frequency upon creation.
74 dtype : numpy.dtype or str, default None
75 Valid ``numpy`` dtypes are ``timedelta64[ns]``, ``timedelta64[us]``,
76 ``timedelta64[ms]``, and ``timedelta64[s]``.
77 copy : bool
78 Make a copy of input array.
79 name : object
80 Name to be stored in the index.
81
82 Attributes
83 ----------
84 days
85 seconds
86 microseconds
87 nanoseconds
88 components
89 inferred_freq
90
91 Methods
92 -------
93 to_pytimedelta
94 to_series
95 round
96 floor
97 ceil
98 to_frame
99 mean
100
101 See Also
102 --------
103 Index : The base pandas Index type.
104 Timedelta : Represents a duration between two dates or times.
105 DatetimeIndex : Index of datetime64 data.
106 PeriodIndex : Index of Period data.
107 timedelta_range : Create a fixed-frequency TimedeltaIndex.
108
109 Notes
110 -----
111 To learn more about the frequency strings, please see `this link
112 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__.
113
114 Examples
115 --------
116 >>> pd.TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'])
117 TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'],
118 dtype='timedelta64[ns]', freq=None)
119
120 We can also let pandas infer the frequency when possible.
121
122 >>> pd.TimedeltaIndex(np.arange(5) * 24 * 3600 * 1e9, freq='infer')
123 TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'],
124 dtype='timedelta64[ns]', freq='D')
125 """
126
127 _typ = "timedeltaindex"
128
129 _data_cls = TimedeltaArray
130
131 @property
132 def _engine_type(self) -> type[libindex.TimedeltaEngine]:
133 return libindex.TimedeltaEngine
134
135 _data: TimedeltaArray
136
137 # Use base class method instead of DatetimeTimedeltaMixin._get_string_slice
138 _get_string_slice = Index._get_string_slice
139
140 # error: Signature of "_resolution_obj" incompatible with supertype
141 # "DatetimeIndexOpsMixin"
142 @property
143 def _resolution_obj(self) -> Resolution | None: # type: ignore[override]
144 return self._data._resolution_obj
145
146 # -------------------------------------------------------------------
147 # Constructors
148
149 def __new__(
150 cls,
151 data=None,
152 unit=lib.no_default,
153 freq=lib.no_default,
154 closed=lib.no_default,
155 dtype=None,
156 copy: bool = False,
157 name=None,
158 ):
159 if closed is not lib.no_default:
160 # GH#52628
161 warnings.warn(
162 f"The 'closed' keyword in {cls.__name__} construction is "
163 "deprecated and will be removed in a future version.",
164 FutureWarning,
165 stacklevel=find_stack_level(),
166 )
167
168 if unit is not lib.no_default:
169 # GH#55499
170 warnings.warn(
171 f"The 'unit' keyword in {cls.__name__} construction is "
172 "deprecated and will be removed in a future version. "
173 "Use pd.to_timedelta instead.",
174 FutureWarning,
175 stacklevel=find_stack_level(),
176 )
177 else:
178 unit = None
179
180 name = maybe_extract_name(name, data, cls)
181
182 if is_scalar(data):
183 cls._raise_scalar_data_error(data)
184
185 disallow_ambiguous_unit(unit)
186 if dtype is not None:
187 dtype = pandas_dtype(dtype)
188
189 if (
190 isinstance(data, TimedeltaArray)
191 and freq is lib.no_default
192 and (dtype is None or dtype == data.dtype)
193 ):
194 if copy:
195 data = data.copy()
196 return cls._simple_new(data, name=name)
197
198 if (
199 isinstance(data, TimedeltaIndex)
200 and freq is lib.no_default
201 and name is None
202 and (dtype is None or dtype == data.dtype)
203 ):
204 if copy:
205 return data.copy()
206 else:
207 return data._view()
208
209 # - Cases checked above all return/raise before reaching here - #
210
211 tdarr = TimedeltaArray._from_sequence_not_strict(
212 data, freq=freq, unit=unit, dtype=dtype, copy=copy
213 )
214 refs = None
215 if not copy and isinstance(data, (ABCSeries, Index)):
216 refs = data._references
217
218 return cls._simple_new(tdarr, name=name, refs=refs)
219
220 # -------------------------------------------------------------------
221
222 def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
223 """
224 Can we compare values of the given dtype to our own?
225 """
226 return lib.is_np_dtype(dtype, "m") # aka self._data._is_recognized_dtype
227
228 # -------------------------------------------------------------------
229 # Indexing Methods
230
231 def get_loc(self, key):
232 """
233 Get integer location for requested label
234
235 Returns
236 -------
237 loc : int, slice, or ndarray[int]
238 """
239 self._check_indexing_error(key)
240
241 try:
242 key = self._data._validate_scalar(key, unbox=False)
243 except TypeError as err:
244 raise KeyError(key) from err
245
246 return Index.get_loc(self, key)
247
248 def _parse_with_reso(self, label: str):
249 # the "with_reso" is a no-op for TimedeltaIndex
250 parsed = Timedelta(label)
251 return parsed, None
252
253 def _parsed_string_to_bounds(self, reso, parsed: Timedelta):
254 # reso is unused, included to match signature of DTI/PI
255 lbound = parsed.round(parsed.resolution_string)
256 rbound = lbound + to_offset(parsed.resolution_string) - Timedelta(1, "ns")
257 return lbound, rbound
258
259 # -------------------------------------------------------------------
260
261 @property
262 def inferred_type(self) -> str:
263 return "timedelta64"
264
265
266def timedelta_range(
267 start=None,
268 end=None,
269 periods: int | None = None,
270 freq=None,
271 name=None,
272 closed=None,
273 *,
274 unit: str | None = None,
275) -> TimedeltaIndex:
276 """
277 Return a fixed frequency TimedeltaIndex with day as the default.
278
279 Parameters
280 ----------
281 start : str or timedelta-like, default None
282 Left bound for generating timedeltas.
283 end : str or timedelta-like, default None
284 Right bound for generating timedeltas.
285 periods : int, default None
286 Number of periods to generate.
287 freq : str, Timedelta, datetime.timedelta, or DateOffset, default 'D'
288 Frequency strings can have multiples, e.g. '5h'.
289 name : str, default None
290 Name of the resulting TimedeltaIndex.
291 closed : str, default None
292 Make the interval closed with respect to the given frequency to
293 the 'left', 'right', or both sides (None).
294 unit : str, default None
295 Specify the desired resolution of the result.
296
297 .. versionadded:: 2.0.0
298
299 Returns
300 -------
301 TimedeltaIndex
302
303 Notes
304 -----
305 Of the four parameters ``start``, ``end``, ``periods``, and ``freq``,
306 exactly three must be specified. If ``freq`` is omitted, the resulting
307 ``TimedeltaIndex`` will have ``periods`` linearly spaced elements between
308 ``start`` and ``end`` (closed on both sides).
309
310 To learn more about the frequency strings, please see `this link
311 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__.
312
313 Examples
314 --------
315 >>> pd.timedelta_range(start='1 day', periods=4)
316 TimedeltaIndex(['1 days', '2 days', '3 days', '4 days'],
317 dtype='timedelta64[ns]', freq='D')
318
319 The ``closed`` parameter specifies which endpoint is included. The default
320 behavior is to include both endpoints.
321
322 >>> pd.timedelta_range(start='1 day', periods=4, closed='right')
323 TimedeltaIndex(['2 days', '3 days', '4 days'],
324 dtype='timedelta64[ns]', freq='D')
325
326 The ``freq`` parameter specifies the frequency of the TimedeltaIndex.
327 Only fixed frequencies can be passed, non-fixed frequencies such as
328 'M' (month end) will raise.
329
330 >>> pd.timedelta_range(start='1 day', end='2 days', freq='6h')
331 TimedeltaIndex(['1 days 00:00:00', '1 days 06:00:00', '1 days 12:00:00',
332 '1 days 18:00:00', '2 days 00:00:00'],
333 dtype='timedelta64[ns]', freq='6h')
334
335 Specify ``start``, ``end``, and ``periods``; the frequency is generated
336 automatically (linearly spaced).
337
338 >>> pd.timedelta_range(start='1 day', end='5 days', periods=4)
339 TimedeltaIndex(['1 days 00:00:00', '2 days 08:00:00', '3 days 16:00:00',
340 '5 days 00:00:00'],
341 dtype='timedelta64[ns]', freq=None)
342
343 **Specify a unit**
344
345 >>> pd.timedelta_range("1 Day", periods=3, freq="100000D", unit="s")
346 TimedeltaIndex(['1 days', '100001 days', '200001 days'],
347 dtype='timedelta64[s]', freq='100000D')
348 """
349 if freq is None and com.any_none(periods, start, end):
350 freq = "D"
351
352 freq = to_offset(freq)
353 tdarr = TimedeltaArray._generate_range(
354 start, end, periods, freq, closed=closed, unit=unit
355 )
356 return TimedeltaIndex._simple_new(tdarr, name=name)