Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/indexes/timedeltas.py: 28%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

226 statements  

1"""implement the TimedeltaIndex""" 

2 

3from __future__ import annotations 

4 

5import operator 

6from typing import ( 

7 TYPE_CHECKING, 

8 Self, 

9 cast, 

10) 

11import warnings 

12 

13from pandas._libs import ( 

14 index as libindex, 

15 lib, 

16) 

17from pandas._libs.tslibs import ( 

18 Day, 

19 Resolution, 

20 Tick, 

21 Timedelta, 

22 Timestamp, 

23 timezones, 

24 to_offset, 

25) 

26from pandas._libs.tslibs.dtypes import abbrev_to_npy_unit 

27from pandas._libs.tslibs.timedeltas import parse_timedelta_string_reso 

28from pandas.util._decorators import set_module 

29 

30from pandas.core.dtypes.common import ( 

31 is_scalar, 

32 pandas_dtype, 

33) 

34from pandas.core.dtypes.dtypes import ArrowDtype 

35from pandas.core.dtypes.generic import ABCSeries 

36from pandas.core.dtypes.missing import isna 

37 

38from pandas.core.arrays.timedeltas import TimedeltaArray 

39import pandas.core.common as com 

40from pandas.core.indexes.base import ( 

41 Index, 

42 maybe_extract_name, 

43) 

44from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin 

45from pandas.core.roperator import ( 

46 rmul, 

47 rsub, 

48) 

49 

50if TYPE_CHECKING: 

51 from collections.abc import Callable 

52 from typing import Any 

53 

54 import numpy as np 

55 

56 from pandas._libs import NaTType 

57 from pandas._typing import ( 

58 AxisInt, 

59 DtypeObj, 

60 NpDtype, 

61 TimeAmbiguous, 

62 TimeNonexistent, 

63 TimeUnit, 

64 npt, 

65 ) 

66 

67 from pandas import DataFrame 

68 

69 

70def _new_TimedeltaIndex(cls, d): 

71 """ 

72 This is called upon unpickling, rather than the default which doesn't 

73 have arguments and breaks __new__ 

74 """ 

75 if "data" in d and not isinstance(d["data"], TimedeltaIndex): 

76 data = d.pop("data") 

77 if isinstance(data, TimedeltaArray): 

78 tdarr = data 

79 else: 

80 tdarr = TimedeltaArray._simple_new(data, dtype=data.dtype) 

81 # Legacy pickles stored freq on the TimedeltaArray; current pickles 

82 # include it in ``d``. Migrate either up onto the Index. 

83 legacy_freq = vars(tdarr).pop("_freq", None) 

84 freq = d.pop("freq", legacy_freq) 

85 result = cls._simple_new(tdarr, **d) 

86 result._freq = freq 

87 else: 

88 with warnings.catch_warnings(): 

89 warnings.simplefilter("ignore") 

90 result = cls.__new__(cls, **d) 

91 return result 

92 

93 

94@set_module("pandas") 

95class TimedeltaIndex(DatetimeTimedeltaMixin): 

96 """ 

97 Immutable Index of timedelta64 data. 

98 

99 Represented internally as int64, and scalars returned Timedelta objects. 

100 

101 Parameters 

102 ---------- 

103 data : array-like (1-dimensional), optional 

104 Optional timedelta-like data to construct index with. 

105 freq : str or pandas offset object, optional 

106 One of pandas date offset strings or corresponding objects. The string 

107 ``'infer'`` can be passed in order to set the frequency of the index as 

108 the inferred frequency upon creation. 

109 dtype : numpy.dtype or str, default None 

110 Valid ``numpy`` dtypes are ``timedelta64[ns]``, ``timedelta64[us]``, 

111 ``timedelta64[ms]``, and ``timedelta64[s]``. 

112 copy : bool, default None 

113 Whether to copy input data, only relevant for array, Series, and Index 

114 inputs (for other input, e.g. a list, a new array is created anyway). 

115 Defaults to True for array input and False for Index/Series. 

116 Set to False to avoid copying array input at your own risk (if you 

117 know the input data won't be modified elsewhere). 

118 Set to True to force copying Series/Index input up front. 

119 name : object 

120 Name to be stored in the index. 

121 

122 Attributes 

123 ---------- 

124 days 

125 seconds 

126 microseconds 

127 nanoseconds 

128 components 

129 inferred_freq 

130 

131 Methods 

132 ------- 

133 to_pytimedelta 

134 to_series 

135 round 

136 floor 

137 ceil 

138 to_frame 

139 mean 

140 

141 See Also 

142 -------- 

143 Index : The base pandas Index type. 

144 Timedelta : Represents a duration between two dates or times. 

145 DatetimeIndex : Index of datetime64 data. 

146 PeriodIndex : Index of Period data. 

147 timedelta_range : Create a fixed-frequency TimedeltaIndex. 

148 

149 Notes 

150 ----- 

151 To learn more about the frequency strings, please see 

152 :ref:`this link<timeseries.offset_aliases>`. 

153 

154 Examples 

155 -------- 

156 >>> pd.TimedeltaIndex(["0 days", "1 days", "2 days", "3 days", "4 days"]) 

157 TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], 

158 dtype='timedelta64[us]', freq=None) 

159 

160 We can also let pandas infer the frequency when possible. 

161 

162 >>> pd.TimedeltaIndex(np.arange(5) * 24 * 3600 * 1e9, freq="infer") 

163 TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], 

164 dtype='timedelta64[ns]', freq='D') 

165 """ 

