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

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

153 statements  

1""" 

2datetimelike delegation 

3""" 

4 

5from __future__ import annotations 

6 

7from typing import ( 

8 TYPE_CHECKING, 

9 NoReturn, 

10 cast, 

11) 

12import warnings 

13 

14import numpy as np 

15 

16from pandas._libs import lib 

17from pandas.errors import Pandas4Warning 

18from pandas.util._exceptions import find_stack_level 

19 

20from pandas.core.dtypes.common import ( 

21 is_integer_dtype, 

22 is_list_like, 

23) 

24from pandas.core.dtypes.dtypes import ( 

25 ArrowDtype, 

26 CategoricalDtype, 

27 DatetimeTZDtype, 

28 PeriodDtype, 

29) 

30from pandas.core.dtypes.generic import ABCSeries 

31 

32from pandas.core.accessor import ( 

33 PandasDelegate, 

34 delegate_names, 

35) 

36from pandas.core.arrays import ( 

37 DatetimeArray, 

38 PeriodArray, 

39 TimedeltaArray, 

40) 

41from pandas.core.arrays.arrow.array import ArrowExtensionArray 

42from pandas.core.base import ( 

43 NoNewAttributesMixin, 

44 PandasObject, 

45) 

46from pandas.core.indexes.datetimes import DatetimeIndex 

47from pandas.core.indexes.timedeltas import TimedeltaIndex 

48 

49if TYPE_CHECKING: 

50 from pandas import ( 

51 DataFrame, 

52 Series, 

53 ) 

54 

55 

56class Properties(PandasDelegate, PandasObject, NoNewAttributesMixin): 

57 _hidden_attrs = PandasObject._hidden_attrs | { 

58 "orig", 

59 "name", 

60 } 

61 

62 def __init__(self, data: Series, orig) -> None: 

63 if not isinstance(data, ABCSeries): 

64 raise TypeError( 

65 f"cannot convert an object of type {type(data)} to a datetimelike index" 

66 ) 

67 

68 self._parent = data 

69 self.orig = orig 

70 self.name = getattr(data, "name", None) 

71 self._freeze() 

72 

73 def _get_values(self): 

74 data = self._parent 

75 if lib.is_np_dtype(data.dtype, "M"): 

76 return DatetimeIndex(data, copy=False, name=self.name) 

77 

78 elif isinstance(data.dtype, DatetimeTZDtype): 

79 return DatetimeIndex(data, copy=False, name=self.name) 

80 

81 elif lib.is_np_dtype(data.dtype, "m"): 

82 return TimedeltaIndex(data, copy=False, name=self.name) 

83 

84 elif isinstance(data.dtype, PeriodDtype): 

85 return PeriodArray(data, copy=False) 

86 

87 raise TypeError( 

88 f"cannot convert an object of type {type(data)} to a datetimelike index" 

89 ) 

90 

91 def _delegate_property_get(self, name: str): 

92 from pandas import Series 

93 

94 values = self._get_values() 

95 

96 result = getattr(values, name) 

97 

98 # maybe need to upcast (ints) 

99 if isinstance(result, np.ndarray): 

100 if is_integer_dtype(result): 

101 result = result.astype("int64") 

102 elif not is_list_like(result): 

103 return result 

104 

105 result = np.asarray(result) 

106 

107 if self.orig is not None: 

108 index = self.orig.index 

109 else: 

110 index = self._parent.index 

111 # return the result as a Series 

112 return Series(result, index=index, name=self.name).__finalize__(self._parent) 

113 

114 def _delegate_property_set(self, name: str, value, *args, **kwargs) -> NoReturn: 

115 raise ValueError( 

116 "modifications to a property of a datetimelike object are not supported. " 

117 "Change values on the original." 

118 ) 

119 

120 def _delegate_method(self, name: str, *args, **kwargs): 

121 from pandas import Series 

122 

123 values = self._get_values() 

124 

125 method = getattr(values, name) 

