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

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

107 statements  

1"""implement the TimedeltaIndex""" 

2 

3from __future__ import annotations 

4 

5from typing import ( 

6 TYPE_CHECKING, 

7 cast, 

8) 

9 

10from pandas._libs import ( 

11 index as libindex, 

12 lib, 

13) 

14from pandas._libs.tslibs import ( 

15 Resolution, 

16 Timedelta, 

17 to_offset, 

18) 

19from pandas._libs.tslibs.dtypes import abbrev_to_npy_unit 

20from pandas.util._decorators import set_module 

21 

22from pandas.core.dtypes.common import ( 

23 is_scalar, 

24 pandas_dtype, 

25) 

26from pandas.core.dtypes.dtypes import ArrowDtype 

27from pandas.core.dtypes.generic import ABCSeries 

28 

29from pandas.core.arrays.timedeltas import TimedeltaArray 

30import pandas.core.common as com 

31from pandas.core.indexes.base import ( 

32 Index, 

33 maybe_extract_name, 

34) 

35from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin 

36from pandas.core.indexes.extension import inherit_names 

37 

38if TYPE_CHECKING: 

39 from pandas._libs import NaTType 

40 from pandas._libs.tslibs import ( 

41 Day, 

42 Tick, 

43 ) 

44 from pandas._typing import ( 

45 DtypeObj, 

46 TimeUnit, 

47 ) 

48 

49 

50@inherit_names( 

51 [ 

52 "__neg__", 

53 "__pos__", 

54 "__abs__", 

55 "total_seconds", 

56 "round", 

57 "floor", 

58 "ceil", 

59 *TimedeltaArray._field_ops, 

60 ], 

61 TimedeltaArray, 

62 wrap=True, 

63) 

64@inherit_names( 

65 [ 

66 "components", 

67 "to_pytimedelta", 

68 "sum", 

69 "std", 

70 "median", 

71 ], 

72 TimedeltaArray, 

73) 

74@set_module("pandas") 

75class TimedeltaIndex(DatetimeTimedeltaMixin): 

76 """ 

77 Immutable Index of timedelta64 data. 

78 

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

80 

81 Parameters 

82 ---------- 

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

84 Optional timedelta-like data to construct index with. 

85 freq : str or pandas offset object, optional 

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

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

88 the inferred frequency upon creation. 

89 dtype : numpy.dtype or str, default None 

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

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

92 copy : bool, default None 

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

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

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

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

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

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

99 name : object 

100 Name to be stored in the index. 

101 

102 Attributes 

103 ---------- 

104 days 

105 seconds 

106 microseconds 

107 nanoseconds 

108 components 

109 inferred_freq 

110 

111 Methods 

112 ------- 

113 to_pytimedelta 

114 to_series 

115 round 

116 floor 

117 ceil 

118 to_frame 

119 mean 

120 

121 See Also 

122 -------- 

123 Index : The base pandas Index type. 

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

125 DatetimeIndex : Index of datetime64 data. 

126 PeriodIndex : Index of Period data. 

127 timedelta_range : Create a fixed-frequency TimedeltaIndex. 

128 

129 Notes 

130 ----- 

131 To learn more about the frequency strings, please see 

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

133 

134 Examples 

135 -------- 

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

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

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

139 

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

141 

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

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

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

145 """ 

146 

147 _typ = "timedeltaindex" 

148 

149 _data_cls = TimedeltaArray 

150 

151 @property 

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

153 return libindex.TimedeltaEngine 

154 

155 _data: TimedeltaArray 

156 

157 # Use base class method instead of DatetimeTimedeltaMixin._get_string_slice 

158 _get_string_slice = Index._get_string_slice 

159 

160 # error: Signature of "_resolution_obj" incompatible with supertype 

161 # "DatetimeIndexOpsMixin" 

162 @property 

163 def _resolution_obj(self) -> Resolution | None: # type: ignore[override] 

164 return self._data._resolution_obj 

165 

166 # ------------------------------------------------------------------- 

167 # Constructors 

168 

169 def __new__( 

170 cls, 

171 data=None, 

172 freq=lib.no_default, 

173 dtype=None, 

174 copy: bool | None = None, 

175 name=None, 

176 ): 

177 name = maybe_extract_name(name, data, cls) 

178 

179 # GH#63388 

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

181 

182 if is_scalar(data): 

183 cls._raise_scalar_data_error(data) 

184 

185 if dtype is not None: 

186 dtype = pandas_dtype(dtype) 

187 

188 if ( 

189 isinstance(data, TimedeltaArray) 

190 and freq is lib.no_default 

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

192 ): 

193 if copy: 

194 data = data.copy() 

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

196 