166 

167 _typ = "timedeltaindex" 

168 

169 _data_cls = TimedeltaArray 

170 

171 @property 

172 def _engine_type(self) -> type[libindex.TimedeltaEngine]: 

173 return libindex.TimedeltaEngine 

174 

175 _data: TimedeltaArray 

176 

177 # Use base class method instead of DatetimeTimedeltaMixin._get_string_slice 

178 _get_string_slice = Index._get_string_slice 

179 

180 # ------------------------------------------------------------------- 

181 # Methods that dispatch to TimedeltaArray and wrap the result 

182 

183 def _wrap_field_result(self, result: np.ndarray) -> Index: 

184 """Wrap an ndarray result computed from ``self._data`` in an Index.""" 

185 return Index(result, name=self.name, dtype=result.dtype, copy=False) 

186 

187 def _wrap_td_result(self, result: TimedeltaArray) -> Self: 

188 """Wrap a TimedeltaArray result computed from ``self._data``.""" 

189 return type(self)._simple_new(result, name=self.name) 

190 

191 def total_seconds(self) -> Index: 

192 """ 

193 Return total duration of each element expressed in seconds. 

194 

195 This method is available directly on TimedeltaArray, TimedeltaIndex 

196 and on Series containing timedelta values under the ``.dt`` namespace. 

197 

198 Returns 

199 ------- 

200 Index 

201 An Index with a float64 dtype. 

202 

203 See Also 

204 -------- 

205 datetime.timedelta.total_seconds : Standard library version 

206 of this method. 

207 TimedeltaIndex.components : Return a DataFrame with components of 

208 each Timedelta. 

209 

210 Examples 

211 -------- 

212 >>> idx = pd.to_timedelta(np.arange(5), unit="D") 

213 >>> idx 

214 TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], 

215 dtype='timedelta64[us]', freq=None) 

216 

217 >>> idx.total_seconds() 

218 Index([0.0, 86400.0, 172800.0, 259200.0, 345600.0], dtype='float64') 

219 """ 

220 return self._wrap_field_result(self._data.total_seconds()) 

221 

222 # error: Signature of "round" incompatible with supertype "Index" 

223 # TimedeltaIndex.round rounds to a freq like Index.floor/ceil, unlike 

224 # Index.round which rounds numeric values to a number of decimals. 

225 def round( # type: ignore[override] 

226 self, 

227 freq, 

228 ambiguous: TimeAmbiguous = "raise", 

229 nonexistent: TimeNonexistent = "raise", 

230 ) -> Self: 

231 """ 

232 Perform round operation on the data to the specified `freq`. 

233 

234 This method rounds each timedelta value in the Series/Index to the 

235 nearest specified frequency using standard rounding rules (round half 

236 to even). 

237 

238 Parameters 

239 ---------- 

240 freq : str or Offset 

241 The frequency level to round the index to. Must be a fixed 

242 frequency like 's' (second) not 'ME' (month end). See 

243 :ref:`frequency aliases <timeseries.offset_aliases>` for 

244 a list of possible `freq` values. 

245 ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise' 

246 Accepted for compatibility with :meth:`DatetimeIndex.round`; 

247 has no effect since TimedeltaIndex values are never tz-aware. 

248 nonexistent : 'shift_forward', 'shift_backward', 'NaT', timedelta, \ 

249 default 'raise' 

250 Accepted for compatibility with :meth:`DatetimeIndex.round`; 

251 has no effect since TimedeltaIndex values are never tz-aware. 

252 

253 Returns 

254 ------- 

255 TimedeltaIndex 

256 Values rounded to the given `freq`. 

257 

258 Raises 

259 ------ 

260 ValueError if the `freq` cannot be converted. 

261 

262 See Also 

263 -------- 

264 TimedeltaIndex.floor : Perform floor operation on the data to the 

265 specified `freq`. 

266 TimedeltaIndex.ceil : Perform ceil operation on the data to the 

267 specified `freq`. 

268 

269 Examples 

270 -------- 

271 >>> tdelta_idx = pd.timedelta_range("1 day", periods=3, freq="6h20min") 

272 >>> tdelta_idx 

273 TimedeltaIndex(['1 days 00:00:00', '1 days 06:20:00', '1 days 12:40:00'], 

274 dtype='timedelta64[us]', freq='380min') 

275 >>> tdelta_idx.round("h") 

276 TimedeltaIndex(['1 days 00:00:00', '1 days 06:00:00', '1 days 13:00:00'], 

277 dtype='timedelta64[us]', freq=None) 

278 """ 

279 return self._wrap_td_result(self._data.round(freq, ambiguous, nonexistent)) 

280 

281 def floor( 

282 self, 

283 freq, 

284 ambiguous: TimeAmbiguous = "raise", 

285 nonexistent: TimeNonexistent = "raise", 

286 ) -> Self: 