126 result = method(*args, **kwargs) 

127 

128 if not is_list_like(result): 

129 return result 

130 

131 return Series(result, index=self._parent.index, name=self.name).__finalize__( 

132 self._parent 

133 ) 

134 

135 

136@delegate_names( 

137 delegate=ArrowExtensionArray, 

138 accessors=TimedeltaArray._datetimelike_ops, 

139 typ="property", 

140 accessor_mapping=lambda x: f"_dt_{x}", 

141 raise_on_missing=False, 

142) 

143@delegate_names( 

144 delegate=ArrowExtensionArray, 

145 accessors=TimedeltaArray._datetimelike_methods, 

146 typ="method", 

147 accessor_mapping=lambda x: f"_dt_{x}", 

148 raise_on_missing=False, 

149) 

150@delegate_names( 

151 delegate=ArrowExtensionArray, 

152 accessors=DatetimeArray._datetimelike_ops, 

153 typ="property", 

154 accessor_mapping=lambda x: f"_dt_{x}", 

155 raise_on_missing=False, 

156) 

157@delegate_names( 

158 delegate=ArrowExtensionArray, 

159 accessors=DatetimeArray._datetimelike_methods, 

160 typ="method", 

161 accessor_mapping=lambda x: f"_dt_{x}", 

162 raise_on_missing=False, 

163) 

164class ArrowTemporalProperties(PandasDelegate, PandasObject, NoNewAttributesMixin): 

165 def __init__(self, data: Series, orig) -> None: 

166 if not isinstance(data, ABCSeries): 

167 raise TypeError( 

168 f"cannot convert an object of type {type(data)} to a datetimelike index" 

169 ) 

170 

171 self._parent = data 

172 self._orig = orig 

173 self._freeze() 

174 

175 def _delegate_property_get(self, name: str): 

176 if not hasattr(self._parent.array, f"_dt_{name}"): 

177 raise NotImplementedError( 

178 f"dt.{name} is not supported for {self._parent.dtype}" 

179 ) 

180 result = getattr(self._parent.array, f"_dt_{name}") 

181 

182 if not is_list_like(result): 

183 return result 

184 

185 if self._orig is not None: 

186 index = self._orig.index 

187 else: 

188 index = self._parent.index 

189 # return the result as a Series, which is by definition a copy 

190 result = type(self._parent)( 

191 result, index=index, name=self._parent.name 

192 ).__finalize__(self._parent) 

193 

194 return result 

195 

196 def _delegate_method(self, name: str, *args, **kwargs): 

197 if not hasattr(self._parent.array, f"_dt_{name}"): 

198 raise NotImplementedError( 

199 f"dt.{name} is not supported for {self._parent.dtype}" 

200 ) 

201 

202 result = getattr(self._parent.array, f"_dt_{name}")(*args, **kwargs) 

203 

204 if self._orig is not None: 

205 index = self._orig.index 

206 else: 

207 index = self._parent.index 

208 # return the result as a Series, which is by definition a copy 

209 result = type(self._parent)( 

210 result, index=index, name=self._parent.name 

211 ).__finalize__(self._parent) 

212 

213 return result 

214 

215 def to_pytimedelta(self): 

216 # GH 57463 

217 warnings.warn( 

218 f"The behavior of {type(self).__name__}.to_pytimedelta is deprecated, " 

219 "in a future version this will return a Series containing python " 

220 "datetime.timedelta objects instead of an ndarray. To retain the " 

221 "old behavior, call `np.array` on the result", 

222 Pandas4Warning, 

223 stacklevel=find_stack_level(), 

224 ) 

225 return cast(ArrowExtensionArray, self._parent.array)._dt_to_pytimedelta() 

226 

227 def to_pydatetime(self) -> Series: 

228 # GH#20306 

229 return cast(ArrowExtensionArray, self._parent.array)._dt_to_pydatetime() 

230 

231 def isocalendar(self) -> DataFrame: 

