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

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

252 statements  

1from __future__ import annotations 

2 

3from datetime import ( 

4 datetime, 

5 timedelta, 

6) 

7from typing import ( 

8 TYPE_CHECKING, 

9 Self, 

10) 

11import warnings 

12 

13import numpy as np 

14 

15from pandas._libs import index as libindex 

16from pandas._libs.tslibs import ( 

17 BaseOffset, 

18 Day, 

19 NaT, 

20 Period, 

21 Resolution, 

22 Tick, 

23 to_offset, 

24) 

25from pandas._libs.tslibs.dtypes import OFFSET_TO_PERIOD_FREQSTR 

26from pandas.errors import Pandas4Warning 

27from pandas.util._decorators import ( 

28 set_module, 

29) 

30from pandas.util._exceptions import find_stack_level 

31 

32from pandas.core.dtypes.common import ( 

33 is_integer, 

34 pandas_dtype, 

35) 

36from pandas.core.dtypes.dtypes import PeriodDtype 

37from pandas.core.dtypes.generic import ABCSeries 

38from pandas.core.dtypes.missing import is_valid_na_for_dtype 

39 

40from pandas.core.arrays.period import ( 

41 PeriodArray, 

42 period_array, 

43 raise_on_incompatible, 

44 validate_dtype_freq, 

45) 

46import pandas.core.common as com 

47from pandas.core.indexes.base import maybe_extract_name 

48from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin 

49from pandas.core.indexes.datetimes import ( 

50 DatetimeIndex, 

51 Index, 

52) 

53 

54if TYPE_CHECKING: 

55 from collections.abc import Hashable 

56 

57 from pandas._typing import ( 

58 Dtype, 

59 DtypeObj, 

60 npt, 

61 ) 

62 

63 

64def _new_PeriodIndex(cls, **d): 

65 # GH13277 for unpickling 

66 values = d.pop("data") 

67 if values.dtype == "int64": 

68 freq = d.pop("freq", None) 

69 dtype = PeriodDtype(freq) 

70 values = PeriodArray(values, dtype=dtype) 

71 return cls._simple_new(values, **d) 

72 else: 

73 return cls(values, **d) 

74 

75 

76@set_module("pandas") 

77class PeriodIndex(DatetimeIndexOpsMixin): 

78 """ 

79 Immutable ndarray holding ordinal values indicating regular periods in time. 

80 

81 Index keys are boxed to Period objects which carries the metadata (eg, 

82 frequency information). 

83 

84 Parameters 

85 ---------- 

86 data : array-like (1d int np.ndarray or PeriodArray), optional 

87 Optional period-like data to construct index with. 

88 freq : str or period object, optional 

89 One of pandas period strings or corresponding objects. 

90 dtype : str or PeriodDtype, default None 

91 A dtype from which to extract a freq. 

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 : str, default None 

100 Name of the resulting PeriodIndex. 

101 

102 Attributes 

103 ---------- 

104 day 

105 dayofweek 

106 day_of_week 

107 dayofyear 

108 day_of_year 

109 days_in_month 

110 daysinmonth 

111 end_time 

112 freq 

113 freqstr 

114 hour 

115 is_leap_year 

116 minute 

117 month 

118 quarter 

119 qyear 

120 second 

121 start_time 

122 week 

123 weekday 

124 weekofyear 

125 year 

126 

127 Methods 

128 ------- 

129 asfreq 

130 strftime 

131 to_timestamp 

132 from_fields 

133 from_ordinals 

134 

135 Raises 

136 ------ 

137 ValueError 

138 Passing the parameter data as a list without specifying either freq or 

139 dtype will raise a ValueError: "freq not specified and cannot be inferred" 

140 

141 See Also 

142 -------- 

143 Index : The base pandas Index type. 

144 Period : Represents a period of time. 

145 DatetimeIndex : Index with datetime64 data. 

146 TimedeltaIndex : Index of timedelta64 data. 

147 period_range : Create a fixed-frequency PeriodIndex. 

148 

149 Examples 

150 -------- 

151 >>> idx = pd.PeriodIndex(data=["2000Q1", "2002Q3"], freq="Q") 

152 >>> idx 

153 PeriodIndex(['2000Q1', '2002Q3'], dtype='period[Q-DEC]') 

154 """ 

155 

156 _typ = "periodindex" 

157 

158 _data: PeriodArray 

159 freq: BaseOffset 

160 dtype: PeriodDtype 

161 

162 _data_cls = PeriodArray 

163 _supports_partial_string_indexing = True 

164 

165 _warn_quarter: bool = False 

166 

167 @property 

168 def _engine_type(self) -> type[libindex.PeriodEngine]: 

169 return libindex.PeriodEngine 

170 

171 # -------------------------------------------------------------------- 

172 # methods that dispatch to array and wrap result in Index 

173 

174 def asfreq(self, freq=None, how: str = "E") -> Self: 

