Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/core/arrays/timedeltas.py: 17%
434 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from __future__ import annotations
3from datetime import timedelta
4from typing import (
5 TYPE_CHECKING,
6 cast,
7)
9import numpy as np
11from pandas._libs import (
12 lib,
13 tslibs,
14)
15from pandas._libs.tslibs import (
16 BaseOffset,
17 NaT,
18 NaTType,
19 Tick,
20 Timedelta,
21 astype_overflowsafe,
22 iNaT,
23 periods_per_second,
24 to_offset,
25)
26from pandas._libs.tslibs.conversion import precision_from_unit
27from pandas._libs.tslibs.fields import get_timedelta_field
28from pandas._libs.tslibs.timedeltas import (
29 array_to_timedelta64,
30 ints_to_pytimedelta,
31 parse_timedelta_unit,
32)
33from pandas._typing import (
34 DtypeObj,
35 NpDtype,
36 npt,
37)
38from pandas.compat.numpy import function as nv
39from pandas.util._validators import validate_endpoints
41from pandas.core.dtypes.astype import astype_td64_unit_conversion
42from pandas.core.dtypes.common import (
43 TD64NS_DTYPE,
44 is_dtype_equal,
45 is_float_dtype,
46 is_integer_dtype,
47 is_object_dtype,
48 is_scalar,
49 is_string_dtype,
50 is_timedelta64_dtype,
51 pandas_dtype,
52)
53from pandas.core.dtypes.missing import isna
55from pandas.core import nanops
56from pandas.core.arrays import datetimelike as dtl
57from pandas.core.arrays._ranges import generate_regular_range
58import pandas.core.common as com
59from pandas.core.ops.common import unpack_zerodim_and_defer
61if TYPE_CHECKING: 61 ↛ 62line 61 didn't jump to line 62, because the condition on line 61 was never true
62 from pandas import DataFrame
65def _field_accessor(name: str, alias: str, docstring: str):
66 def f(self) -> np.ndarray:
67 values = self.asi8
68 result = get_timedelta_field(values, alias, reso=self._reso)
69 if self._hasna:
70 result = self._maybe_mask_results(
71 result, fill_value=None, convert="float64"
72 )
74 return result
76 f.__name__ = name
77 f.__doc__ = f"\n{docstring}\n"
78 return property(f)
81class TimedeltaArray(dtl.TimelikeOps):
82 """
83 Pandas ExtensionArray for timedelta data.
85 .. warning::
87 TimedeltaArray is currently experimental, and its API may change
88 without warning. In particular, :attr:`TimedeltaArray.dtype` is
89 expected to change to be an instance of an ``ExtensionDtype``
90 subclass.
92 Parameters
93 ----------
94 values : array-like
95 The timedelta data.
97 dtype : numpy.dtype
98 Currently, only ``numpy.dtype("timedelta64[ns]")`` is accepted.
99 freq : Offset, optional
100 copy : bool, default False
101 Whether to copy the underlying array of data.
103 Attributes
104 ----------
105 None
107 Methods
108 -------
109 None
110 """
112 _typ = "timedeltaarray"
113 _internal_fill_value = np.timedelta64("NaT", "ns")
114 _recognized_scalars = (timedelta, np.timedelta64, Tick)
115 _is_recognized_dtype = is_timedelta64_dtype
116 _infer_matches = ("timedelta", "timedelta64")
118 @property
119 def _scalar_type(self) -> type[Timedelta]:
120 return Timedelta
122 __array_priority__ = 1000
123 # define my properties & methods for delegation
124 _other_ops: list[str] = []
125 _bool_ops: list[str] = []
126 _object_ops: list[str] = ["freq"]
127 _field_ops: list[str] = ["days", "seconds", "microseconds", "nanoseconds"]
128 _datetimelike_ops: list[str] = _field_ops + _object_ops + _bool_ops
129 _datetimelike_methods: list[str] = [
130 "to_pytimedelta",
131 "total_seconds",
132 "round",
133 "floor",
134 "ceil",
135 ]
137 # Note: ndim must be defined to ensure NaT.__richcmp__(TimedeltaArray)
138 # operates pointwise.
140 def _box_func(self, x: np.timedelta64) -> Timedelta | NaTType:
141 y = x.view("i8")
142 if y == NaT.value:
143 return NaT
144 return Timedelta._from_value_and_reso(y, reso=self._reso)
146 @property
147 # error: Return type "dtype" of "dtype" incompatible with return type
148 # "ExtensionDtype" in supertype "ExtensionArray"
149 def dtype(self) -> np.dtype: # type: ignore[override]
150 """
151 The dtype for the TimedeltaArray.
153 .. warning::
155 A future version of pandas will change dtype to be an instance
156 of a :class:`pandas.api.extensions.ExtensionDtype` subclass,
157 not a ``numpy.dtype``.
159 Returns
160 -------
161 numpy.dtype
162 """
163 return self._ndarray.dtype
165 # ----------------------------------------------------------------
166 # Constructors
168 _freq = None
169 _default_dtype = TD64NS_DTYPE # used in TimeLikeOps.__init__
171 @classmethod
172 def _validate_dtype(cls, values, dtype):
173 # used in TimeLikeOps.__init__
174 _validate_td64_dtype(values.dtype)
175 dtype = _validate_td64_dtype(dtype)
176 return dtype
178 # error: Signature of "_simple_new" incompatible with supertype "NDArrayBacked"
179 @classmethod
180 def _simple_new( # type: ignore[override]
181 cls, values: np.ndarray, freq: BaseOffset | None = None, dtype=TD64NS_DTYPE
182 ) -> TimedeltaArray:
183 # Require td64 dtype, not unit-less, matching values.dtype
184 assert isinstance(dtype, np.dtype) and dtype.kind == "m"
185 assert not tslibs.is_unitless(dtype)
186 assert isinstance(values, np.ndarray), type(values)
187 assert dtype == values.dtype
189 result = super()._simple_new(values=values, dtype=dtype)
190 result._freq = freq
191 return result
193 @classmethod
194 def _from_sequence(
195 cls, data, *, dtype=TD64NS_DTYPE, copy: bool = False
196 ) -> TimedeltaArray:
197 if dtype:
198 _validate_td64_dtype(dtype)
200 data, inferred_freq = sequence_to_td64ns(data, copy=copy, unit=None)
201 freq, _ = dtl.validate_inferred_freq(None, inferred_freq, False)
203 return cls._simple_new(data, dtype=data.dtype, freq=freq)
205 @classmethod
206 def _from_sequence_not_strict(
207 cls,
208 data,
209 dtype=TD64NS_DTYPE,
210 copy: bool = False,
211 freq=lib.no_default,
212 unit=None,
213 ) -> TimedeltaArray:
214 if dtype:
215 _validate_td64_dtype(dtype)
217 assert unit not in ["Y", "y", "M"] # caller is responsible for checking
219 explicit_none = freq is None
220 freq = freq if freq is not lib.no_default else None
222 freq, freq_infer = dtl.maybe_infer_freq(freq)
224 data, inferred_freq = sequence_to_td64ns(data, copy=copy, unit=unit)
225 freq, freq_infer = dtl.validate_inferred_freq(freq, inferred_freq, freq_infer)
226 if explicit_none:
227 freq = None
229 result = cls._simple_new(data, dtype=data.dtype, freq=freq)
231 if inferred_freq is None and freq is not None:
232 # this condition precludes `freq_infer`
233 cls._validate_frequency(result, freq)
235 elif freq_infer:
236 # Set _freq directly to bypass duplicative _validate_frequency
237 # check.
238 result._freq = to_offset(result.inferred_freq)
240 return result
242 @classmethod
243 def _generate_range(cls, start, end, periods, freq, closed=None):
245 periods = dtl.validate_periods(periods)
246 if freq is None and any(x is None for x in [periods, start, end]):
247 raise ValueError("Must provide freq argument if no data is supplied")
249 if com.count_not_none(start, end, periods, freq) != 3:
250 raise ValueError(
251 "Of the four parameters: start, end, periods, "
252 "and freq, exactly three must be specified"
253 )
255 if start is not None:
256 start = Timedelta(start)
258 if end is not None:
259 end = Timedelta(end)
261 left_closed, right_closed = validate_endpoints(closed)
263 if freq is not None:
264 index = generate_regular_range(start, end, periods, freq)
265 else:
266 index = np.linspace(start.value, end.value, periods).astype("i8")
268 if not left_closed:
269 index = index[1:]
270 if not right_closed:
271 index = index[:-1]
273 td64values = index.view("m8[ns]")
274 return cls._simple_new(td64values, dtype=td64values.dtype, freq=freq)
276 # ----------------------------------------------------------------
277 # DatetimeLike Interface
279 def _unbox_scalar(self, value, setitem: bool = False) -> np.timedelta64:
280 if not isinstance(value, self._scalar_type) and value is not NaT:
281 raise ValueError("'value' should be a Timedelta.")
282 self._check_compatible_with(value, setitem=setitem)
283 return np.timedelta64(value.value, "ns")
285 def _scalar_from_string(self, value) -> Timedelta | NaTType:
286 return Timedelta(value)
288 def _check_compatible_with(self, other, setitem: bool = False) -> None:
289 # we don't have anything to validate.
290 pass
292 # ----------------------------------------------------------------
293 # Array-Like / EA-Interface Methods
295 def astype(self, dtype, copy: bool = True):
296 # We handle
297 # --> timedelta64[ns]
298 # --> timedelta64
299 # DatetimeLikeArrayMixin super call handles other cases
300 dtype = pandas_dtype(dtype)
302 if dtype.kind == "m":
303 return astype_td64_unit_conversion(self._ndarray, dtype, copy=copy)
305 return dtl.DatetimeLikeArrayMixin.astype(self, dtype, copy=copy)
307 def __iter__(self):
308 if self.ndim > 1:
309 for i in range(len(self)):
310 yield self[i]
311 else:
312 # convert in chunks of 10k for efficiency
313 data = self._ndarray
314 length = len(self)
315 chunksize = 10000
316 chunks = (length // chunksize) + 1
317 for i in range(chunks):
318 start_i = i * chunksize
319 end_i = min((i + 1) * chunksize, length)
320 converted = ints_to_pytimedelta(data[start_i:end_i], box=True)
321 yield from converted
323 # ----------------------------------------------------------------
324 # Reductions
326 def sum(
327 self,
328 *,
329 axis: int | None = None,
330 dtype: NpDtype | None = None,
331 out=None,
332 keepdims: bool = False,
333 initial=None,
334 skipna: bool = True,
335 min_count: int = 0,
336 ):
337 nv.validate_sum(
338 (), {"dtype": dtype, "out": out, "keepdims": keepdims, "initial": initial}
339 )
341 result = nanops.nansum(
342 self._ndarray, axis=axis, skipna=skipna, min_count=min_count
343 )
344 return self._wrap_reduction_result(axis, result)
346 def std(
347 self,
348 *,
349 axis: int | None = None,
350 dtype: NpDtype | None = None,
351 out=None,
352 ddof: int = 1,
353 keepdims: bool = False,
354 skipna: bool = True,
355 ):
356 nv.validate_stat_ddof_func(
357 (), {"dtype": dtype, "out": out, "keepdims": keepdims}, fname="std"
358 )
360 result = nanops.nanstd(self._ndarray, axis=axis, skipna=skipna, ddof=ddof)
361 if axis is None or self.ndim == 1:
362 return self._box_func(result)
363 return self._from_backing_data(result)
365 # ----------------------------------------------------------------
366 # Rendering Methods
368 def _formatter(self, boxed: bool = False):
369 from pandas.io.formats.format import get_format_timedelta64
371 return get_format_timedelta64(self, box=True)
373 def _format_native_types(
374 self, *, na_rep="NaT", date_format=None, **kwargs
375 ) -> npt.NDArray[np.object_]:
376 from pandas.io.formats.format import get_format_timedelta64
378 # Relies on TimeDelta._repr_base
379 formatter = get_format_timedelta64(self._ndarray, na_rep)
380 # equiv: np.array([formatter(x) for x in self._ndarray])
381 # but independent of dimension
382 return np.frompyfunc(formatter, 1, 1)(self._ndarray)
384 # ----------------------------------------------------------------
385 # Arithmetic Methods
387 def _add_offset(self, other):
388 assert not isinstance(other, Tick)
389 raise TypeError(
390 f"cannot add the type {type(other).__name__} to a {type(self).__name__}"
391 )
393 @unpack_zerodim_and_defer("__mul__")
394 def __mul__(self, other) -> TimedeltaArray:
395 if is_scalar(other):
396 # numpy will accept float and int, raise TypeError for others
397 result = self._ndarray * other
398 freq = None
399 if self.freq is not None and not isna(other):
400 freq = self.freq * other
401 return type(self)._simple_new(result, dtype=result.dtype, freq=freq)
403 if not hasattr(other, "dtype"):
404 # list, tuple
405 other = np.array(other)
406 if len(other) != len(self) and not is_timedelta64_dtype(other.dtype):
407 # Exclude timedelta64 here so we correctly raise TypeError
408 # for that instead of ValueError
409 raise ValueError("Cannot multiply with unequal lengths")
411 if is_object_dtype(other.dtype):
412 # this multiplication will succeed only if all elements of other
413 # are int or float scalars, so we will end up with
414 # timedelta64[ns]-dtyped result
415 arr = self._ndarray
416 result = [arr[n] * other[n] for n in range(len(self))]
417 result = np.array(result)
418 return type(self)._simple_new(result, dtype=result.dtype)
420 # numpy will accept float or int dtype, raise TypeError for others
421 result = self._ndarray * other
422 return type(self)._simple_new(result, dtype=result.dtype)
424 __rmul__ = __mul__
426 @unpack_zerodim_and_defer("__truediv__")
427 def __truediv__(self, other):
428 # timedelta / X is well-defined for timedelta-like or numeric X
430 if isinstance(other, self._recognized_scalars):
431 other = Timedelta(other)
432 # mypy assumes that __new__ returns an instance of the class
433 # github.com/python/mypy/issues/1020
434 if cast("Timedelta | NaTType", other) is NaT:
435 # specifically timedelta64-NaT
436 result = np.empty(self.shape, dtype=np.float64)
437 result.fill(np.nan)
438 return result
440 # otherwise, dispatch to Timedelta implementation
441 return self._ndarray / other
443 elif lib.is_scalar(other):
444 # assume it is numeric
445 result = self._ndarray / other
446 freq = None
447 if self.freq is not None:
448 # Tick division is not implemented, so operate on Timedelta
449 freq = self.freq.delta / other
450 freq = to_offset(freq)
451 return type(self)._simple_new(result, dtype=result.dtype, freq=freq)
453 if not hasattr(other, "dtype"):
454 # e.g. list, tuple
455 other = np.array(other)
457 if len(other) != len(self):
458 raise ValueError("Cannot divide vectors with unequal lengths")
460 elif is_timedelta64_dtype(other.dtype):
461 # let numpy handle it
462 return self._ndarray / other
464 elif is_object_dtype(other.dtype):
465 # We operate on raveled arrays to avoid problems in inference
466 # on NaT
467 # TODO: tests with non-nano
468 srav = self.ravel()
469 orav = other.ravel()
470 result_list = [srav[n] / orav[n] for n in range(len(srav))]
471 result = np.array(result_list).reshape(self.shape)
473 # We need to do dtype inference in order to keep DataFrame ops
474 # behavior consistent with Series behavior
475 inferred = lib.infer_dtype(result, skipna=False)
476 if inferred == "timedelta":
477 flat = result.ravel()
478 result = type(self)._from_sequence(flat).reshape(result.shape)
479 elif inferred == "floating":
480 result = result.astype(float)
481 elif inferred == "datetime":
482 # GH#39750 this occurs when result is all-NaT, in which case
483 # we want to interpret these NaTs as td64.
484 # We construct an all-td64NaT result.
485 # error: Incompatible types in assignment (expression has type
486 # "TimedeltaArray", variable has type "ndarray[Any,
487 # dtype[floating[_64Bit]]]")
488 result = self * np.nan # type: ignore[assignment]
490 return result
492 else:
493 result = self._ndarray / other
494 return type(self)._simple_new(result, dtype=result.dtype)
496 @unpack_zerodim_and_defer("__rtruediv__")
497 def __rtruediv__(self, other):
498 # X / timedelta is defined only for timedelta-like X
499 if isinstance(other, self._recognized_scalars):
500 other = Timedelta(other)
501 # mypy assumes that __new__ returns an instance of the class
502 # github.com/python/mypy/issues/1020
503 if cast("Timedelta | NaTType", other) is NaT:
504 # specifically timedelta64-NaT
505 result = np.empty(self.shape, dtype=np.float64)
506 result.fill(np.nan)
507 return result
509 # otherwise, dispatch to Timedelta implementation
510 return other / self._ndarray
512 elif lib.is_scalar(other):
513 raise TypeError(
514 f"Cannot divide {type(other).__name__} by {type(self).__name__}"
515 )
517 if not hasattr(other, "dtype"):
518 # e.g. list, tuple
519 other = np.array(other)
521 if len(other) != len(self):
522 raise ValueError("Cannot divide vectors with unequal lengths")
524 elif is_timedelta64_dtype(other.dtype):
525 # let numpy handle it
526 return other / self._ndarray
528 elif is_object_dtype(other.dtype):
529 # Note: unlike in __truediv__, we do not _need_ to do type
530 # inference on the result. It does not raise, a numeric array
531 # is returned. GH#23829
532 result_list = [other[n] / self[n] for n in range(len(self))]
533 return np.array(result_list)
535 else:
536 raise TypeError(
537 f"Cannot divide {other.dtype} data by {type(self).__name__}"
538 )
540 @unpack_zerodim_and_defer("__floordiv__")
541 def __floordiv__(self, other):
543 if is_scalar(other):
544 if isinstance(other, self._recognized_scalars):
545 other = Timedelta(other)
546 # mypy assumes that __new__ returns an instance of the class
547 # github.com/python/mypy/issues/1020
548 if cast("Timedelta | NaTType", other) is NaT:
549 # treat this specifically as timedelta-NaT
550 result = np.empty(self.shape, dtype=np.float64)
551 result.fill(np.nan)
552 return result
554 # dispatch to Timedelta implementation
555 return other.__rfloordiv__(self._ndarray)
557 # at this point we should only have numeric scalars; anything
558 # else will raise
559 result = self._ndarray // other
560 freq = None
561 if self.freq is not None:
562 # Note: freq gets division, not floor-division
563 freq = self.freq / other
564 if freq.nanos == 0 and self.freq.nanos != 0:
565 # e.g. if self.freq is Nano(1) then dividing by 2
566 # rounds down to zero
567 freq = None
568 return type(self)(result, freq=freq)
570 if not hasattr(other, "dtype"):
571 # list, tuple
572 other = np.array(other)
573 if len(other) != len(self):
574 raise ValueError("Cannot divide with unequal lengths")
576 elif is_timedelta64_dtype(other.dtype):
577 other = type(self)(other)
579 # numpy timedelta64 does not natively support floordiv, so operate
580 # on the i8 values
581 result = self.asi8 // other.asi8
582 mask = self._isnan | other._isnan
583 if mask.any():
584 result = result.astype(np.float64)
585 np.putmask(result, mask, np.nan)
586 return result
588 elif is_object_dtype(other.dtype):
589 # error: Incompatible types in assignment (expression has type
590 # "List[Any]", variable has type "ndarray")
591 srav = self.ravel()
592 orav = other.ravel()
593 res_list = [srav[n] // orav[n] for n in range(len(srav))]
594 result_flat = np.asarray(res_list)
595 inferred = lib.infer_dtype(result_flat, skipna=False)
597 result = result_flat.reshape(self.shape)
599 if inferred == "timedelta":
600 result, _ = sequence_to_td64ns(result)
601 return type(self)(result)
602 if inferred == "datetime":
603 # GH#39750 occurs when result is all-NaT, which in this
604 # case should be interpreted as td64nat. This can only
605 # occur when self is all-td64nat
606 return self * np.nan
607 return result
609 elif is_integer_dtype(other.dtype) or is_float_dtype(other.dtype):
610 result = self._ndarray // other
611 return type(self)(result)
613 else:
614 dtype = getattr(other, "dtype", type(other).__name__)
615 raise TypeError(f"Cannot divide {dtype} by {type(self).__name__}")
617 @unpack_zerodim_and_defer("__rfloordiv__")
618 def __rfloordiv__(self, other):
620 if is_scalar(other):
621 if isinstance(other, self._recognized_scalars):
622 other = Timedelta(other)
623 # mypy assumes that __new__ returns an instance of the class
624 # github.com/python/mypy/issues/1020
625 if cast("Timedelta | NaTType", other) is NaT:
626 # treat this specifically as timedelta-NaT
627 result = np.empty(self.shape, dtype=np.float64)
628 result.fill(np.nan)
629 return result
631 # dispatch to Timedelta implementation
632 return other.__floordiv__(self._ndarray)
634 raise TypeError(
635 f"Cannot divide {type(other).__name__} by {type(self).__name__}"
636 )
638 if not hasattr(other, "dtype"):
639 # list, tuple
640 other = np.array(other)
642 if len(other) != len(self):
643 raise ValueError("Cannot divide with unequal lengths")
645 elif is_timedelta64_dtype(other.dtype):
646 other = type(self)(other)
647 # numpy timedelta64 does not natively support floordiv, so operate
648 # on the i8 values
649 result = other.asi8 // self.asi8
650 mask = self._isnan | other._isnan
651 if mask.any():
652 result = result.astype(np.float64)
653 np.putmask(result, mask, np.nan)
654 return result
656 elif is_object_dtype(other.dtype):
657 result_list = [other[n] // self[n] for n in range(len(self))]
658 result = np.array(result_list)
659 return result
661 else:
662 dtype = getattr(other, "dtype", type(other).__name__)
663 raise TypeError(f"Cannot divide {dtype} by {type(self).__name__}")
665 @unpack_zerodim_and_defer("__mod__")
666 def __mod__(self, other):
667 # Note: This is a naive implementation, can likely be optimized
668 if isinstance(other, self._recognized_scalars):
669 other = Timedelta(other)
670 return self - (self // other) * other
672 @unpack_zerodim_and_defer("__rmod__")
673 def __rmod__(self, other):
674 # Note: This is a naive implementation, can likely be optimized
675 if isinstance(other, self._recognized_scalars):
676 other = Timedelta(other)
677 return other - (other // self) * self
679 @unpack_zerodim_and_defer("__divmod__")
680 def __divmod__(self, other):
681 # Note: This is a naive implementation, can likely be optimized
682 if isinstance(other, self._recognized_scalars):
683 other = Timedelta(other)
685 res1 = self // other
686 res2 = self - res1 * other
687 return res1, res2
689 @unpack_zerodim_and_defer("__rdivmod__")
690 def __rdivmod__(self, other):
691 # Note: This is a naive implementation, can likely be optimized
692 if isinstance(other, self._recognized_scalars):
693 other = Timedelta(other)
695 res1 = other // self
696 res2 = other - res1 * self
697 return res1, res2
699 def __neg__(self) -> TimedeltaArray:
700 if self.freq is not None:
701 return type(self)(-self._ndarray, freq=-self.freq)
702 return type(self)(-self._ndarray)
704 def __pos__(self) -> TimedeltaArray:
705 return type(self)(self._ndarray.copy(), freq=self.freq)
707 def __abs__(self) -> TimedeltaArray:
708 # Note: freq is not preserved
709 return type(self)(np.abs(self._ndarray))
711 # ----------------------------------------------------------------
712 # Conversion Methods - Vectorized analogues of Timedelta methods
714 def total_seconds(self) -> npt.NDArray[np.float64]:
715 """
716 Return total duration of each element expressed in seconds.
718 This method is available directly on TimedeltaArray, TimedeltaIndex
719 and on Series containing timedelta values under the ``.dt`` namespace.
721 Returns
722 -------
723 seconds : [ndarray, Float64Index, Series]
724 When the calling object is a TimedeltaArray, the return type
725 is ndarray. When the calling object is a TimedeltaIndex,
726 the return type is a Float64Index. When the calling object
727 is a Series, the return type is Series of type `float64` whose
728 index is the same as the original.
730 See Also
731 --------
732 datetime.timedelta.total_seconds : Standard library version
733 of this method.
734 TimedeltaIndex.components : Return a DataFrame with components of
735 each Timedelta.
737 Examples
738 --------
739 **Series**
741 >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit='d'))
742 >>> s
743 0 0 days
744 1 1 days
745 2 2 days
746 3 3 days
747 4 4 days
748 dtype: timedelta64[ns]
750 >>> s.dt.total_seconds()
751 0 0.0
752 1 86400.0
753 2 172800.0
754 3 259200.0
755 4 345600.0
756 dtype: float64
758 **TimedeltaIndex**
760 >>> idx = pd.to_timedelta(np.arange(5), unit='d')
761 >>> idx
762 TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'],
763 dtype='timedelta64[ns]', freq=None)
765 >>> idx.total_seconds()
766 Float64Index([0.0, 86400.0, 172800.0, 259200.0, 345600.0],
767 dtype='float64')
768 """
769 pps = periods_per_second(self._reso)
770 return self._maybe_mask_results(self.asi8 / pps, fill_value=None)
772 def to_pytimedelta(self) -> npt.NDArray[np.object_]:
773 """
774 Return an ndarray of datetime.timedelta objects.
776 Returns
777 -------
778 timedeltas : ndarray[object]
779 """
780 return ints_to_pytimedelta(self._ndarray)
782 days = _field_accessor("days", "days", "Number of days for each element.")
783 seconds = _field_accessor(
784 "seconds",
785 "seconds",
786 "Number of seconds (>= 0 and less than 1 day) for each element.",
787 )
788 microseconds = _field_accessor(
789 "microseconds",
790 "microseconds",
791 "Number of microseconds (>= 0 and less than 1 second) for each element.",
792 )
793 nanoseconds = _field_accessor(
794 "nanoseconds",
795 "nanoseconds",
796 "Number of nanoseconds (>= 0 and less than 1 microsecond) for each element.",
797 )
799 @property
800 def components(self) -> DataFrame:
801 """
802 Return a DataFrame of the individual resolution components of the Timedeltas.
804 The components (days, hours, minutes seconds, milliseconds, microseconds,
805 nanoseconds) are returned as columns in a DataFrame.
807 Returns
808 -------
809 DataFrame
810 """
811 from pandas import DataFrame
813 columns = [
814 "days",
815 "hours",
816 "minutes",
817 "seconds",
818 "milliseconds",
819 "microseconds",
820 "nanoseconds",
821 ]
822 hasnans = self._hasna
823 if hasnans:
825 def f(x):
826 if isna(x):
827 return [np.nan] * len(columns)
828 return x.components
830 else:
832 def f(x):
833 return x.components
835 result = DataFrame([f(x) for x in self], columns=columns)
836 if not hasnans:
837 result = result.astype("int64")
838 return result
841# ---------------------------------------------------------------------
842# Constructor Helpers
845def sequence_to_td64ns(
846 data, copy: bool = False, unit=None, errors="raise"
847) -> tuple[np.ndarray, Tick | None]:
848 """
849 Parameters
850 ----------
851 data : list-like
852 copy : bool, default False
853 unit : str, optional
854 The timedelta unit to treat integers as multiples of. For numeric
855 data this defaults to ``'ns'``.
856 Must be un-specified if the data contains a str and ``errors=="raise"``.
857 errors : {"raise", "coerce", "ignore"}, default "raise"
858 How to handle elements that cannot be converted to timedelta64[ns].
859 See ``pandas.to_timedelta`` for details.
861 Returns
862 -------
863 converted : numpy.ndarray
864 The sequence converted to a numpy array with dtype ``timedelta64[ns]``.
865 inferred_freq : Tick or None
866 The inferred frequency of the sequence.
868 Raises
869 ------
870 ValueError : Data cannot be converted to timedelta64[ns].
872 Notes
873 -----
874 Unlike `pandas.to_timedelta`, if setting ``errors=ignore`` will not cause
875 errors to be ignored; they are caught and subsequently ignored at a
876 higher level.
877 """
878 assert unit not in ["Y", "y", "M"] # caller is responsible for checking
880 inferred_freq = None
881 if unit is not None:
882 unit = parse_timedelta_unit(unit)
884 data, copy = dtl.ensure_arraylike_for_datetimelike(
885 data, copy, cls_name="TimedeltaArray"
886 )
888 if isinstance(data, TimedeltaArray):
889 inferred_freq = data.freq
891 # Convert whatever we have into timedelta64[ns] dtype
892 if is_object_dtype(data.dtype) or is_string_dtype(data.dtype):
893 # no need to make a copy, need to convert if string-dtyped
894 data = _objects_to_td64ns(data, unit=unit, errors=errors)
895 copy = False
897 elif is_integer_dtype(data.dtype):
898 # treat as multiples of the given unit
899 data, copy_made = ints_to_td64ns(data, unit=unit)
900 copy = copy and not copy_made
902 elif is_float_dtype(data.dtype):
903 # cast the unit, multiply base/frac separately
904 # to avoid precision issues from float -> int
905 mask = np.isnan(data)
906 # The next few lines are effectively a vectorized 'cast_from_unit'
907 m, p = precision_from_unit(unit or "ns")
908 base = data.astype(np.int64)
909 frac = data - base
910 if p:
911 frac = np.round(frac, p)
912 data = (base * m + (frac * m).astype(np.int64)).view("timedelta64[ns]")
913 data[mask] = iNaT
914 copy = False
916 elif is_timedelta64_dtype(data.dtype):
917 if data.dtype != TD64NS_DTYPE:
918 # non-nano unit
919 data = astype_overflowsafe(data, dtype=TD64NS_DTYPE)
920 copy = False
922 else:
923 # This includes datetime64-dtype, see GH#23539, GH#29794
924 raise TypeError(f"dtype {data.dtype} cannot be converted to timedelta64[ns]")
926 data = np.array(data, copy=copy)
928 assert data.dtype == "m8[ns]", data
929 return data, inferred_freq
932def ints_to_td64ns(data, unit="ns"):
933 """
934 Convert an ndarray with integer-dtype to timedelta64[ns] dtype, treating
935 the integers as multiples of the given timedelta unit.
937 Parameters
938 ----------
939 data : numpy.ndarray with integer-dtype
940 unit : str, default "ns"
941 The timedelta unit to treat integers as multiples of.
943 Returns
944 -------
945 numpy.ndarray : timedelta64[ns] array converted from data
946 bool : whether a copy was made
947 """
948 copy_made = False
949 unit = unit if unit is not None else "ns"
951 if data.dtype != np.int64:
952 # converting to int64 makes a copy, so we can avoid
953 # re-copying later
954 data = data.astype(np.int64)
955 copy_made = True
957 if unit != "ns":
958 dtype_str = f"timedelta64[{unit}]"
959 data = data.view(dtype_str)
961 data = astype_overflowsafe(data, dtype=TD64NS_DTYPE)
963 # the astype conversion makes a copy, so we can avoid re-copying later
964 copy_made = True
966 else:
967 data = data.view("timedelta64[ns]")
969 return data, copy_made
972def _objects_to_td64ns(data, unit=None, errors="raise"):
973 """
974 Convert a object-dtyped or string-dtyped array into an
975 timedelta64[ns]-dtyped array.
977 Parameters
978 ----------
979 data : ndarray or Index
980 unit : str, default "ns"
981 The timedelta unit to treat integers as multiples of.
982 Must not be specified if the data contains a str.
983 errors : {"raise", "coerce", "ignore"}, default "raise"
984 How to handle elements that cannot be converted to timedelta64[ns].
985 See ``pandas.to_timedelta`` for details.
987 Returns
988 -------
989 numpy.ndarray : timedelta64[ns] array converted from data
991 Raises
992 ------
993 ValueError : Data cannot be converted to timedelta64[ns].
995 Notes
996 -----
997 Unlike `pandas.to_timedelta`, if setting `errors=ignore` will not cause
998 errors to be ignored; they are caught and subsequently ignored at a
999 higher level.
1000 """
1001 # coerce Index to np.ndarray, converting string-dtype if necessary
1002 values = np.array(data, dtype=np.object_, copy=False)
1004 result = array_to_timedelta64(values, unit=unit, errors=errors)
1005 return result.view("timedelta64[ns]")
1008def _validate_td64_dtype(dtype) -> DtypeObj:
1009 dtype = pandas_dtype(dtype)
1010 if is_dtype_equal(dtype, np.dtype("timedelta64")):
1011 # no precision disallowed GH#24806
1012 msg = (
1013 "Passing in 'timedelta' dtype with no precision is not allowed. "
1014 "Please pass in 'timedelta64[ns]' instead."
1015 )
1016 raise ValueError(msg)
1018 if not is_dtype_equal(dtype, TD64NS_DTYPE):
1019 raise ValueError(f"dtype {dtype} cannot be converted to timedelta64[ns]")
1021 return dtype