232 from pandas import DataFrame 

233 

234 result = ( 

235 cast(ArrowExtensionArray, self._parent.array) 

236 ._dt_isocalendar() 

237 ._pa_array.combine_chunks() 

238 ) 

239 iso_calendar_df = DataFrame( 

240 { 

241 col: type(self._parent.array)(result.field(i)) # type: ignore[call-arg] 

242 for i, col in enumerate(["year", "week", "day"]) 

243 } 

244 ) 

245 return iso_calendar_df 

246 

247 @property 

248 def components(self) -> DataFrame: 

249 from pandas import DataFrame 

250 

251 components_df = DataFrame( 

252 { 

253 col: getattr(self._parent.array, f"_dt_{col}") 

254 for col in [ 

255 "days", 

256 "hours", 

257 "minutes", 

258 "seconds", 

259 "milliseconds", 

260 "microseconds", 

261 "nanoseconds", 

262 ] 

263 } 

264 ) 

265 return components_df 

266 

267 

268@delegate_names( 

269 delegate=DatetimeArray, 

270 accessors=[*DatetimeArray._datetimelike_ops, "unit"], 

271 typ="property", 

272) 

273@delegate_names( 

274 delegate=DatetimeArray, 

275 accessors=[*DatetimeArray._datetimelike_methods, "as_unit"], 

276 typ="method", 

277) 

278class DatetimeProperties(Properties): 

279 """ 

280 Accessor object for datetimelike properties of the Series values. 

281 

282 Examples 

283 -------- 

284 >>> seconds_series = pd.Series(pd.date_range("2000-01-01", periods=3, freq="s")) 

285 >>> seconds_series 

286 0 2000-01-01 00:00:00 

287 1 2000-01-01 00:00:01 

288 2 2000-01-01 00:00:02 

289 dtype: datetime64[us] 

290 >>> seconds_series.dt.second 

291 0 0 

292 1 1 

293 2 2 

294 dtype: int32 

295 

296 >>> hours_series = pd.Series(pd.date_range("2000-01-01", periods=3, freq="h")) 

297 >>> hours_series 

298 0 2000-01-01 00:00:00 

299 1 2000-01-01 01:00:00 

300 2 2000-01-01 02:00:00 

301 dtype: datetime64[us] 

302 >>> hours_series.dt.hour 

303 0 0 

304 1 1 

305 2 2 

306 dtype: int32 

307 

308 >>> quarters_series = pd.Series(pd.date_range("2000-01-01", periods=3, freq="QE")) 

309 >>> quarters_series 

310 0 2000-03-31 

311 1 2000-06-30 

312 2 2000-09-30 

313 dtype: datetime64[us] 

314 >>> quarters_series.dt.quarter 

315 0 1 

316 1 2 

317 2 3 

318 dtype: int32 

319 

320 Returns a Series indexed like the original Series. 

321 Raises TypeError if the Series does not contain datetimelike values. 

322 """ 

323 

324 def to_pydatetime(self) -> Series: 

325 """ 

326 Return the data as a Series of :class:`datetime.datetime` objects. 

327 

328 Timezone information is retained if present. 

329 

330 .. warning:: 

331 

332 Python's datetime uses microsecond resolution, which is lower than 

333 pandas (nanosecond). The values are truncated. 

334 

335 Returns 

336 ------- 

337 numpy.ndarray 

338 Object dtype array containing native Python datetime objects. 

339 

340 See Also 

341 -------- 

342 datetime.datetime : Standard library value for a datetime. 

343 

344 Examples 

345 -------- 

346 >>> s = pd.Series(pd.date_range("20180310", periods=2)) 

347 >>> s 

348 0 2018-03-10 

349 1 2018-03-11 

350 dtype: datetime64[us] 

351 

352 >>> s.dt.to_pydatetime() 

353 0 2018-03-10 00:00:00 

354 1 2018-03-11 00:00:00 

355 dtype: object 

356 

357 pandas' nanosecond precision is truncated to microseconds. 

358 

359 >>> s = pd.Series(pd.date_range("20180310", periods=2, freq="ns")) 

360 >>> s 

361 0 2018-03-10 00:00:00.000000000 

362 1 2018-03-10 00:00:00.000000001 

363 dtype: datetime64[ns] 

364 

365 >>> s.dt.to_pydatetime() 

366 0 2018-03-10 00:00:00 

367 1 2018-03-10 00:00:00 

368 dtype: object 

369 """ 