287 """ 

288 Perform floor operation on the data to the specified `freq`. 

289 

290 This method rounds each timedelta value in the Series/Index down to 

291 the specified frequency (i.e., towards negative infinity). 

292 

293 Parameters 

294 ---------- 

295 freq : str or Offset 

296 The frequency level to floor the index to. Must be a fixed 

297 frequency like 's' (second) not 'ME' (month end). See 

298 :ref:`frequency aliases <timeseries.offset_aliases>` for 

299 a list of possible `freq` values. 

300 ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise' 

301 Accepted for compatibility with :meth:`DatetimeIndex.floor`; 

302 has no effect since TimedeltaIndex values are never tz-aware. 

303 nonexistent : 'shift_forward', 'shift_backward', 'NaT', timedelta, \ 

304 default 'raise' 

305 Accepted for compatibility with :meth:`DatetimeIndex.floor`; 

306 has no effect since TimedeltaIndex values are never tz-aware. 

307 

308 Returns 

309 ------- 

310 TimedeltaIndex 

311 Values rounded down to the given `freq`. 

312 

313 Raises 

314 ------ 

315 ValueError if the `freq` cannot be converted. 

316 

317 See Also 

318 -------- 

319 TimedeltaIndex.round : Perform round operation on the data to the 

320 specified `freq`. 

321 TimedeltaIndex.ceil : Perform ceil operation on the data to the 

322 specified `freq`. 

323 

324 Examples 

325 -------- 

326 >>> tdelta_idx = pd.timedelta_range("1 day", periods=3, freq="6h20min") 

327 >>> tdelta_idx 

328 TimedeltaIndex(['1 days 00:00:00', '1 days 06:20:00', '1 days 12:40:00'], 

329 dtype='timedelta64[us]', freq='380min') 

330 >>> tdelta_idx.floor("h") 

331 TimedeltaIndex(['1 days 00:00:00', '1 days 06:00:00', '1 days 12:00:00'], 

332 dtype='timedelta64[us]', freq=None) 

333 """ 

334 return self._wrap_td_result(self._data.floor(freq, ambiguous, nonexistent)) 

335 

336 def ceil( 

337 self, 

338 freq, 

339 ambiguous: TimeAmbiguous = "raise", 

340 nonexistent: TimeNonexistent = "raise", 

341 ) -> Self: 

342 """ 

343 Perform ceil operation on the data to the specified `freq`. 

344 

345 This method rounds each timedelta value in the Series/Index up to 

346 the specified frequency (i.e., towards positive infinity). 

347 

348 Parameters 

349 ---------- 

350 freq : str or Offset 

351 The frequency level to ceil the index to. Must be a fixed 

352 frequency like 's' (second) not 'ME' (month end). See 

353 :ref:`frequency aliases <timeseries.offset_aliases>` for 

354 a list of possible `freq` values. 

355 ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise' 

356 Accepted for compatibility with :meth:`DatetimeIndex.ceil`; 

357 has no effect since TimedeltaIndex values are never tz-aware. 

358 nonexistent : 'shift_forward', 'shift_backward', 'NaT', timedelta, \ 

359 default 'raise' 

360 Accepted for compatibility with :meth:`DatetimeIndex.ceil`; 

361 has no effect since TimedeltaIndex values are never tz-aware. 

362 

363 Returns 

364 ------- 

365 TimedeltaIndex 

366 Values rounded up to the given `freq`. 

367 

368 Raises 

369 ------ 

370 ValueError if the `freq` cannot be converted. 

371 

372 See Also 

373 -------- 

374 TimedeltaIndex.round : Perform round operation on the data to the 

375 specified `freq`. 

376 TimedeltaIndex.floor : Perform floor operation on the data to the 

377 specified `freq`. 

378 

379 Examples 

380 -------- 

381 >>> tdelta_idx = pd.timedelta_range("1 day", periods=3, freq="6h20min") 

382 >>> tdelta_idx 

383 TimedeltaIndex(['1 days 00:00:00', '1 days 06:20:00', '1 days 12:40:00'], 

384 dtype='timedelta64[us]', freq='380min') 

385 >>> tdelta_idx.ceil("h") 

386 TimedeltaIndex(['1 days 00:00:00', '1 days 07:00:00', '1 days 13:00:00'], 

387 dtype='timedelta64[us]', freq=None) 

388 """ 

389 return self._wrap_td_result(self._data.ceil(freq, ambiguous, nonexistent)) 

390 

391 @property 

392 def days(self) -> Index: 

393 """ 

394 Number of days for each element. 

395 

396 This attribute returns the number of whole days in each timedelta value. 

397 It represents the days component of the duration, not the total duration 

398 expressed in days. 

399 

400 See Also 

401 -------- 

402 Series.dt.seconds : Return number of seconds for each element. 

403 Series.dt.microseconds : Return number of microseconds for each element. 

404 Series.dt.nanoseconds : Return number of nanoseconds for each element. 

405 

406 Examples 

407 -------- 

408 >>> tdelta_idx = pd.to_timedelta(["0 days", "10 days", "20 days"]) 

409 >>> tdelta_idx 

410 TimedeltaIndex(['0 days', '10 days', '20 days'], 

411 dtype='timedelta64[us]', freq=None) 

412 >>> tdelta_idx.days 

413 Index([0, 10, 20], dtype='int64') 

414 """ 

