Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/pandas/core/indexes/accessors.py: 36%

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

154 statements  

1""" 

2datetimelike delegation 

3""" 

4from __future__ import annotations 

5 

6from typing import ( 

7 TYPE_CHECKING, 

8 cast, 

9) 

10import warnings 

11 

12import numpy as np 

13 

14from pandas._libs import lib 

15from pandas.util._exceptions import find_stack_level 

16 

17from pandas.core.dtypes.common import ( 

18 is_integer_dtype, 

19 is_list_like, 

20) 

21from pandas.core.dtypes.dtypes import ( 

22 ArrowDtype, 

23 CategoricalDtype, 

24 DatetimeTZDtype, 

25 PeriodDtype, 

26) 

27from pandas.core.dtypes.generic import ABCSeries 

28 

29from pandas.core.accessor import ( 

30 PandasDelegate, 

31 delegate_names, 

32) 

33from pandas.core.arrays import ( 

34 DatetimeArray, 

35 PeriodArray, 

36 TimedeltaArray, 

37) 

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

39from pandas.core.base import ( 

40 NoNewAttributesMixin, 

41 PandasObject, 

42) 

43from pandas.core.indexes.datetimes import DatetimeIndex 

44from pandas.core.indexes.timedeltas import TimedeltaIndex 

45 

46if TYPE_CHECKING: 

47 from pandas import ( 

48 DataFrame, 

49 Series, 

50 ) 

51 

52 

53class Properties(PandasDelegate, PandasObject, NoNewAttributesMixin): 

54 _hidden_attrs = PandasObject._hidden_attrs | { 

55 "orig", 

56 "name", 

57 } 

58 

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

60 if not isinstance(data, ABCSeries): 

61 raise TypeError( 

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

63 ) 

64 

65 self._parent = data 

66 self.orig = orig 

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

68 self._freeze() 

69 

70 def _get_values(self): 

71 data = self._parent 

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

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

74 

75 elif isinstance(data.dtype, DatetimeTZDtype): 

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

77 

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

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

80 

81 elif isinstance(data.dtype, PeriodDtype): 

82 return PeriodArray(data, copy=False) 

83 

84 raise TypeError( 

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

86 ) 

87 

88 def _delegate_property_get(self, name: str): 

89 from pandas import Series 

90 

91 values = self._get_values() 

92 

93 result = getattr(values, name) 

94 

95 # maybe need to upcast (ints) 

96 if isinstance(result, np.ndarray): 

97 if is_integer_dtype(result): 

98 result = result.astype("int64") 

99 elif not is_list_like(result): 

100 return result 

101 

102 result = np.asarray(result) 

103 

104 if self.orig is not None: 

105 index = self.orig.index 

106 else: 

107 index = self._parent.index 

108 # return the result as a Series 

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

110 

111 # setting this object will show a SettingWithCopyWarning/Error 

112 result._is_copy = ( 

113 "modifications to a property of a datetimelike " 

114 "object are not supported and are discarded. " 

115 "Change values on the original." 

116 ) 

117 

118 return result 

119 

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

121 raise ValueError( 

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

123 "Change values on the original." 

124 ) 

125 

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

127 from pandas import Series 

128 

129 values = self._get_values() 

130 

131 method = getattr(values, name) 

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

133 

134 if not is_list_like(result): 

135 return result 

136 

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

138 self._parent 

139 ) 

140 

141 # setting this object will show a SettingWithCopyWarning/Error 

142 result._is_copy = ( 

143 "modifications to a method of a datetimelike " 

144 "object are not supported and are discarded. " 

145 "Change values on the original." 

146 ) 

147 

148 return result 

149 

150 

151@delegate_names( 

152 delegate=ArrowExtensionArray, 

153 accessors=TimedeltaArray._datetimelike_ops, 

154 typ="property", 

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

156 raise_on_missing=False, 

157) 

158@delegate_names( 

159 delegate=ArrowExtensionArray, 

160 accessors=TimedeltaArray._datetimelike_methods, 

161 typ="method", 

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

163 raise_on_missing=False, 

164) 