175 """ 

176 Convert the PeriodIndex to the specified frequency `freq`. 

177 

178 Equivalent to applying :meth:`pandas.Period.asfreq` with the given arguments 

179 to each :class:`~pandas.Period` in this PeriodIndex. 

180 

181 Parameters 

182 ---------- 

183 freq : str 

184 A frequency. 

185 how : {'end', 'start', 'e', 's'}, default 'end' 

186 Whether the elements should be aligned to the end or start of 

187 each period, e.g. January 31st vs. January 1st. Case-insensitive. 

188 

189 Returns 

190 ------- 

191 PeriodIndex 

192 The transformed PeriodIndex with the new frequency. 

193 

194 See Also 

195 -------- 

196 arrays.PeriodArray.asfreq: Convert each Period in a PeriodArray to 

197 the given frequency. 

198 Period.asfreq : Convert a :class:`~pandas.Period` object to the given frequency. 

199 

200 Examples 

201 -------- 

202 >>> pidx = pd.period_range("2010-01-01", "2015-01-01", freq="Y") 

203 >>> pidx 

204 PeriodIndex(['2010', '2011', '2012', '2013', '2014', '2015'], 

205 dtype='period[Y-DEC]') 

206 

207 >>> pidx.asfreq("M") 

208 PeriodIndex(['2010-12', '2011-12', '2012-12', '2013-12', '2014-12', 

209 '2015-12'], dtype='period[M]') 

210 

211 >>> pidx.asfreq("M", how="S") 

212 PeriodIndex(['2010-01', '2011-01', '2012-01', '2013-01', '2014-01', 

213 '2015-01'], dtype='period[M]') 

214 """ 

215 arr = self._data.asfreq(freq, how) 

216 return type(self)._simple_new(arr, name=self.name) 

217 

218 def to_timestamp(self, freq=None, how: str = "start") -> DatetimeIndex: 

219 """ 

220 Cast to DatetimeIndex. 

221 

222 If possible, gives microsecond-unit DatetimeIndex. Otherwise 

223 gives nanosecond unit. 

224 

225 Parameters 

226 ---------- 

227 freq : str or DateOffset, optional 

228 Target frequency. The default is 'D' for week or longer, 

229 's' otherwise. 

230 how : {'start', 'end', 's', 'e'}, default 'start' 

231 Whether to use the start or end of the time period being converted. 

232 Case-insensitive. 

233 

234 Returns 

235 ------- 

236 DatetimeIndex 

237 Timestamp representation of given Period-like object. 

238 

239 See Also 

240 -------- 

241 PeriodIndex.day : The days of the period. 

242 PeriodIndex.from_fields : Construct a PeriodIndex from fields 

243 (year, month, day, etc.). 

244 PeriodIndex.from_ordinals : Construct a PeriodIndex from ordinals. 

245 PeriodIndex.hour : The hour of the period. 

246 PeriodIndex.minute : The minute of the period. 

247 PeriodIndex.month : The month as January=1, December=12. 

248 PeriodIndex.second : The second of the period. 

249 PeriodIndex.year : The year of the period. 

250 

251 Examples 

252 -------- 

253 >>> idx = pd.PeriodIndex(["2023-01", "2023-02", "2023-03"], freq="M") 

254 >>> idx.to_timestamp() 

255 DatetimeIndex(['2023-01-01', '2023-02-01', '2023-03-01'], 

256 dtype='datetime64[us]', freq='MS') 

257 

258 The frequency will not be inferred if the index contains less than 

259 three elements, or if the values of index are not strictly monotonic: 

260 

261 >>> idx = pd.PeriodIndex(["2023-01", "2023-02"], freq="M") 

262 >>> idx.to_timestamp() 

263 DatetimeIndex(['2023-01-01', '2023-02-01'], dtype='datetime64[us]', freq=None) 

264 

265 >>> idx = pd.PeriodIndex( 

266 ... ["2023-01", "2023-02", "2023-02", "2023-03"], freq="2M" 

267 ... ) 

268 >>> idx.to_timestamp() 

269 DatetimeIndex(['2023-01-01', '2023-02-01', '2023-02-01', '2023-03-01'], 

270 dtype='datetime64[us]', freq=None) 

271 """ 

272 parr = self._data 

273 arr = parr.to_timestamp(freq, how) 

274 result = DatetimeIndex._simple_new(arr, name=self.name) 

275 result._freq = parr._to_timestamp_freq(arr, target_freq=freq, how=how) 

276 return result 

277 

278 def strftime(self, date_format: str) -> Index: 

279 """ 

280 Convert to Index using specified date_format. 

281 

282 Return an Index of formatted strings specified by date_format, which 

283 supports the same string format as the python standard library. Details 

284 of the string format can be found in `python string format 

285 doc <https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior>`__. 

286 :class:`Period` objects additionally support several directives not 

287 covered there, detailed in :meth:`Period.strftime`. 

288 

289 Parameters 

290 ---------- 

291 date_format : str 

292 Date format string (e.g. "%Y-%m-%d"). 

293 

294 Returns 

295 ------- 

296 Index 

297 Index of formatted strings. 

298 

299 See Also 

300 -------- 

301 to_datetime : Convert the given argument to datetime. 

302 Period.strftime : Format a single Period. 

303 

304 Examples 

305 -------- 

306 >>> idx = pd.PeriodIndex(["2023-01", "2023-02", "2023-03"], freq="M") 

307 >>> idx.strftime("%B %d, %Y") 

308 Index(['January 31, 2023', 'February 28, 2023', 'March 31, 2023'], dtype='str') 

309 """ 

310 arr = self._data.strftime(date_format) 

311 return Index(arr, name=self.name, dtype=arr.dtype, copy=False) 

312 

313 def _wrap_field(self, name: str) -> Index: 

314 result = getattr(self._data, name) 

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

316 

317 @property 

318 def start_time(self) -> DatetimeIndex: 