415 return self._wrap_field_result(self._data.days) 

416 

417 @property 

418 def seconds(self) -> Index: 

419 """ 

420 Number of seconds (>= 0 and less than 1 day) for each element. 

421 

422 This attribute returns the seconds component of each timedelta value, 

423 which is the number of seconds remaining after subtracting whole days. 

424 Values range from 0 to 86399. 

425 

426 See Also 

427 -------- 

428 Series.dt.seconds : Return number of seconds for each element. 

429 Series.dt.nanoseconds : Return number of nanoseconds for each element. 

430 

431 Examples 

432 -------- 

433 >>> tdelta_idx = pd.to_timedelta([1, 2, 3], unit="s") 

434 >>> tdelta_idx 

435 TimedeltaIndex(['0 days 00:00:01', '0 days 00:00:02', '0 days 00:00:03'], 

436 dtype='timedelta64[us]', freq=None) 

437 >>> tdelta_idx.seconds 

438 Index([1, 2, 3], dtype='int32') 

439 """ 

440 return self._wrap_field_result(self._data.seconds) 

441 

442 @property 

443 def microseconds(self) -> Index: 

444 """ 

445 Number of microseconds (>= 0 and less than 1 second) for each element. 

446 

447 This attribute returns the microseconds component of each timedelta value, 

448 which is the number of microseconds remaining after subtracting whole 

449 seconds. Values range from 0 to 999999. 

450 

451 See Also 

452 -------- 

453 Timedelta.microseconds : Number of microseconds (>= 0 and less than 

454 1 second). 

455 

456 Examples 

457 -------- 

458 >>> tdelta_idx = pd.to_timedelta([1, 2, 3], unit="us") 

459 >>> tdelta_idx 

460 TimedeltaIndex(['0 days 00:00:00.000001', '0 days 00:00:00.000002', 

461 '0 days 00:00:00.000003'], 

462 dtype='timedelta64[us]', freq=None) 

463 >>> tdelta_idx.microseconds 

464 Index([1, 2, 3], dtype='int32') 

465 """ 

466 return self._wrap_field_result(self._data.microseconds) 

467 

468 @property 

469 def nanoseconds(self) -> Index: 

470 """ 

471 Number of nanoseconds (>= 0 and less than 1 microsecond) for each element. 

472 

473 This attribute returns the nanoseconds component of each timedelta value, 

474 which is the number of nanoseconds remaining after subtracting whole 

475 microseconds. Values range from 0 to 999. 

476 

477 See Also 

478 -------- 

479 Series.dt.seconds : Return number of seconds for each element. 

480 Series.dt.microseconds : Return number of microseconds for each element. 

481 

482 Examples 

483 -------- 

484 >>> tdelta_idx = pd.to_timedelta([1, 2, 3], unit="ns") 

485 >>> tdelta_idx 

486 TimedeltaIndex(['0 days 00:00:00.000000001', '0 days 00:00:00.000000002', 

487 '0 days 00:00:00.000000003'], 

488 dtype='timedelta64[ns]', freq=None) 

489 >>> tdelta_idx.nanoseconds 

490 Index([1, 2, 3], dtype='int32') 

491 """ 

492 return self._wrap_field_result(self._data.nanoseconds) 

493 

494 @property 

495 def components(self) -> DataFrame: 

496 """ 

497 Return a DataFrame of the individual resolution components of the Timedeltas. 

498 

499 The components (days, hours, minutes seconds, milliseconds, microseconds, 

500 nanoseconds) are returned as columns in a DataFrame. 

501 

502 Returns 

503 ------- 

504 DataFrame 

505 

506 See Also 

507 -------- 

508 TimedeltaIndex.total_seconds : Return total duration expressed in seconds. 

509 Timedelta.components : Return a components namedtuple-like of a single 

510 timedelta. 

511 

512 Examples 

513 -------- 

514 >>> tdelta_idx = pd.to_timedelta(["1 day 3 min 2 us 42 ns"]) 

515 >>> tdelta_idx 

516 TimedeltaIndex(['1 days 00:03:00.000002042'], 

517 dtype='timedelta64[ns]', freq=None) 

518 >>> tdelta_idx.components 

519 days hours minutes seconds milliseconds microseconds nanoseconds 

520 0 1 0 3 0 0 2 42 

521 """ 

522 return self._data.components 

523 

524 def to_pytimedelta(self) -> npt.NDArray[np.object_]: 

525 """ 

526 Return an ndarray of datetime.timedelta objects. 

527 

528 Each element of the :class:`TimedeltaIndex` is converted to the 

529 corresponding native Python :class:`datetime.timedelta` object. 

530 

531 Returns 

532 ------- 

533 numpy.ndarray 

534 Object-dtype array of :class:`datetime.timedelta`. 

535 

536 See Also 

537 -------- 

538 to_timedelta : Convert argument to timedelta format. 

539 Timedelta : Represents a duration between two dates or times. 

540 DatetimeIndex: Index of datetime64 data. 

541 Timedelta.components : Return a components namedtuple-like 

542 of a single timedelta. 

543 

544 Examples 

545 -------- 

546 >>> tdelta_idx = pd.to_timedelta([1, 2, 3], unit="D") 

547 >>> tdelta_idx 

548 TimedeltaIndex(['1 days', '2 days', '3 days'], 

549 dtype='timedelta64[us]', freq=None) 

550 >>> tdelta_idx.to_pytimedelta() 

551 array([datetime.timedelta(days=1), datetime.timedelta(days=2), 

552 datetime.timedelta(days=3)], dtype=object) 

553 """ 