165@delegate_names( 

166 delegate=ArrowExtensionArray, 

167 accessors=DatetimeArray._datetimelike_ops, 

168 typ="property", 

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

170 raise_on_missing=False, 

171) 

172@delegate_names( 

173 delegate=ArrowExtensionArray, 

174 accessors=DatetimeArray._datetimelike_methods, 

175 typ="method", 

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

177 raise_on_missing=False, 

178) 

179class ArrowTemporalProperties(PandasDelegate, PandasObject, NoNewAttributesMixin): 

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

181 if not isinstance(data, ABCSeries): 

182 raise TypeError( 

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

184 ) 

185 

186 self._parent = data 

187 self._orig = orig 

188 self._freeze() 

189 

190 def _delegate_property_get(self, name: str): 

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

192 raise NotImplementedError( 

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

194 ) 

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

196 

197 if not is_list_like(result): 

198 return result 

199 

200 if self._orig is not None: 

201 index = self._orig.index 

202 else: 

203 index = self._parent.index 

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

205 result = type(self._parent)( 

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

207 ).__finalize__(self._parent) 

208 

209 return result 

210 

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

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

213 raise NotImplementedError( 

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

215 ) 

216 

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

218 

219 if self._orig is not None: 

220 index = self._orig.index 

221 else: 

222 index = self._parent.index 

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

224 result = type(self._parent)( 

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

226 ).__finalize__(self._parent) 

227 

228 return result 

229 

230 def to_pytimedelta(self): 

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

232 

233 def to_pydatetime(self): 

234 # GH#20306 

235 warnings.warn( 

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

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

238 "datetime objects instead of an ndarray. To retain the old behavior, " 

239 "call `np.array` on the result", 

240 FutureWarning, 

241 stacklevel=find_stack_level(), 

242 ) 

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

244 

245 def isocalendar(self) -> DataFrame: 

246 from pandas import DataFrame 

247 

248 result = ( 

249 cast(ArrowExtensionArray, self._parent.array) 

250 ._dt_isocalendar() 

251 ._pa_array.combine_chunks() 

252 ) 

253 iso_calendar_df = DataFrame( 

254 { 

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

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

257 } 

258 ) 

259 return iso_calendar_df 

260 

261 @property 

262 def components(self) -> DataFrame: 

263 from pandas import DataFrame 

264 

265 components_df = DataFrame( 

266 { 

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

268 for col in [ 

269 "days", 

270 "hours", 

271 "minutes", 

272 "seconds", 

273 "milliseconds", 

274 "microseconds", 

275 "nanoseconds", 

276 ] 

277 } 

278 ) 

279 return components_df 

280 

281 

282@delegate_names( 

283 delegate=DatetimeArray, 

284 accessors=DatetimeArray._datetimelike_ops + ["unit"], 

285 typ="property", 

286) 

287@delegate_names( 

288 delegate=DatetimeArray, 

289 accessors=DatetimeArray._datetimelike_methods + ["as_unit"], 

290 typ="method", 

291) 

292class DatetimeProperties(Properties): 

293 """ 

294 Accessor object for datetimelike properties of the Series values. 

295 

296 Examples 

297 -------- 

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

299 >>> seconds_series 

300 0 2000-01-01 00:00:00 

301 1 2000-01-01 00:00:01 

302 2 2000-01-01 00:00:02 

303 dtype: datetime64[ns] 

304 >>> seconds_series.dt.second 

305 0 0 

306 1 1 

307 2 2 

308 dtype: int32 

309 

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

311 >>> hours_series 

312 0 2000-01-01 00:00:00 

313 1 2000-01-01 01:00:00 

314 2 2000-01-01 02:00:00 

315 dtype: datetime64[ns] 

316 >>> hours_series.dt.hour 

317 0 0 

318 1 1 

319 2 2 

320 dtype: int32 

321 

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

323 >>> quarters_series 

324 0 2000-03-31 

325 1 2000-06-30 

326 2 2000-09-30 

327 dtype: datetime64[ns] 

328 >>> quarters_series.dt.quarter 

329 0 1 

330 1 2 

331 2 3 

332 dtype: int32 

333 

334 Returns a Series indexed like the original Series. 

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

336 """ 