319 """ 

320 Get the Timestamp for the start of the period. 

321 

322 Returns a DatetimeIndex with the exact start Timestamp of each 

323 period in the index. 

324 

325 Returns 

326 ------- 

327 DatetimeIndex 

328 

329 See Also 

330 -------- 

331 PeriodIndex.end_time : Return the end Timestamp. 

332 PeriodIndex.to_timestamp : Cast to DatetimeIndex. 

333 Period.start_time : Return the start Timestamp for a single Period. 

334 

335 Examples 

336 -------- 

337 >>> idx = pd.PeriodIndex(["2023-01", "2023-02"], freq="M") 

338 >>> idx.start_time 

339 DatetimeIndex(['2023-01-01', '2023-02-01'], dtype='datetime64[us]', freq=None) 

340 """ 

341 return DatetimeIndex(self._data.start_time, name=self.name, copy=False) 

342 

343 @property 

344 def end_time(self) -> DatetimeIndex: 

345 """ 

346 Get the Timestamp for the end of the period. 

347 

348 Returns a DatetimeIndex with the last possible moment within each 

349 period in the index (e.g. 23:59:59.999999 for a daily period). 

350 

351 Returns 

352 ------- 

353 DatetimeIndex 

354 

355 See Also 

356 -------- 

357 PeriodIndex.start_time : Return the start Timestamp. 

358 PeriodIndex.to_timestamp : Cast to DatetimeIndex. 

359 Period.end_time : Return the end Timestamp for a single Period. 

360 

361 Examples 

362 -------- 

363 >>> idx = pd.PeriodIndex(["2023-01", "2023-02"], freq="M") 

364 >>> idx.end_time 

365 DatetimeIndex(['2023-01-31 23:59:59.999999', '2023-02-28 23:59:59.999999'], 

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

367 """ 

368 return DatetimeIndex(self._data.end_time, name=self.name, copy=False) 

369 

370 @property 

371 def year(self) -> Index: 

372 """ 

373 The year of the period. 

374 

375 Returns the year component for each period in the index. 

376 

377 See Also 

378 -------- 

379 PeriodIndex.day_of_year : The ordinal day of the year. 

380 PeriodIndex.is_leap_year : Logical indicating if the date belongs to a 

381 leap year. 

382 PeriodIndex.weekofyear : The week ordinal of the year. 

383 

384 Examples 

385 -------- 

386 >>> idx = pd.PeriodIndex(["2023", "2024", "2025"], freq="Y") 

387 >>> idx.year 

388 Index([2023, 2024, 2025], dtype='int64') 

389 """ 

390 return self._wrap_field("year") 

391 

392 @property 

393 def month(self) -> Index: 

394 """ 

395 The month as January=1, December=12. 

396 

397 Returns the month component for each period in the index as an 

398 integer, where January is 1 and December is 12. 

399 

400 See Also 

401 -------- 

402 PeriodIndex.days_in_month : The number of days in the month. 

403 

404 Examples 

405 -------- 

406 >>> idx = pd.PeriodIndex(["2023-01", "2023-02", "2023-03"], freq="M") 

407 >>> idx.month 

408 Index([1, 2, 3], dtype='int64') 

409 """ 

410 return self._wrap_field("month") 

411 

412 @property 

413 def day(self) -> Index: 

414 """ 

415 The days of the period. 

416 

417 Returns the day-of-month component for each period in the index. 

418 

419 See Also 

420 -------- 

421 PeriodIndex.day_of_week : The day of the week with Monday=0, Sunday=6. 

422 PeriodIndex.day_of_year : The ordinal day of the year. 

423 PeriodIndex.days_in_month : The number of days in the month. 

424 

425 Examples 

426 -------- 

427 >>> idx = pd.PeriodIndex(["2020-01-31", "2020-02-28"], freq="D") 

428 >>> idx.day 

429 Index([31, 28], dtype='int64') 

430 """ 

431 return self._wrap_field("day") 

432 

433 @property 

434 def hour(self) -> Index: 

435 """ 

436 The hour of the period. 

437 

438 Returns the hour component for each period in the index. 

439 

440 See Also 

441 -------- 

442 PeriodIndex.minute : The minute of the period. 

443 PeriodIndex.second : The second of the period. 

444 PeriodIndex.to_timestamp : Cast to DatetimeArray/Index. 

445 

446 Examples 

447 -------- 

448 >>> idx = pd.PeriodIndex(["2023-01-01 10:00", "2023-01-01 11:00"], freq="h") 

449 >>> idx.hour 

450 Index([10, 11], dtype='int64') 

451 """ 

452 return self._wrap_field("hour") 

453 

454 @property 

455 def minute(self) -> Index: 

456 """ 

457 The minute of the period. 

458 

459 Returns the minute component for each period in the index. 

460 

461 See Also 

462 -------- 

463 PeriodIndex.hour : The hour of the period. 

464 PeriodIndex.second : The second of the period. 

465 PeriodIndex.to_timestamp : Cast to DatetimeArray/Index. 

466 

467 Examples 

468 -------- 

469 >>> idx = pd.PeriodIndex( 

470 ... ["2023-01-01 10:30:00", "2023-01-01 11:50:00"], freq="min" 

471 ... ) 

472 >>> idx.minute 

473 Index([30, 50], dtype='int64') 

474 """ 

475 return self._wrap_field("minute") 

476 

477 @property 

478 def second(self) -> Index: 