554 return self._data.to_pytimedelta() 

555 

556 def sum( 

557 self, 

558 *, 

559 axis: AxisInt | None = None, 

560 dtype: NpDtype | None = None, 

561 out=None, 

562 keepdims: bool = False, 

563 initial=None, 

564 skipna: bool = True, 

565 min_count: int = 0, 

566 ) -> Timedelta | NaTType: 

567 """ 

568 Return the sum of the values over the requested axis. 

569 

570 Parameters 

571 ---------- 

572 axis : int, optional 

573 Axis for the function to be applied on. 

574 dtype, out, keepdims, initial 

575 Not implemented; kept for compatibility with :func:`numpy.sum`, 

576 which calls this method when ``numpy.sum(tdi)`` is used. Must be 

577 left at their default values. 

578 skipna : bool, default True 

579 Whether to ignore any NaT elements. 

580 min_count : int, default 0 

581 The required number of valid values to perform the operation. If fewer 

582 than ``min_count`` non-NaT values are present the result is NaT. 

583 

584 Returns 

585 ------- 

586 Timedelta 

587 The sum, or NaT if it cannot be computed. 

588 

589 See Also 

590 -------- 

591 numpy.ndarray.sum : Returns the sum of array elements along a given axis. 

592 Series.sum : Return the sum of the values in a Series. 

593 

594 Examples 

595 -------- 

596 >>> tdelta_idx = pd.to_timedelta([1, 2, 3], unit="D") 

597 >>> tdelta_idx 

598 TimedeltaIndex(['1 days', '2 days', '3 days'], 

599 dtype='timedelta64[us]', freq=None) 

600 >>> tdelta_idx.sum() 

601 Timedelta('6 days 00:00:00') 

602 """ 

603 return self._data.sum( 

604 axis=axis, 

605 dtype=dtype, 

606 out=out, 

607 keepdims=keepdims, 

608 initial=initial, 

609 skipna=skipna, 

610 min_count=min_count, 

611 ) 

612 

613 def std( 

614 self, 

615 *, 

616 axis: AxisInt | None = None, 

617 dtype: NpDtype | None = None, 

618 out=None, 

619 ddof: int = 1, 

620 keepdims: bool = False, 

621 skipna: bool = True, 

622 ) -> Timedelta | NaTType: 

623 """ 

624 Return the standard deviation of the values over the requested axis. 

625 

626 Parameters 

627 ---------- 

628 axis : int, optional 

629 Axis for the function to be applied on. 

630 dtype, out, keepdims 

631 Not implemented; kept for compatibility with :func:`numpy.std`, 

632 which calls this method when ``numpy.std(tdi)`` is used. Must be 

633 left at their default values. 

634 ddof : int, default 1 

635 Delta degrees of freedom. The divisor used in calculations is 

636 ``N - ddof``, where ``N`` represents the number of elements. 

637 skipna : bool, default True 

638 Whether to ignore any NaT elements. 

639 

640 Returns 

641 ------- 

642 Timedelta 

643 The standard deviation, or NaT if it cannot be computed. 

644 

645 See Also 

646 -------- 

647 numpy.ndarray.std : Returns the standard deviation of array elements 

648 along a given axis. 

649 Series.std : Return sample standard deviation over requested axis. 

650 

651 Examples 

652 -------- 

653 >>> tdelta_idx = pd.to_timedelta([1, 2, 3], unit="D") 

654 >>> tdelta_idx 

655 TimedeltaIndex(['1 days', '2 days', '3 days'], 

656 dtype='timedelta64[us]', freq=None) 

657 >>> tdelta_idx.std() 

658 Timedelta('1 days 00:00:00') 

659 """ 

660 return self._data.std( 

661 axis=axis, dtype=dtype, out=out, ddof=ddof, keepdims=keepdims, skipna=skipna 

662 ) 

663 

664 def median( 

665 self, 

666 *, 

667 axis: AxisInt | None = None, 

668 skipna: bool = True, 

669 **kwargs, 

670 ) -> Timedelta | NaTType: 

671 """ 

672 Return the median of the values over the requested axis. 

673 

674 Parameters 

675 ---------- 

676 axis : int, optional 

677 Axis for the function to be applied on. 

678 skipna : bool, default True 

679 Whether to ignore any NaT elements. 

680 **kwargs 

681 Additional keywords have no effect but might be accepted for 

682 compatibility with NumPy. 

683 

684 Returns 

685 ------- 

686 Timedelta 

687 The median, or NaT if it cannot be computed. 

688 

689 See Also 

690 -------- 

691 numpy.median : Compute the median along the specified axis. 

692 Series.median : Return the median of the values in a Series. 

693 

694 Examples 

695 -------- 

696 >>> tdelta_idx = pd.to_timedelta([1, 2, 3], unit="D") 

697 >>> tdelta_idx 

698 TimedeltaIndex(['1 days', '2 days', '3 days'], 

699 dtype='timedelta64[us]', freq=None) 

700 >>> tdelta_idx.median() 

701 Timedelta('2 days 00:00:00') 

702 """ 