197 if ( 

198 isinstance(data, TimedeltaIndex) 

199 and freq is lib.no_default 

200 and name is None 

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

202 ): 

203 if copy: 

204 return data.copy() 

205 else: 

206 return data._view() 

207 

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

209 

210 tdarr = TimedeltaArray._from_sequence_not_strict( 

211 data, freq=freq, unit=None, dtype=dtype, copy=copy 

212 ) 

213 refs = None 

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

215 refs = data._references 

216 

217 return cls._simple_new(tdarr, name=name, refs=refs) 

218 

219 # ------------------------------------------------------------------- 

220 

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

222 """ 

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

224 """ 

225 if isinstance(dtype, ArrowDtype): 

226 return dtype.kind == "m" 

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

228 

229 # ------------------------------------------------------------------- 

230 # Indexing Methods 

231 

232 def get_loc(self, key): 

233 """ 

234 Get integer location for requested label 

235 

236 Returns 

237 ------- 

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

239 """ 

240 self._check_indexing_error(key) 

241 

242 try: 

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

244 except TypeError as err: 

245 raise KeyError(key) from err 

246 

247 return Index.get_loc(self, key) 

248 

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

250 # "_parse_with_reso" incompatible with return type 

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

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

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

254 parsed = Timedelta(label) 

255 if isinstance(parsed, Timedelta): 

256 reso = Resolution.get_reso_from_freqstr(parsed.unit) 

257 else: 

258 # i.e. pd.NaT 

259 reso = Resolution.get_reso_from_freqstr("s") 

260 return parsed, reso 

261 

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

263 # reso is unused, included to match signature of DTI/PI 

264 lbound = parsed.round(parsed.resolution_string) 

265 rbound = ( 

266 lbound 

267 + to_offset(parsed.resolution_string) 

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

269 ) 

270 return lbound, rbound 

271 

272 # ------------------------------------------------------------------- 

273 

274 @property 

275 def inferred_type(self) -> str: 

276 return "timedelta64" 

277 

278 

279@set_module("pandas") 

280def timedelta_range( 

281 start=None, 

282 end=None, 

283 periods: int | None = None, 

284 freq=None, 

285 name=None, 

286 closed=None, 

287 *, 

288 unit: TimeUnit | None = None, 

289) -> TimedeltaIndex: 

290 """ 

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

292 

293 Parameters 

294 ---------- 

295 start : str or timedelta-like, default None 

296 Left bound for generating timedeltas. 

297 end : str or timedelta-like, default None 

298 Right bound for generating timedeltas. 

299 periods : int, default None 

300 Number of periods to generate. 

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

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

303 name : Hashable, default None 

304 Name of the resulting TimedeltaIndex. 

305 closed : str, default None 

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

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

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

309 Specify the desired resolution of the result. 

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

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

312 resolution of the three that are provided. 

313 

314 .. versionadded:: 2.0.0 

315 

316 Returns 

317 ------- 

318 TimedeltaIndex 

319 Fixed frequency, with day as the default. 

320 

321 See Also 

322 -------- 

323 date_range : Return a fixed frequency DatetimeIndex. 

324 period_range : Return a fixed frequency PeriodIndex. 

325 

326 Notes 

327 ----- 

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

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

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

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

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

333 (closed on both sides). 

334 

335 To learn more about the frequency strings, please see 

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

337 

338 Examples 

339 -------- 

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

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

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

343 

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

345 behavior is to include both endpoints. 

346 

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

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

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

350 

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

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

353 'M' (month end) will raise. 

354 

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

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

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

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

359 

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

361 automatically (linearly spaced). 

362 

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

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

365 '5 days 00:00:00'], 

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

367 

368 **Specify a unit** 

369 

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

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

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

373 """ 

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

375 freq = "D" 

376 freq = to_offset(freq) 

377 

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

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

380 raise ValueError( 

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

382 "and freq, exactly three must be specified" 

383 ) 

384 

385 if unit is None: 

386 # Infer the unit based on the inputs 

387 

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

389 start = Timedelta(start) 

390 end = Timedelta(end) 

391 start = cast(Timedelta, start) 

392 end = cast(Timedelta, end) 

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

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

395 else: 

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

397 elif start is not None: 

398 start = Timedelta(start) 

399 start = cast(Timedelta, start) 

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

401 else: 

402 end = Timedelta(end) 

403 end = cast(Timedelta, end) 

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

405 

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

407 # unit than either start or end 

408 if freq is not None: 

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

410 creso = abbrev_to_npy_unit(unit) 

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

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

413 

414 tdarr = TimedeltaArray._generate_range( 

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

416 ) 

417 return TimedeltaIndex._simple_new(tdarr, name=name)