479 """ 

480 The second of the period. 

481 

482 Returns the second component for each period in the index. 

483 

484 See Also 

485 -------- 

486 PeriodIndex.hour : The hour of the period. 

487 PeriodIndex.minute : The minute of the period. 

488 PeriodIndex.to_timestamp : Cast to DatetimeArray/Index. 

489 

490 Examples 

491 -------- 

492 >>> idx = pd.PeriodIndex( 

493 ... ["2023-01-01 10:00:30", "2023-01-01 10:00:31"], freq="s" 

494 ... ) 

495 >>> idx.second 

496 Index([30, 31], dtype='int64') 

497 """ 

498 return self._wrap_field("second") 

499 

500 @property 

501 def weekofyear(self) -> Index: 

502 """ 

503 The week ordinal of the year. 

504 

505 Returns the week number (1 through 53) for each period in the index. 

506 

507 See Also 

508 -------- 

509 PeriodIndex.day_of_week : The day of the week with Monday=0, Sunday=6. 

510 PeriodIndex.week : The week ordinal of the year. 

511 PeriodIndex.year : The year of the period. 

512 

513 Examples 

514 -------- 

515 >>> idx = pd.PeriodIndex(["2023-01", "2023-02", "2023-03"], freq="M") 

516 >>> idx.week # It can be written `weekofyear` 

517 Index([5, 9, 13], dtype='int64') 

518 """ 

519 return self._wrap_field("weekofyear") 

520 

521 week = weekofyear 

522 

523 @property 

524 def day_of_week(self) -> Index: 

525 """ 

526 The day of the week with Monday=0, Sunday=6. 

527 

528 Returns the day-of-week component for each period, following the 

529 Python convention where Monday is 0 and Sunday is 6. 

530 

531 See Also 

532 -------- 

533 PeriodIndex.day : The days of the period. 

534 PeriodIndex.day_of_year : The ordinal day of the year. 

535 PeriodIndex.week : The week ordinal of the year. 

536 PeriodIndex.weekofyear : The week ordinal of the year. 

537 

538 Examples 

539 -------- 

540 >>> idx = pd.PeriodIndex(["2023-01-01", "2023-01-02", "2023-01-03"], freq="D") 

541 >>> idx.day_of_week 

542 Index([6, 0, 1], dtype='int64') 

543 """ 

544 return self._wrap_field("day_of_week") 

545 

546 @property 

547 def weekday(self) -> Index: 

548 """ 

549 The day of the week with Monday=0, Sunday=6. 

550 

551 .. deprecated:: 3.1.0 

552 Use :attr:`PeriodIndex.day_of_week` instead. 

553 """ 

554 warnings.warn( 

555 "PeriodIndex.weekday is deprecated and will be removed " 

556 "in a future version. Use PeriodIndex.day_of_week instead.", 

557 Pandas4Warning, 

558 stacklevel=find_stack_level(), 

559 ) 

560 return self._wrap_field("day_of_week") 

561 

562 @property 

563 def dayofweek(self) -> Index: 

564 """ 

565 The day of the week with Monday=0, Sunday=6. 

566 

567 .. deprecated:: 3.1.0 

568 Use :attr:`PeriodIndex.day_of_week` instead. 

569 """ 

570 warnings.warn( 

571 "PeriodIndex.dayofweek is deprecated and will be removed " 

572 "in a future version. Use PeriodIndex.day_of_week instead.", 

573 Pandas4Warning, 

574 stacklevel=find_stack_level(), 

575 ) 

576 return self._wrap_field("day_of_week") 

577 

578 @property 

579 def day_of_year(self) -> Index: 

580 """ 

581 The ordinal day of the year. 

582 

583 Returns the day-of-year component for each period, ranging from 

584 1 (January 1st) to 365 or 366 for leap years. 

585 

586 See Also 

587 -------- 

588 PeriodIndex.day : The days of the period. 

589 PeriodIndex.day_of_week : The day of the week with Monday=0, Sunday=6. 

590 PeriodIndex.weekofyear : The week ordinal of the year. 

591 PeriodIndex.year : The year of the period. 

592 

593 Examples 

594 -------- 

595 >>> idx = pd.PeriodIndex(["2023-01-10", "2023-02-01", "2023-03-01"], freq="D") 

596 >>> idx.day_of_year 

597 Index([10, 32, 60], dtype='int64') 

598 

599 >>> idx = pd.PeriodIndex(["2023", "2024", "2025"], freq="Y") 

600 >>> idx 

601 PeriodIndex(['2023', '2024', '2025'], dtype='period[Y-DEC]') 

602 >>> idx.day_of_year 

603 Index([365, 366, 365], dtype='int64') 

604 """ 

605 return self._wrap_field("day_of_year") 

606 

607 @property 

608 def dayofyear(self) -> Index: 

609 """ 

610 The ordinal day of the year. 

611 

612 .. deprecated:: 3.1.0 

613 Use :attr:`PeriodIndex.day_of_year` instead. 

614 """ 

615 warnings.warn( 

616 "PeriodIndex.dayofyear is deprecated and will be removed in a " 

617 "future version. Use PeriodIndex.day_of_year instead.", 

618 Pandas4Warning, 

619 stacklevel=find_stack_level(), 

620 ) 

621 return self._wrap_field("day_of_year") 

622 

623 @property 

624 def quarter(self) -> Index: 