703 return self._data.median(axis=axis, skipna=skipna, **kwargs) 

704 

705 # ------------------------------------------------------------------- 

706 # Constructors 

707 

708 def __new__( 

709 cls, 

710 data=None, 

711 freq=lib.no_default, 

712 dtype=None, 

713 copy: bool | None = None, 

714 name=None, 

715 ): 

716 name = maybe_extract_name(name, data, cls) 

717 

718 # GH#63388 

719 data, copy = cls._maybe_copy_array_input(data, copy, dtype) 

720 

721 if is_scalar(data): 

722 cls._raise_scalar_data_error(data) 

723 

724 if dtype is not None: 

725 dtype = pandas_dtype(dtype) 

726 

727 if ( 

728 isinstance(data, TimedeltaArray) 

729 and freq is lib.no_default 

730 and (dtype is None or dtype == data.dtype) 

731 ): 

732 if copy: 

733 data = data.copy() 

734 return cls._simple_new(data, name=name) 

735 

736 if ( 

737 isinstance(data, TimedeltaIndex) 

738 and freq is lib.no_default 

739 and name is None 

740 and (dtype is None or dtype == data.dtype) 

741 ): 

742 if copy: 

743 return data.copy() 

744 else: 

745 return data._view() 

746 

747 # - Cases checked above all return/raise before reaching here - # 

748 

749 inferred_freq = data.freq if isinstance(data, TimedeltaIndex) else None 

750 

751 tdarr = TimedeltaArray._from_sequence(data, dtype=dtype, copy=copy) 

752 refs = None 

753 if not copy and isinstance(data, (ABCSeries, Index)): 

754 refs = data._references 

755 

756 result = cls._simple_new(tdarr, name=name, refs=refs) 

757 result._pin_freq(freq, inferred_freq) 

758 return result 

759 

760 def __reduce__(self): 

761 d = {"data": self._data, "name": self.name, "freq": self._freq} 

762 return _new_TimedeltaIndex, (type(self), d), None 

763 

764 # ------------------------------------------------------------------- 

765 

766 def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: 

767 """ 

768 Can we compare values of the given dtype to our own? 

769 """ 

770 if isinstance(dtype, ArrowDtype): 

771 return dtype.kind == "m" 

772 return lib.is_np_dtype(dtype, "m") # aka self._data._is_recognized_dtype 

773 

774 # ------------------------------------------------------------------- 

775 # Arithmetic Methods 

776 

777 def __neg__(self) -> TimedeltaIndex: 

778 result = self._data.__neg__() 

779 idx = type(self)._simple_new(result, name=self.name) 

780 if self.freq is not None: 

781 idx._freq = -self.freq 

782 return idx 

783 

784 def __pos__(self) -> TimedeltaIndex: 

785 result = self._data.__pos__() 

786 idx = type(self)._simple_new(result, name=self.name) 

787 if self.freq is not None: 

788 idx._freq = self.freq 

789 return idx 

790 

791 def _arith_method(self, other: object, op: Callable[..., Any]) -> Index: 

792 result = super()._arith_method(other, op) 

793 if self.freq is None or not is_scalar(other): 

794 return result 

795 

796 new_freq = None 

797 if isinstance(result, type(self)): 

798 new_freq = self._get_arith_result_freq(other, op) 

799 elif ( 

800 getattr(getattr(result, "dtype", None), "kind", None) == "M" and op is rsub 

801 ): 

802 # Timestamp/datetime - TDI produces a DatetimeIndex. 

803 # The array __rsub__ does (-self) + other, losing freq in 

804 # negation. Compute the freq at the Index level. 

805 new_freq = self._get_rsub_datetime_result_freq(other) 

806 

807 if new_freq is not None: 

808 result._freq = new_freq 

809 return result 

810 

811 def _get_arith_result_freq( 

812 self, other: object, op: Callable[..., Any] 

813 ) -> Day | Tick | None: 

814 """ 

815 Compute the result freq for arithmetic operations whose result 

816 is also a TimedeltaIndex. 

817 

818 Caller is responsible for checking self.freq is not None. 

819 """ 

820 freq = self.freq 

821 assert freq is not None # caller ensures this 

822 if op in (operator.mul, rmul): 

823 if bool(isna(other)): 

824 return None 

825 # error: No overload variant of "__mul__" of "BaseOffset" 

826 # matches argument type "object" 

827 new_freq = freq * other # type: ignore[operator] 

828 if new_freq.n == 0: 

829 # GH#51575 Better to have no freq than an incorrect one 

830 return None 

831 return new_freq 

832 

833 if op in (operator.truediv, operator.floordiv): 

834 # Note: freq gets division, not floor-division, even if op 

835 # is floordiv. 