337 

338 def to_pydatetime(self) -> np.ndarray: 

339 """ 

340 Return the data as an array of :class:`datetime.datetime` objects. 

341 

342 .. deprecated:: 2.1.0 

343 

344 The current behavior of dt.to_pydatetime is deprecated. 

345 In a future version this will return a Series containing python 

346 datetime objects instead of a ndarray. 

347 

348 Timezone information is retained if present. 

349 

350 .. warning:: 

351 

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

353 pandas (nanosecond). The values are truncated. 

354 

355 Returns 

356 ------- 

357 numpy.ndarray 

358 Object dtype array containing native Python datetime objects. 

359 

360 See Also 

361 -------- 

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

363 

364 Examples 

365 -------- 

366 >>> s = pd.Series(pd.date_range('20180310', periods=2)) 

367 >>> s 

368 0 2018-03-10 

369 1 2018-03-11 

370 dtype: datetime64[ns] 

371 

372 >>> s.dt.to_pydatetime() 

373 array([datetime.datetime(2018, 3, 10, 0, 0), 

374 datetime.datetime(2018, 3, 11, 0, 0)], dtype=object) 

375 

376 pandas' nanosecond precision is truncated to microseconds. 

377 

378 >>> s = pd.Series(pd.date_range('20180310', periods=2, freq='ns')) 

379 >>> s 

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

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

382 dtype: datetime64[ns] 

383 

384 >>> s.dt.to_pydatetime() 

385 array([datetime.datetime(2018, 3, 10, 0, 0), 

386 datetime.datetime(2018, 3, 10, 0, 0)], dtype=object) 

387 """ 

388 # GH#20306 

389 warnings.warn( 

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

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

392 "datetime objects instead of an ndarray. To retain the old behavior, " 

393 "call `np.array` on the result", 

394 FutureWarning, 

395 stacklevel=find_stack_level(), 

396 ) 

397 return self._get_values().to_pydatetime() 

398 

399 @property 

400 def freq(self): 

401 return self._get_values().inferred_freq 

402 

403 def isocalendar(self) -> DataFrame: 

404 """ 

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

406 

407 Returns 

408 ------- 

409 DataFrame 

410 With columns year, week and day. 

411 

412 See Also 

413 -------- 

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

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

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

417 three components: year, week and weekday. 

418 

419 Examples 

420 -------- 

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

422 >>> ser.dt.isocalendar() 

423 year week day 

424 0 2009 53 5 

425 1 <NA> <NA> <NA> 

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

427 0 53 

428 1 <NA> 

429 Name: week, dtype: UInt32 

430 """ 

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

432 

433 

434@delegate_names( 

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

436) 

437@delegate_names( 

438 delegate=TimedeltaArray, 

439 accessors=TimedeltaArray._datetimelike_methods, 

440 typ="method", 

441) 

442class TimedeltaProperties(Properties): 

443 """ 

444 Accessor object for datetimelike properties of the Series values. 

445 

446 Returns a Series indexed like the original Series. 

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

448 

449 Examples 

450 -------- 

451 >>> seconds_series = pd.Series( 

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

453 ... ) 

454 >>> seconds_series 

455 0 0 days 00:00:01 

456 1 0 days 00:00:02 

457 2 0 days 00:00:03 

458 dtype: timedelta64[ns] 

459 >>> seconds_series.dt.seconds 

460 0 1 

461 1 2 

462 2 3 

463 dtype: int32 

464 """ 

465 

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

467 """ 

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

469 

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

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

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

473 Series. 

474 

475 Returns 

476 ------- 

477 numpy.ndarray 

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

479 

480 See Also 

481 -------- 

482 datetime.timedelta : A duration expressing the difference 

483 between two date, time, or datetime. 

484 

485 Examples 

486 -------- 

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

488 >>> s 

489 0 0 days 

490 1 1 days 

491 2 2 days 

492 3 3 days 

493 4 4 days 

494 dtype: timedelta64[ns] 

495 

496 >>> s.dt.to_pytimedelta() 

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

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

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

500 """ 