625 """ 

626 The quarter of the date. 

627 

628 Returns the quarter (1 through 4) for each period in the index. 

629 

630 See Also 

631 -------- 

632 PeriodIndex.qyear : Fiscal year the Period lies in according to its 

633 starting-quarter. 

634 

635 Examples 

636 -------- 

637 >>> idx = pd.PeriodIndex(["2023-01", "2023-02", "2023-03"], freq="M") 

638 >>> idx.quarter 

639 Index([1, 1, 1], dtype='int64') 

640 """ 

641 return self._wrap_field("quarter") 

642 

643 @property 

644 def qyear(self) -> Index: 

645 """ 

646 Fiscal year the Period lies in according to its starting-quarter. 

647 

648 The `year` and the `qyear` of the period will be the same if the fiscal 

649 and calendar years are the same. When they are not, the fiscal year 

650 can be different from the calendar year of the period. 

651 

652 Returns 

653 ------- 

654 Index 

655 The fiscal year of each period. 

656 

657 See Also 

658 -------- 

659 PeriodIndex.quarter : The quarter of the date. 

660 PeriodIndex.year : The year of the period. 

661 

662 Examples 

663 -------- 

664 If the natural and fiscal year are the same, `qyear` and `year` will 

665 be the same. 

666 

667 >>> idx = pd.PeriodIndex(["2018Q1"], freq="Q") 

668 >>> idx.qyear 

669 Index([2018], dtype='int64') 

670 >>> idx.year 

671 Index([2018], dtype='int64') 

672 

673 If the fiscal year starts in April (`Q-MAR`), the first quarter of 

674 2018 will start in April 2017. `year` will then be 2017, but `qyear` 

675 will be the fiscal year, 2018. 

676 

677 >>> idx = pd.PeriodIndex(["2018Q1"], freq="Q-MAR") 

678 >>> idx.start_time 

679 DatetimeIndex(['2017-04-01'], dtype='datetime64[us]', freq=None) 

680 >>> idx.qyear 

681 Index([2018], dtype='int64') 

682 >>> idx.year 

683 Index([2017], dtype='int64') 

684 """ 

685 return self._wrap_field("qyear") 

686 

687 @property 

688 def days_in_month(self) -> Index: 

689 """ 

690 The number of days in the month. 

691 

692 Returns the total number of days in the month of each period, 

693 accounting for leap years. 

694 

695 See Also 

696 -------- 

697 PeriodIndex.day : The days of the period. 

698 PeriodIndex.month : The month as January=1, December=12. 

699 

700 Examples 

701 -------- 

702 >>> idx = pd.PeriodIndex(["2023-01", "2023-02", "2023-03"], freq="M") 

703 >>> idx.days_in_month 

704 Index([31, 28, 31], dtype='int64') 

705 """ 

706 return self._wrap_field("days_in_month") 

707 

708 @property 

709 def daysinmonth(self) -> Index: 

710 """ 

711 The number of days in the month. 

712 

713 .. deprecated:: 3.1.0 

714 Use :attr:`PeriodIndex.days_in_month` instead. 

715 """ 

716 warnings.warn( 

717 "PeriodIndex.daysinmonth is deprecated and will be removed in a " 

718 "future version. Use PeriodIndex.days_in_month instead.", 

719 Pandas4Warning, 

720 stacklevel=find_stack_level(), 

721 ) 

722 return self._wrap_field("days_in_month") 

723 

724 @property 

725 def is_leap_year(self) -> npt.NDArray[np.bool_]: 

726 """ 

727 Logical indicating if the date belongs to a leap year. 

728 

729 Returns a boolean array where ``True`` indicates the period's year 

730 is a leap year. 

731 

732 See Also 

733 -------- 

734 PeriodIndex.qyear : Fiscal year the Period lies in according to its 

735 starting-quarter. 

736 PeriodIndex.year : The year of the period. 

737 

738 Examples 

739 -------- 

740 >>> idx = pd.PeriodIndex(["2023", "2024", "2025"], freq="Y") 

741 >>> idx.is_leap_year 

742 array([False, True, False]) 

743 """ 

744 return self._data.is_leap_year 

745 

746 # ------------------------------------------------------------------------ 

747 # Index Constructors 

748 

749 def __new__( 

750 cls, 

751 data=None, 

752 freq=None, 

753 dtype: Dtype | None = None, 

754 copy: bool | None = None, 

755 name: Hashable | None = None, 

756 ) -> Self: 

757 refs = None 

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

759 refs = data._references 

760 if dtype is not None: 

761 dtype = pandas_dtype(dtype) 

762 

763 name = maybe_extract_name(name, data, cls) 

764 

765 if freq is not None: 

766 freq = to_offset(freq, is_period=True) 

767 dtype2 = PeriodDtype(freq) if freq is not None else None 

768 dtype = validate_dtype_freq(dtype, dtype2) 

769 if dtype is not None: 

770 freq = dtype._freq 

771 

772 # GH#63388 

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

774 

775 data = period_array(data=data, dtype=dtype) 

776 

777 if copy: 

778 data = data.copy() 

779 

780 return cls._simple_new(data, name=name, refs=refs) 

781 

782 @classmethod 

783 def from_fields( 

784 cls, 

785 *, 

786 year=None, 

787 quarter=None, 

788 month=None, 

789 day=None, 

790 hour=None, 

791 minute=None, 

792 second=None, 

793 freq=None, 

794 ) -> Self: 