836 if isinstance(freq, Day): 

837 if freq.n % other == 0: # type: ignore[operator] 

838 new_freq = Day(freq.n // other) # type: ignore[operator] 

839 else: 

840 new_freq = to_offset(Timedelta(days=freq.n)) / other # type: ignore[operator] 

841 else: 

842 new_freq = freq / other # type: ignore[operator] 

843 if new_freq.nanos == 0 and freq.nanos != 0: 

844 # e.g. if self.freq is Nano(1) then dividing by 2 

845 # rounds down to zero 

846 return None 

847 return new_freq 

848 

849 if op is rsub: 

850 # scalar_timedelta - TDI: the array uses (-self) + other, 

851 # losing freq in negation. Result freq is -self.freq. 

852 return -freq # type: ignore[return-value] 

853 

854 return None 

855 

856 def _get_rsub_datetime_result_freq(self, other: object) -> Day | Tick | None: 

857 """ 

858 Compute the result freq for Timestamp/datetime - TimedeltaIndex. 

859 

860 Mirrors the logic of _get_arithmetic_result_freq for the negated 

861 array case. 

862 """ 

863 freq = self.freq 

864 assert freq is not None # caller ensures this 

865 if isinstance(freq, Tick): 

866 return -freq 

867 

868 # freq is a Day; only preserve with tz-naive or UTC 

869 if isinstance(other, Timestamp): 

870 tz = other.tz 

871 else: 

872 tz = Timestamp(other).tz # type: ignore[arg-type] 

873 if tz is None or timezones.is_utc(tz): 

874 return -freq # type: ignore[return-value] 

875 return None 

876 

877 # ------------------------------------------------------------------- 

878 # Indexing Methods 

879 

880 def get_loc(self, key): 

881 """ 

882 Get integer location for requested label 

883 

884 Returns 

885 ------- 

886 loc : int, slice, or ndarray[int] 

887 """ 

888 self._check_indexing_error(key) 

889 

890 try: 

891 key = self._data._validate_scalar(key, unbox=False) 

892 except TypeError as err: 

893 raise KeyError(key) from err 

894 

895 return Index.get_loc(self, key) 

896 

897 # error: Return type "tuple[Timedelta | NaTType, Resolution]" of 

898 # "_parse_with_reso" incompatible with return type 

899 # "tuple[datetime, Resolution]" in supertype 

900 # "pandas.core.indexes.datetimelike.DatetimeIndexOpsMixin" 

901 def _parse_with_reso(self, label: str) -> tuple[Timedelta | NaTType, Resolution]: # type: ignore[override] 

902 # Resolution comes from the string text (GH#33603), not the value's 

903 # components: "720s" resolves to "s", not "min" (even though 

904 # 720s == 12 minutes). The parser reports both in a single pass. 

905 parsed, string_reso_code = parse_timedelta_string_reso(label) 

906 if isinstance(parsed, Timedelta): 

907 string_reso = Resolution(string_reso_code) 

908 # Fold in sub-unit precision the written unit doesn't capture, 

909 # e.g. "1.5min" is minute-written but second-resolution. 

910 value_reso = Resolution.get_reso_from_freqstr(parsed.resolution_string) 

911 reso = min(string_reso, value_reso) 

912 else: 

913 # i.e. pd.NaT 

914 reso = Resolution.RESO_SEC 

915 return parsed, reso 

916 

917 def _parsed_string_to_bounds(self, reso: Resolution, parsed: Timedelta): 

918 reso_str = reso.attr_abbrev 

919 lbound = parsed.floor(reso_str) 

920 rbound = ( 

921 lbound 

922 + to_offset(reso_str) 

923 - Timedelta(1, unit=self.unit).as_unit(self.unit) 

924 ) 

925 # If reso is finer than the index unit, the window [lbound, rbound] 

926 # collapses to lbound alone; without the clamp rbound < lbound. 

927 return lbound, max(lbound, rbound) 

928 

929 # ------------------------------------------------------------------- 

930 

931 @property 

932 def inferred_type(self) -> str: 

933 return "timedelta64" 

934 

935 

936@set_module("pandas") 

937def timedelta_range( 

938 start=None, 

939 end=None, 

940 periods: int | None = None, 

941 freq=None, 

942 name=None, 

943 closed=None, 

944 *, 

945 unit: TimeUnit | None = None, 

946) -> TimedeltaIndex: 

947 """ 

948 Return a fixed frequency TimedeltaIndex with day as the default. 

949 

950 This function generates a sequence of evenly spaced timedelta values 

951 between the specified bounds, using day as the default frequency. 

952 

953 Parameters 

954 ---------- 

955 start : str or timedelta-like, default None 

956 Left bound for generating timedeltas. 

957 end : str or timedelta-like, default None 

958 Right bound for generating timedeltas. 

959 periods : int, default None 

960 Number of periods to generate. 

961 freq : str, Timedelta, datetime.timedelta, or DateOffset, default 'D' 

962 Frequency strings can have multiples, e.g. '5h'. 

963 name : Hashable, default None 

964 Name of the resulting TimedeltaIndex. 

965 closed : str, default None 

966 Make the interval closed with respect to the given frequency to 

967 the 'left', 'right', or both sides (None). 

968 unit : {'s', 'ms', 'us', 'ns', None}, default None 

969 Specify the desired resolution of the result. 

970 If not specified, this is inferred from the 'start', 'end', and 'freq' 

971 using the same inference as :class:`Timedelta` taking the highest 

972 resolution of the three that are provided. 

973 

974 .. versionadded:: 2.0.0 

975 

976 Returns 

977 ------- 

978 TimedeltaIndex 

979 Fixed frequency, with day as the default. 

980 

981 See Also 

982 -------- 

983 date_range : Return a fixed frequency DatetimeIndex. 

984 period_range : Return a fixed frequency PeriodIndex. 

985 

986 Notes 

987 ----- 

988 Of the four parameters ``start``, ``end``, ``periods``, and ``freq``, 

989 a maximum of three can be specified at once. Of the three parameters 

990 ``start``, ``end``, and ``periods``, at least two must be specified. 

991 If ``freq`` is omitted, the resulting ``DatetimeIndex`` will have 

992 ``periods`` linearly spaced elements between ``start`` and ``end`` 

993 (closed on both sides). 

994 

995 To learn more about the frequency strings, please see 

996 :ref:`this link<timeseries.offset_aliases>`. 

997 

998 Examples 

999 -------- 

1000 >>> pd.timedelta_range(start="1 day", periods=4) 

1001 TimedeltaIndex(['1 days', '2 days', '3 days', '4 days'], 

1002 dtype='timedelta64[us]', freq='D') 

1003 

1004 The ``closed`` parameter specifies which endpoint is included. The default 

1005 behavior is to include both endpoints. 

1006 

1007 >>> pd.timedelta_range(start="1 day", periods=4, closed="right") 

1008 TimedeltaIndex(['2 days', '3 days', '4 days'], 

1009 dtype='timedelta64[us]', freq='D') 

1010 

1011 The ``freq`` parameter specifies the frequency of the TimedeltaIndex. 

1012 Only fixed frequencies can be passed, non-fixed frequencies such as 

1013 'M' (month end) will raise. 

1014 

1015 >>> pd.timedelta_range(start="1 day", end="2 days", freq="6h") 

1016 TimedeltaIndex(['1 days 00:00:00', '1 days 06:00:00', '1 days 12:00:00', 

1017 '1 days 18:00:00', '2 days 00:00:00'], 

1018 dtype='timedelta64[us]', freq='6h') 

1019 

1020 Specify ``start``, ``end``, and ``periods``; the frequency is generated 

1021 automatically (linearly spaced). 

1022 

1023 >>> pd.timedelta_range(start="1 day", end="5 days", periods=4) 

1024 TimedeltaIndex(['1 days 00:00:00', '2 days 08:00:00', '3 days 16:00:00', 

1025 '5 days 00:00:00'], 

1026 dtype='timedelta64[us]', freq=None) 

1027 

1028 **Specify a unit** 

1029 

1030 >>> pd.timedelta_range("1 Day", periods=3, freq="100000D", unit="s") 

1031 TimedeltaIndex(['1 days', '100001 days', '200001 days'], 

1032 dtype='timedelta64[s]', freq='100000D') 

1033 """ 

1034 if freq is None and com.any_none(periods, start, end): 

1035 freq = "D" 

1036 freq = to_offset(freq) 

1037 

1038 if com.count_not_none(start, end, periods, freq) != 3: 

1039 # This check needs to come before the `unit = start.unit` line below 

1040 raise ValueError( 

1041 "Of the four parameters: start, end, periods, " 

1042 "and freq, exactly three must be specified" 

1043 ) 

1044 

1045 if unit is None: 

1046 # Infer the unit based on the inputs 

1047 

1048 if start is not None and end is not None: 

1049 start = Timedelta(start) 

1050 end = Timedelta(end) 

1051 start = cast("Timedelta", start) 

1052 end = cast("Timedelta", end) 

1053 if abbrev_to_npy_unit(start.unit) > abbrev_to_npy_unit(end.unit): 

1054 unit = cast("TimeUnit", start.unit) 

1055 else: 

1056 unit = cast("TimeUnit", end.unit) 

1057 elif start is not None: 

1058 start = Timedelta(start) 

1059 start = cast("Timedelta", start) 

1060 unit = cast("TimeUnit", start.unit) 

1061 else: 

1062 end = Timedelta(end) 

1063 end = cast("Timedelta", end) 

1064 unit = cast("TimeUnit", end.unit) 

1065 

1066 # Last we need to watch out for cases where the 'freq' implies a higher 

1067 # unit than either start or end 

1068 if freq is not None: 

1069 freq = cast("Tick | Day", freq) 

1070 creso = abbrev_to_npy_unit(unit) 

1071 if freq._creso > creso: # pyright: ignore[reportAttributeAccessIssue] 

1072 unit = cast("TimeUnit", freq.base.freqstr) 

1073 

1074 tdarr = TimedeltaArray._generate_range( 

1075 start, end, periods, freq, closed=closed, unit=unit 

1076 ) 

1077 result = TimedeltaIndex._simple_new(tdarr, name=name) 

1078 result._freq = freq 

1079 return result