501 return self._get_values().to_pytimedelta() 

502 

503 @property 

504 def components(self): 

505 """ 

506 Return a Dataframe of the components of the Timedeltas. 

507 

508 Returns 

509 ------- 

510 DataFrame 

511 

512 Examples 

513 -------- 

514 >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit='s')) 

515 >>> s 

516 0 0 days 00:00:00 

517 1 0 days 00:00:01 

518 2 0 days 00:00:02 

519 3 0 days 00:00:03 

520 4 0 days 00:00:04 

521 dtype: timedelta64[ns] 

522 >>> s.dt.components 

523 days hours minutes seconds milliseconds microseconds nanoseconds 

524 0 0 0 0 0 0 0 0 

525 1 0 0 0 1 0 0 0 

526 2 0 0 0 2 0 0 0 

527 3 0 0 0 3 0 0 0 

528 4 0 0 0 4 0 0 0 

529 """ 

530 return ( 

531 self._get_values() 

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

533 .__finalize__(self._parent) 

534 ) 

535 

536 @property 

537 def freq(self): 

538 return self._get_values().inferred_freq 

539 

540 

541@delegate_names( 

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

543) 

544@delegate_names( 

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

546) 

547class PeriodProperties(Properties): 

548 """ 

549 Accessor object for datetimelike properties of the Series values. 

550 

551 Returns a Series indexed like the original Series. 

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

553 

554 Examples 

555 -------- 

556 >>> seconds_series = pd.Series( 

557 ... pd.period_range( 

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

559 ... ) 

560 ... ) 

561 >>> seconds_series 

562 0 2000-01-01 00:00:00 

563 1 2000-01-01 00:00:01 

564 2 2000-01-01 00:00:02 

565 3 2000-01-01 00:00:03 

566 dtype: period[s] 

567 >>> seconds_series.dt.second 

568 0 0 

569 1 1 

570 2 2 

571 3 3 

572 dtype: int64 

573 

574 >>> hours_series = pd.Series( 

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

576 ... ) 

577 >>> hours_series 

578 0 2000-01-01 00:00 

579 1 2000-01-01 01:00 

580 2 2000-01-01 02:00 

581 3 2000-01-01 03:00 

582 dtype: period[h] 

583 >>> hours_series.dt.hour 

584 0 0 

585 1 1 

586 2 2 

587 3 3 

588 dtype: int64 

589 

590 >>> quarters_series = pd.Series( 

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

592 ... ) 

593 >>> quarters_series 

594 0 2000Q1 

595 1 2000Q2 

596 2 2000Q3 

597 3 2000Q4 

598 dtype: period[Q-DEC] 

599 >>> quarters_series.dt.quarter 

600 0 1 

601 1 2 

602 2 3 

603 3 4 

604 dtype: int64 

605 """ 

606 

607 

608class CombinedDatetimelikeProperties( 

609 DatetimeProperties, TimedeltaProperties, PeriodProperties 

610): 

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

612 # CombinedDatetimelikeProperties isn't really instantiated. Instead 

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

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

615 # do all the validation here. 

616 

617 if not isinstance(data, ABCSeries): 

618 raise TypeError( 

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

620 ) 

621 

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

623 if orig is not None: 

624 data = data._constructor( 

625 orig.array, 

626 name=orig.name, 

627 copy=False, 

628 dtype=orig._values.categories.dtype, 

629 index=orig.index, 

630 ) 

631 

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

633 return ArrowTemporalProperties(data, orig) 

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

635 return DatetimeProperties(data, orig) 

636 elif isinstance(data.dtype, DatetimeTZDtype): 

637 return DatetimeProperties(data, orig) 

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

639 return TimedeltaProperties(data, orig) 

640 elif isinstance(data.dtype, PeriodDtype): 

641 return PeriodProperties(data, orig) 

642 

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