370 # GH#20306 

371 from pandas import Series 

372 

373 return Series(self._get_values().to_pydatetime(), dtype=object) 

374 

375 @property 

376 def freq(self): 

377 """ 

378 Tries to return a string representing a frequency generated by infer_freq. 

379 

380 Returns None if it can't autodetect the frequency. 

381 

382 See Also 

383 -------- 

384 Series.dt.to_period : Cast to PeriodArray/PeriodIndex at a particular 

385 frequency. 

386 

387 Examples 

388 -------- 

389 >>> ser = pd.Series(["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04"]) 

390 >>> ser = pd.to_datetime(ser) 

391 >>> ser.dt.freq 

392 'D' 

393 

394 >>> ser = pd.Series(["2022-01-01", "2024-01-01", "2026-01-01", "2028-01-01"]) 

395 >>> ser = pd.to_datetime(ser) 

396 >>> ser.dt.freq 

397 '2YS-JAN' 

398 """ 

399 return self._get_values().inferred_freq 

400 

401 def isocalendar(self) -> DataFrame: 

402 """ 

403 Calculate year, week, and day according to the ISO 8601 standard. 

404 

405 Returns 

406 ------- 

407 DataFrame 

408 With columns year, week and day. 

409 

410 See Also 

411 -------- 

412 Timestamp.isocalendar : Function return a 3-tuple containing ISO year, 

413 week number, and weekday for the given Timestamp object. 

414 datetime.date.isocalendar : Return a named tuple object with 

415 three components: year, week and weekday. 

416 

417 Examples 

418 -------- 

419 >>> ser = pd.to_datetime(pd.Series(["2010-01-01", pd.NaT])) 

420 >>> ser.dt.isocalendar() 

421 year week day 

422 0 2009 53 5 

423 1 <NA> <NA> <NA> 

424 >>> ser.dt.isocalendar().week 

425 0 53 

426 1 <NA> 

427 Name: week, dtype: UInt32 

428 """ 

429 return self._get_values().isocalendar().set_index(self._parent.index) 

430 

431 

432@delegate_names( 

433 delegate=TimedeltaArray, accessors=TimedeltaArray._datetimelike_ops, typ="property" 

434) 

435@delegate_names( 

436 delegate=TimedeltaArray, 

437 accessors=TimedeltaArray._datetimelike_methods, 

438 typ="method", 

439) 

440class TimedeltaProperties(Properties): 

441 """ 

442 Accessor object for datetimelike properties of the Series values. 

443 

444 Returns a Series indexed like the original Series. 

445 Raises TypeError if the Series does not contain datetimelike values. 

446 

447 Examples 

448 -------- 

449 >>> seconds_series = pd.Series( 

450 ... pd.timedelta_range(start="1 second", periods=3, freq="s") 

451 ... ) 

452 >>> seconds_series 

453 0 0 days 00:00:01 

454 1 0 days 00:00:02 

455 2 0 days 00:00:03 

456 dtype: timedelta64[us] 

457 >>> seconds_series.dt.seconds 

458 0 1 

459 1 2 

460 2 3 

461 dtype: int32 

462 """ 

463 

464 def to_pytimedelta(self) -> np.ndarray: 