795 """ 

796 Construct a PeriodIndex from fields (year, month, day, etc.). 

797 

798 Each field (year, quarter, month, day, hour, minute, second) can be 

799 specified as a scalar or list-like. At least one field must be 

800 list-like; scalar fields are broadcast to its length. The frequency 

801 is inferred from the fields provided or can be given explicitly. 

802 

803 Parameters 

804 ---------- 

805 year : int or list-like, default None 

806 Year for the PeriodIndex. 

807 quarter : int or list-like, default None 

808 Quarter for the PeriodIndex. 

809 month : int or list-like, default None 

810 Month for the PeriodIndex. 

811 day : int or list-like, default None 

812 Day for the PeriodIndex. 

813 hour : int or list-like, default None 

814 Hour for the PeriodIndex. 

815 minute : int or list-like, default None 

816 Minute for the PeriodIndex. 

817 second : int or list-like, default None 

818 Second for the PeriodIndex. 

819 freq : str or period object, optional 

820 One of pandas period strings or corresponding objects. 

821 

822 Returns 

823 ------- 

824 PeriodIndex 

825 

826 See Also 

827 -------- 

828 PeriodIndex.from_ordinals : Construct a PeriodIndex from ordinals. 

829 PeriodIndex.to_timestamp : Cast to DatetimeArray/Index. 

830 

831 Notes 

832 ----- 

833 A list-like field must be a ``list``, ``tuple``, ``np.ndarray``, or 

834 ``Series``. Other array-like inputs, such as a ``range`` or an 

835 ``Index``, are not accepted. 

836 

837 Examples 

838 -------- 

839 >>> idx = pd.PeriodIndex.from_fields(year=[2000, 2002], quarter=[1, 3]) 

840 >>> idx 

841 PeriodIndex(['2000Q1', '2002Q3'], dtype='period[Q-DEC]') 

842 """ 

843 fields = { 

844 "year": year, 

845 "quarter": quarter, 

846 "month": month, 

847 "day": day, 

848 "hour": hour, 

849 "minute": minute, 

850 "second": second, 

851 } 

852 fields = {key: value for key, value in fields.items() if value is not None} 

853 arr = PeriodArray._from_fields(fields=fields, freq=freq) 

854 return cls._simple_new(arr) 

855 

856 @classmethod 

857 def from_ordinals(cls, ordinals, *, freq, name=None) -> Self: 

858 """ 

859 Construct a PeriodIndex from ordinals. 

860 

861 Ordinals are integer offsets from the proleptic Gregorian epoch, 

862 interpreted according to the given frequency. 

863 

864 Parameters 

865 ---------- 

866 ordinals : array-like of int 

867 The period offsets from the proleptic Gregorian epoch. 

868 freq : str or period object 

869 One of pandas period strings or corresponding objects. 

870 name : str, default None 

871 Name of the resulting PeriodIndex. 

872 

873 Returns 

874 ------- 

875 PeriodIndex 

876 

877 See Also 

878 -------- 

879 PeriodIndex.from_fields : Construct a PeriodIndex from fields 

880 (year, month, day, etc.). 

881 PeriodIndex.to_timestamp : Cast to DatetimeArray/Index. 

882 

883 Examples 

884 -------- 

885 >>> idx = pd.PeriodIndex.from_ordinals([-1, 0, 1], freq="Q") 

886 >>> idx 

887 PeriodIndex(['1969Q4', '1970Q1', '1970Q2'], dtype='period[Q-DEC]') 

888 """ 

889 ordinals = np.asarray(ordinals, dtype=np.int64) 

890 dtype = PeriodDtype(freq) 

891 data = PeriodArray._simple_new(ordinals, dtype=dtype) 

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

893 

894 # ------------------------------------------------------------------------ 

895 # Data 

896 

897 @property 

898 def values(self) -> npt.NDArray[np.object_]: 

899 warnings.warn( 

900 "PeriodIndex.values returning an object-dtype ndarray is " 

901 "deprecated. In a future version, this will return the " 

902 "underlying PeriodArray instead. Use 'PeriodIndex.to_numpy()' " 

903 "to get a NumPy array, or 'PeriodIndex.array' to get the " 

904 "ExtensionArray.", 

905 Pandas4Warning, 

906 stacklevel=2, 

907 ) 

908 return np.asarray(self, dtype=object) 

909 

910 def _mpl_repr(self) -> np.ndarray: 

911 # Return ordinals directly so matplotlib receives numeric x-values, 

912 # bypassing a round-trip through Period scalar objects. GH#10578 

913 return self.asi8 

914 

915 def _maybe_convert_timedelta(self, other) -> int | npt.NDArray[np.int64]: 

916 """ 

917 Convert timedelta-like input to an integer multiple of self.freq 

918 

919 Parameters 

920 ---------- 

921 other : timedelta, np.timedelta64, DateOffset, int, np.ndarray 

922 

923 Returns 

924 ------- 

925 converted : int, np.ndarray[int64] 

926 

927 Raises 

928 ------ 

929 IncompatibleFrequency : if the input cannot be written as a multiple 

930 of self.freq. Note IncompatibleFrequency subclasses ValueError. 

931 """ 

932 if isinstance(other, (timedelta, np.timedelta64, Tick, np.ndarray)): 

933 if isinstance(self.freq, (Tick, Day)): 

934 # _check_timedeltalike_freq_compat will raise if incompatible 

935 delta = self._data._check_timedeltalike_freq_compat(other) 

936 return delta 

937 elif isinstance(other, BaseOffset): 

938 if other.base == self.freq.base: 

939 return other.n 

940 

941 raise raise_on_incompatible(self, other) 

942 elif is_integer(other): 