465 """ 

466 Return an array of native :class:`datetime.timedelta` objects. 

467 

468 Python's standard `datetime` library uses a different representation 

469 timedelta's. This method converts a Series of pandas Timedeltas 

470 to `datetime.timedelta` format with the same length as the original 

471 Series. 

472 

473 Returns 

474 ------- 

475 numpy.ndarray 

476 Array of 1D containing data with `datetime.timedelta` type. 

477 

478 See Also 

479 -------- 

480 datetime.timedelta : A duration expressing the difference 

481 between two date, time, or datetime. 

482 

483 Examples 

484 -------- 

485 >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="D")) 

486 >>> s 

487 0 0 days 

488 1 1 days 

489 2 2 days 

490 3 3 days 

491 4 4 days 

492 dtype: timedelta64[s] 

493 

494 >>> s.dt.to_pytimedelta() 

495 array([datetime.timedelta(0), datetime.timedelta(days=1), 

496 datetime.timedelta(days=2), datetime.timedelta(days=3), 

497 datetime.timedelta(days=4)], dtype=object) 

498 """ 

499 # GH 57463 

500 warnings.warn( 

501 f"The behavior of {type(self).__name__}.to_pytimedelta is deprecated, " 

502 "in a future version this will return a Series containing python " 

503 "datetime.timedelta objects instead of an ndarray. To retain the " 

504 "old behavior, call `np.array` on the result", 

505 Pandas4Warning, 

506 stacklevel=find_stack_level(), 

507 ) 

508 return self._get_values().to_pytimedelta() 

509 

510 @property 

511 def components(self) -> DataFrame: 

512 """ 

513 Return a Dataframe of the components of the Timedeltas. 

514 

515 Each row of the DataFrame corresponds to a Timedelta in the original 

516 Series and contains the individual components (days, hours, minutes, 

517 seconds, milliseconds, microseconds, nanoseconds) of the Timedelta. 

518 

519 Returns 

520 ------- 

521 DataFrame 

522 

523 See Also 

524 -------- 

525 TimedeltaIndex.components : Return a DataFrame of the individual resolution 

526 components of the Timedeltas. 

527 Series.dt.total_seconds : Return the total number of seconds in the duration. 

528 

529 Examples 

530 -------- 

531 >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="s")) 

532 >>> s 

533 0 0 days 00:00:00 

534 1 0 days 00:00:01 

535 2 0 days 00:00:02 

536 3 0 days 00:00:03 

537 4 0 days 00:00:04 

538 dtype: timedelta64[s] 

539 >>> s.dt.components 

540 days hours minutes seconds milliseconds microseconds nanoseconds 

541 0 0 0 0 0 0 0 0 

542 1 0 0 0 1 0 0 0 

543 2 0 0 0 2 0 0 0 

544 3 0 0 0 3 0 0 0 

545 4 0 0 0 4 0 0 0 

546 """ 

547 return ( 

548 self._get_values() 

549 .components.set_index(self._parent.index) 

550 .__finalize__(self._parent) 

551 ) 

552 

553 @property 

554 def freq(self): 

555 return self._get_values().inferred_freq 

556 

557 

558@delegate_names( 

559 delegate=PeriodArray, accessors=PeriodArray._datetimelike_ops, typ="property" 

560) 

561@delegate_names( 

562 delegate=PeriodArray, accessors=PeriodArray._datetimelike_methods, typ="method" 

563) 

564class PeriodProperties(Properties): 