943 assert isinstance(other, int) 

944 return other 

945 

946 # raise when input doesn't have freq 

947 raise raise_on_incompatible(self, None) 

948 

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

950 """ 

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

952 """ 

953 return self.dtype == dtype 

954 

955 # ------------------------------------------------------------------------ 

956 # Index Methods 

957 

958 def asof_locs(self, where: Index, mask: npt.NDArray[np.bool_]) -> np.ndarray: 

959 """ 

960 where : array of timestamps 

961 mask : np.ndarray[bool] 

962 Array of booleans where data is not NA. 

963 """ 

964 if isinstance(where, DatetimeIndex): 

965 where = PeriodIndex(where._values, freq=self.freq, copy=False) 

966 elif not isinstance(where, PeriodIndex): 

967 raise TypeError("asof_locs `where` must be DatetimeIndex or PeriodIndex") 

968 

969 return super().asof_locs(where, mask) 

970 

971 @property 

972 def is_full(self) -> bool: 

973 """ 

974 Return True if the index contains all periods from start to end 

975 (inclusive) with no gaps. 

976 

977 Requires monotonic increasing order. Duplicate periods are allowed. 

978 

979 .. deprecated:: 3.1.0 

980 ``PeriodIndex.is_full`` is deprecated and will be removed in 

981 a future version. Use 

982 ``index.empty or len(index.unique()) == 

983 len(period_range(index.min(), index.max(), freq=index.freq))`` 

984 instead. Unlike ``is_full``, this does not raise on a 

985 non-monotonic index. 

986 """ 

987 warnings.warn( 

988 "PeriodIndex.is_full is deprecated and will be removed in a " 

989 "future version. Use index.empty or len(index.unique()) == " 

990 "len(period_range(index.min(), index.max(), freq=index.freq)) " 

991 "instead.", 

992 Pandas4Warning, 

993 stacklevel=find_stack_level(), 

994 ) 

995 if len(self) == 0: 

996 return True 

997 if not self.is_monotonic_increasing: 

998 raise ValueError("Index is not monotonic") 

999 values = self.asi8 

1000 return bool(((values[1:] - values[:-1]) < 2).all()) 

1001 

1002 @property 

1003 def inferred_type(self) -> str: 

1004 # b/c data is represented as ints make sure we can't have ambiguous 

1005 # indexing 

1006 return "period" 

1007 

1008 # ------------------------------------------------------------------------ 

1009 # Indexing Methods 

1010 

1011 def _convert_tolerance(self, tolerance, target): 

1012 # Returned tolerance must be in dtype/units so that 

1013 # `|self._get_engine_target() - target._engine_target()| <= tolerance` 

1014 # is meaningful. Since PeriodIndex returns int64 for engine_target, 

1015 # we may need to convert timedelta64 tolerance to int64. 

1016 tolerance = super()._convert_tolerance(tolerance, target) 

1017 

1018 if self.dtype == target.dtype: 

1019 # convert tolerance to i8 

1020 tolerance = self._maybe_convert_timedelta(tolerance) 

1021 

1022 return tolerance 

1023 

1024 def get_loc(self, key): 

1025 """ 

1026 Get integer location for requested label. 

1027 

1028 Parameters 

1029 ---------- 

1030 key : Period, NaT, str, or datetime 

1031 String or datetime key must be parsable as Period. 

1032 

1033 Returns 

1034 ------- 

1035 loc : int or ndarray[int64] 

1036 

1037 Raises 

1038 ------ 

1039 KeyError 

1040 Key is not present in the index. 

1041 TypeError 

1042 If key is listlike or otherwise not hashable. 

1043 """ 

1044 orig_key = key 

1045 

1046 self._check_indexing_error(key) 

1047 

1048 if is_valid_na_for_dtype(key, self.dtype): 

1049 key = NaT 

1050 

1051 elif isinstance(key, str): 

1052 try: 

1053 parsed, reso = self._parse_with_reso(key) 

1054 except ValueError as err: 

1055 # A string with invalid format 

1056 raise KeyError(f"Cannot interpret '{key}' as period") from err 

1057 

1058 if self._can_partial_date_slice(reso): 

1059 try: 

1060 return self._partial_date_slice(reso, parsed) 

1061 except KeyError as err: 

1062 raise KeyError(key) from err 

1063 

1064 if reso == self._resolution_obj: 

1065 # the reso < self._resolution_obj case goes 

1066 # through _get_string_slice 

1067 key = self._cast_partial_indexing_scalar(parsed) 

1068 else: 

1069 raise KeyError(key) 

1070 

1071 elif isinstance(key, Period): 

1072 self._disallow_mismatched_indexing(key) 

1073 

1074 elif isinstance(key, datetime): 

1075 key = self._cast_partial_indexing_scalar(key) 

1076 

1077 else: 

1078 # in particular integer, which Period constructor would cast to string 

1079 raise KeyError(key) 

1080 

1081 try: 

1082 return Index.get_loc(self, key) 

1083 except KeyError as err: 

1084 raise KeyError(orig_key) from err 

1085 

1086 def _disallow_mismatched_indexing(self, key: Period) -> None: 

1087 if key._dtype != self.dtype: 

1088 raise KeyError(key) 

1089 

1090 def _cast_partial_indexing_scalar(self, label: datetime) -> Period: 

1091 try: 

1092 period = Period(label, freq=self.freq) 

1093 except ValueError as err: 

1094 # we cannot construct the Period 