565 """ 

566 Accessor object for datetimelike properties of the Series values. 

567 

568 Returns a Series indexed like the original Series. 

569 Raises TypeError if the Series does not contain datetimelike values. 

570 

571 Examples 

572 -------- 

573 >>> seconds_series = pd.Series( 

574 ... pd.period_range( 

575 ... start="2000-01-01 00:00:00", end="2000-01-01 00:00:03", freq="s" 

576 ... ) 

577 ... ) 

578 >>> seconds_series 

579 0 2000-01-01 00:00:00 

580 1 2000-01-01 00:00:01 

581 2 2000-01-01 00:00:02 

582 3 2000-01-01 00:00:03 

583 dtype: period[s] 

584 >>> seconds_series.dt.second 

585 0 0 

586 1 1 

587 2 2 

588 3 3 

589 dtype: int64 

590 

591 >>> hours_series = pd.Series( 

592 ... pd.period_range(start="2000-01-01 00:00", end="2000-01-01 03:00", freq="h") 

593 ... ) 

594 >>> hours_series 

595 0 2000-01-01 00:00 

596 1 2000-01-01 01:00 

597 2 2000-01-01 02:00 

598 3 2000-01-01 03:00 

599 dtype: period[h] 

600 >>> hours_series.dt.hour 

601 0 0 

602 1 1 

603 2 2 

604 3 3 

605 dtype: int64 

606 

607 >>> quarters_series = pd.Series( 

608 ... pd.period_range(start="2000-01-01", end="2000-12-31", freq="Q-DEC") 

609 ... ) 

610 >>> quarters_series 

611 0 2000Q1 

612 1 2000Q2 

613 2 2000Q3 

614 3 2000Q4 

615 dtype: period[Q-DEC] 

616 >>> quarters_series.dt.quarter 

617 0 1 

618 1 2 

619 2 3 

620 3 4 

621 dtype: int64 

622 """ 

623 

624 

625class CombinedDatetimelikeProperties( 

626 DatetimeProperties, TimedeltaProperties, PeriodProperties 

627): 

628 """ 

629 Accessor object for Series values' datetime-like, timedelta and period properties. 

630 

631 See Also 

632 -------- 

633 DatetimeIndex : Index of datetime64 data. 

634 

635 Examples 

636 -------- 

637 >>> dates = pd.Series( 

638 ... ["2024-01-01", "2024-01-15", "2024-02-5"], dtype="datetime64[ns]" 

639 ... ) 

640 >>> dates.dt.day 

641 0 1 

642 1 15 

643 2 5 

644 dtype: int32 

645 >>> dates.dt.month 

646 0 1 

647 1 1 

648 2 2 

649 dtype: int32 

650 

651 >>> dates = pd.Series( 

652 ... ["2024-01-01", "2024-01-15", "2024-02-5"], dtype="datetime64[ns, UTC]" 

653 ... ) 

654 >>> dates.dt.day 

655 0 1 

656 1 15 

657 2 5 

658 dtype: int32 

659 >>> dates.dt.month 

660 0 1 

661 1 1 

662 2 2 

663 dtype: int32 

664 """ 

665 

666 def __new__(cls, data: Series): # pyright: ignore[reportInconsistentConstructor] 

667 # CombinedDatetimelikeProperties isn't really instantiated. Instead 

668 # we need to choose which parent (datetime or timedelta) is 

669 # appropriate. Since we're checking the dtypes anyway, we'll just 

670 # do all the validation here. 

671 

672 if not isinstance(data, ABCSeries): 

673 raise TypeError( 

674 f"cannot convert an object of type {type(data)} to a datetimelike index" 

675 ) 

676 

677 orig = data if isinstance(data.dtype, CategoricalDtype) else None 

678 if orig is not None: 

679 data = data._constructor( 

680 orig.array, 

681 name=orig.name, 

682 copy=False, 

683 dtype=orig._values.categories.dtype, 

684 index=orig.index, 

685 ) 

686 

687 if isinstance(data.dtype, ArrowDtype) and data.dtype.kind in "Mm": 

688 return ArrowTemporalProperties(data, orig) 

689 if lib.is_np_dtype(data.dtype, "M"): 

690 return DatetimeProperties(data, orig) 

691 elif isinstance(data.dtype, DatetimeTZDtype): 

692 return DatetimeProperties(data, orig) 

693 elif lib.is_np_dtype(data.dtype, "m"): 

694 return TimedeltaProperties(data, orig) 

695 elif isinstance(data.dtype, PeriodDtype): 

696 return PeriodProperties(data, orig) 

697 

698 raise AttributeError("Can only use .dt accessor with datetimelike values")