1095 raise KeyError(label) from err 

1096 return period 

1097 

1098 def _maybe_cast_slice_bound(self, label, side: str): 

1099 """ 

1100 If label is a string, cast it to scalar type according to resolution. 

1101 

1102 Parameters 

1103 ---------- 

1104 label : object 

1105 side : {'left', 'right'} 

1106 

1107 Returns 

1108 ------- 

1109 label : object 

1110 

1111 Notes 

1112 ----- 

1113 Value of `side` parameter should be validated in caller. 

1114 """ 

1115 if isinstance(label, datetime): 

1116 label = self._cast_partial_indexing_scalar(label) 

1117 

1118 return super()._maybe_cast_slice_bound(label, side) 

1119 

1120 def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime): 

1121 freq = OFFSET_TO_PERIOD_FREQSTR.get(reso.attr_abbrev, reso.attr_abbrev) 

1122 iv = Period(parsed, freq=freq) 

1123 return (iv.asfreq(self.freq, how="start"), iv.asfreq(self.freq, how="end")) 

1124 

1125 def shift(self, periods: int = 1, freq=None) -> Self: 

1126 """ 

1127 Shift index by desired number of time frequency increments. 

1128 

1129 This method is for shifting the values of datetime-like indexes 

1130 by a specified time increment a given number of times. 

1131 

1132 Parameters 

1133 ---------- 

1134 periods : int, default 1 

1135 Number of periods (or increments) to shift by, 

1136 can be positive or negative. 

1137 freq : pandas.DateOffset, pandas.Timedelta or string, optional 

1138 Frequency increment to shift by. 

1139 If None, the index is shifted by its own `freq` attribute. 

1140 Offset aliases are valid strings, e.g., 'D', 'W', 'M' etc. 

1141 

1142 Returns 

1143 ------- 

1144 pandas.DatetimeIndex 

1145 Shifted index. 

1146 

1147 See Also 

1148 -------- 

1149 Index.shift : Shift values of Index. 

1150 PeriodIndex.shift : Shift values of PeriodIndex. 

1151 """ 

1152 if freq is not None: 

1153 raise TypeError( 

1154 f"`freq` argument is not supported for {type(self).__name__}.shift" 

1155 ) 

1156 return self + periods 

1157 

1158 

1159@set_module("pandas") 

1160def period_range( 

1161 start=None, 

1162 end=None, 

1163 periods: int | None = None, 

1164 freq=None, 

1165 name: Hashable | None = None, 

1166) -> PeriodIndex: 

1167 """ 

1168 Return a fixed frequency PeriodIndex. 

1169 

1170 The day (calendar) is the default frequency. 

1171 

1172 Parameters 

1173 ---------- 

1174 start : str, datetime, date, pandas.Timestamp, or period-like, default None 

1175 Left bound for generating periods. 

1176 end : str, datetime, date, pandas.Timestamp, or period-like, default None 

1177 Right bound for generating periods. 

1178 periods : int, default None 

1179 Number of periods to generate. 

1180 freq : str or DateOffset, optional 

1181 Frequency alias. By default the freq is taken from `start` or `end` 

1182 if those are Period objects. Otherwise, the default is ``"D"`` for 

1183 daily frequency. 

1184 name : str, default None 

1185 Name of the resulting PeriodIndex. 

1186 

1187 Returns 

1188 ------- 

1189 PeriodIndex 

1190 A PeriodIndex of fixed frequency periods. 

1191 

1192 See Also 

1193 -------- 

1194 date_range : Returns a fixed frequency DatetimeIndex. 

1195 Period : Represents a period of time. 

1196 PeriodIndex : Immutable ndarray holding ordinal values indicating regular periods 

1197 in time. 

1198 

1199 Notes 

1200 ----- 

1201 Of the three parameters: ``start``, ``end``, and ``periods``, exactly two 

1202 must be specified. 

1203 

1204 To learn more about the frequency strings, please see 

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

1206 

1207 Examples 

1208 -------- 

1209 >>> pd.period_range(start="2017-01-01", end="2018-01-01", freq="M") 

1210 PeriodIndex(['2017-01', '2017-02', '2017-03', '2017-04', '2017-05', '2017-06', 

1211 '2017-07', '2017-08', '2017-09', '2017-10', '2017-11', '2017-12', 

1212 '2018-01'], 

1213 dtype='period[M]') 

1214 

1215 If ``start`` or ``end`` are ``Period`` objects, they will be used as anchor 

1216 endpoints for a ``PeriodIndex`` with frequency matching that of the 

1217 ``period_range`` constructor. 

1218 

1219 >>> pd.period_range( 

1220 ... start=pd.Period("2017Q1", freq="Q"), 

1221 ... end=pd.Period("2017Q2", freq="Q"), 

1222 ... freq="M", 

1223 ... ) 

1224 PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'], 

1225 dtype='period[M]') 

1226 """ 

1227 if com.count_not_none(start, end, periods) != 2: 

1228 raise ValueError( 

1229 "Of the three parameters: start, end, and periods, " 

1230 "exactly two must be specified" 

1231 ) 

1232 if freq is None and (not isinstance(start, Period) and not isinstance(end, Period)): 

1233 freq = "D" 

1234 

1235 data, freq = PeriodArray._generate_range(start, end, periods, freq) 

1236 dtype = PeriodDtype(freq) 

1237 data = PeriodArray(data, dtype=dtype) 

1238 return PeriodIndex(data, name=name, copy=False)