Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/base.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

281 statements  

1""" 

2Base and utility classes for pandas objects. 

3""" 

4 

5from __future__ import annotations 

6 

7from typing import ( 

8 TYPE_CHECKING, 

9 Any, 

10 Generic, 

11 Literal, 

12 Self, 

13 cast, 

14 final, 

15 overload, 

16) 

17 

18import numpy as np 

19 

20from pandas._libs import lib 

21from pandas._typing import ( 

22 AxisInt, 

23 DtypeObj, 

24 IndexLabel, 

25 NDFrameT, 

26 Shape, 

27 npt, 

28) 

29from pandas.compat import PYPY 

30from pandas.compat.numpy import function as nv 

31from pandas.errors import AbstractMethodError 

32from pandas.util._decorators import cache_readonly 

33 

34from pandas.core.dtypes.cast import can_hold_element 

35from pandas.core.dtypes.common import ( 

36 is_object_dtype, 

37 is_scalar, 

38) 

39from pandas.core.dtypes.dtypes import ExtensionDtype 

40from pandas.core.dtypes.generic import ( 

41 ABCDataFrame, 

42 ABCIndex, 

43 ABCMultiIndex, 

44 ABCSeries, 

45) 

46from pandas.core.dtypes.missing import ( 

47 isna, 

48 remove_na_arraylike, 

49) 

50 

51from pandas.core import ( 

52 algorithms, 

53 nanops, 

54 ops, 

55) 

56from pandas.core.accessor import DirNamesMixin 

57from pandas.core.arraylike import OpsMixin 

58from pandas.core.arrays import ExtensionArray 

59from pandas.core.construction import ( 

60 ensure_wrapped_if_datetimelike, 

61 extract_array, 

62) 

63 

64if TYPE_CHECKING: 

65 from collections.abc import ( 

66 Hashable, 

67 Iterator, 

68 ) 

69 

70 from pandas._typing import ( 

71 DropKeep, 

72 NumpySorter, 

73 NumpyValueArrayLike, 

74 ScalarLike_co, 

75 ) 

76 

77 from pandas import ( 

78 DataFrame, 

79 Index, 

80 Series, 

81 ) 

82 

83 

84class PandasObject(DirNamesMixin): 

85 """ 

86 Base class for various pandas objects. 

87 """ 

88 

89 # results from calls to methods decorated with cache_readonly get added to _cache 

90 _cache: dict[str, Any] 

91 

92 @property 

93 def _constructor(self) -> type[Self]: 

94 """ 

95 Class constructor (for this class it's just `__class__`). 

96 """ 

97 return type(self) 

98 

99 def __repr__(self) -> str: 

100 """ 

101 Return a string representation for a particular object. 

102 """ 

103 # Should be overwritten by base classes 

104 return object.__repr__(self) 

105 

106 def _reset_cache(self, key: str | None = None) -> None: 

107 """ 

108 Reset cached properties. If ``key`` is passed, only clears that key. 

109 """ 

110 if not hasattr(self, "_cache"): 

111 return 

112 if key is None: 

113 self._cache.clear() 

114 else: 

115 self._cache.pop(key, None) 

116 

117 def __sizeof__(self) -> int: 

118 """ 

119 Generates the total memory usage for an object that returns 

120 either a value or Series of values 

121 """ 

122 memory_usage = getattr(self, "memory_usage", None) 

123 if memory_usage: 

124 mem = memory_usage(deep=True) 

125 return int(mem if is_scalar(mem) else mem.sum()) 

126 

127 # no memory_usage attribute, so fall back to object's 'sizeof' 

128 return super().__sizeof__() 

129 

130 

131class NoNewAttributesMixin: 

132 """ 

133 Mixin which prevents adding new attributes. 

134 

135 Prevents additional attributes via xxx.attribute = "something" after a 

136 call to `self.__freeze()`. Mainly used to prevent the user from using 

137 wrong attributes on an accessor (`Series.cat/.str/.dt`). 

138 

139 If you really want to add a new attribute at a later time, you need to use 

140 `object.__setattr__(self, key, value)`. 

141 """ 

142 

143 def _freeze(self) -> None: 

144 """ 

145 Prevents setting additional attributes. 

146 """ 

147 object.__setattr__(self, "__frozen", True) 

148 

149 # prevent adding any attribute via s.xxx.new_attribute = ... 

150 def __setattr__(self, key: str, value) -> None: 

151 # _cache is used by a decorator 

152 # We need to check both 1.) cls.__dict__ and 2.) getattr(self, key) 

153 # because 

154 # 1.) getattr is false for attributes that raise errors 

155 # 2.) cls.__dict__ doesn't traverse into base classes 

156 if getattr(self, "__frozen", False) and not ( 

157 key == "_cache" 

158 or key in type(self).__dict__ 

159 or getattr(self, key, None) is not None 

160 ): 

161 raise AttributeError(f"You cannot add any new attribute '{key}'") 

162 object.__setattr__(self, key, value) 

163 

164 

165class SelectionMixin(Generic[NDFrameT]): 

166 """ 

167 mixin implementing the selection & aggregation interface on a group-like 

168 object sub-classes need to define: obj, exclusions 

169 """ 

170 

171 obj: NDFrameT 

172 _selection: IndexLabel | None = None 

173 exclusions: frozenset[Hashable] 

174 _internal_names = ["_cache", "__setstate__"] 

175 _internal_names_set = set(_internal_names) 

176 

177 @final 

178 @property 

179 def _selection_list(self): 

180 if not isinstance( 

181 self._selection, (list, tuple, ABCSeries, ABCIndex, np.ndarray) 

182 ): 

183 return [self._selection] 

184 return self._selection 

185 

186 @cache_readonly 

187 def _selected_obj(self): 

188 if self._selection is None or isinstance(self.obj, ABCSeries): 

189 return self.obj 

190 else: 

191 return self.obj[self._selection] 

192 

193 @final 

194 @cache_readonly 

195 def ndim(self) -> int: 

196 return self._selected_obj.ndim 

197 

198 @final 

199 @cache_readonly 

200 def _obj_with_exclusions(self): 

201 if isinstance(self.obj, ABCSeries): 

202 return self.obj 

203 

204 if self._selection is not None: 

205 return self.obj[self._selection_list] 

206 

207 if len(self.exclusions) > 0: 

208 # equivalent to `self.obj.drop(self.exclusions, axis=1) 

209 # but this avoids consolidating and making a copy 

210 # TODO: following GH#45287 can we now use .drop directly without 

211 # making a copy? 

212 return self.obj._drop_axis(self.exclusions, axis=1, only_slice=True) 

213 else: 

214 return self.obj 

215 

216 def __getitem__(self, key): 

217 if self._selection is not None: 

218 raise IndexError(f"Column(s) {self._selection} already selected") 

219 

220 if isinstance(key, (list, tuple, ABCSeries, ABCIndex, np.ndarray)): 

221 if len(self.obj.columns.intersection(key)) != len(set(key)): 

222 bad_keys = list(set(key).difference(self.obj.columns)) 

223 raise KeyError(f"Columns not found: {str(bad_keys)[1:-1]}") 

224 return self._gotitem(list(key), ndim=2) 

225 

226 else: 

227 if key not in self.obj: 

228 raise KeyError(f"Column not found: {key}") 

229 ndim = self.obj[key].ndim 

230 return self._gotitem(key, ndim=ndim) 

231 

232 def _gotitem(self, key, ndim: int, subset=None): 

233 """ 

234 sub-classes to define 

235 return a sliced object 

236 

237 Parameters 

238 ---------- 

239 key : str / list of selections 

240 ndim : {1, 2} 

241 requested ndim of result 

242 subset : object, default None 

243 subset to act on 

244 """ 

245 raise AbstractMethodError(self) 

246 

247 @final 

248 def _infer_selection(self, key, subset: Series | DataFrame): 

249 """ 

250 Infer the `selection` to pass to our constructor in _gotitem. 

251 """ 

252 # Shared by Rolling and Resample 

253 selection = None 

254 if subset.ndim == 2 and ( 

255 (lib.is_scalar(key) and key in subset) or lib.is_list_like(key) 

256 ): 

257 selection = key 

258 elif subset.ndim == 1 and lib.is_scalar(key) and key == subset.name: 

259 selection = key 

260 return selection 

261 

262 def aggregate(self, func, *args, **kwargs): 

263 raise AbstractMethodError(self) 

264 

265 agg = aggregate 

266 

267 

268class IndexOpsMixin(OpsMixin): 

269 """ 

270 Common ops mixin to support a unified interface / docs for Series / Index 

271 """ 

272 

273 # ndarray compatibility 

274 __array_priority__ = 1000 

275 _hidden_attrs: frozenset[str] = frozenset( 

276 ["tolist"] # tolist is not deprecated, just suppressed in the __dir__ 

277 ) 

278 

279 @property 

280 def dtype(self) -> DtypeObj: 

281 # must be defined here as a property for mypy 

282 raise AbstractMethodError(self) 

283 

284 @property 

285 def _values(self) -> ExtensionArray | np.ndarray: 

286 # must be defined here as a property for mypy 

287 raise AbstractMethodError(self) 

288 

289 @final 

290 def transpose(self, *args, **kwargs) -> Self: 

291 """ 

292 Return the transpose, which is by definition self. 

293 

294 Returns 

295 ------- 

296 %(klass)s 

297 """ 

298 nv.validate_transpose(args, kwargs) 

299 return self 

300 

301 T = property( 

302 transpose, 

303 doc=""" 

304 Return the transpose, which is by definition self. 

305 

306 See Also 

307 -------- 

308 Index : Immutable sequence used for indexing and alignment. 

309 

310 Examples 

311 -------- 

312 For Series: 

313 

314 >>> s = pd.Series(['Ant', 'Bear', 'Cow']) 

315 >>> s 

316 0 Ant 

317 1 Bear 

318 2 Cow 

319 dtype: str 

320 >>> s.T 

321 0 Ant 

322 1 Bear 

323 2 Cow 

324 dtype: str 

325 

326 For Index: 

327 

328 >>> idx = pd.Index([1, 2, 3]) 

329 >>> idx.T 

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

331 """, 

332 ) 

333 

334 @property 

335 def shape(self) -> Shape: 

336 """ 

337 Return a tuple of the shape of the underlying data. 

338 

339 See Also 

340 -------- 

341 Series.ndim : Number of dimensions of the underlying data. 

342 Series.size : Return the number of elements in the underlying data. 

343 Series.nbytes : Return the number of bytes in the underlying data. 

344 

345 Examples 

346 -------- 

347 >>> s = pd.Series([1, 2, 3]) 

348 >>> s.shape 

349 (3,) 

350 """ 

351 return self._values.shape 

352 

353 def __len__(self) -> int: 

354 # We need this defined here for mypy 

355 raise AbstractMethodError(self) 

356 

357 # Temporarily avoid using `-> Literal[1]:` because of an IPython (jedi) bug 

358 # https://github.com/ipython/ipython/issues/14412 

359 # https://github.com/davidhalter/jedi/issues/1990 

360 @property 

361 def ndim(self) -> int: 

362 """ 

363 Number of dimensions of the underlying data, by definition 1. 

364 

365 See Also 

366 -------- 

367 Series.size: Return the number of elements in the underlying data. 

368 Series.shape: Return a tuple of the shape of the underlying data. 

369 Series.dtype: Return the dtype object of the underlying data. 

370 Series.values: Return Series as ndarray or ndarray-like depending on the dtype. 

371 

372 Examples 

373 -------- 

374 >>> s = pd.Series(["Ant", "Bear", "Cow"]) 

375 >>> s 

376 0 Ant 

377 1 Bear 

378 2 Cow 

379 dtype: str 

380 >>> s.ndim 

381 1 

382 

383 For Index: 

384 

385 >>> idx = pd.Index([1, 2, 3]) 

386 >>> idx 

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

388 >>> idx.ndim 

389 1 

390 """ 

391 return 1 

392 

393 @final 

394 def item(self): 

395 """ 

396 Return the first element of the underlying data as a Python scalar. 

397 

398 Returns 

399 ------- 

400 scalar 

401 The first element of Series or Index. 

402 

403 Raises 

404 ------ 

405 ValueError 

406 If the data is not length = 1. 

407 

408 See Also 

409 -------- 

410 Index.values : Returns an array representing the data in the Index. 

411 Series.head : Returns the first `n` rows. 

412 

413 Examples 

414 -------- 

415 >>> s = pd.Series([1]) 

416 >>> s.item() 

417 1 

418 

419 For an index: 

420 

421 >>> s = pd.Series([1], index=["a"]) 

422 >>> s.index.item() 

423 'a' 

424 """ 

425 if len(self) == 1: 

426 return next(iter(self)) 

427 raise ValueError("can only convert an array of size 1 to a Python scalar") 

428 

429 @property 

430 def nbytes(self) -> int: 

431 """ 

432 Return the number of bytes in the underlying data. 

433 

434 See Also 

435 -------- 

436 Series.ndim : Number of dimensions of the underlying data. 

437 Series.size : Return the number of elements in the underlying data. 

438 

439 Examples 

440 -------- 

441 For Series: 

442 

443 >>> s = pd.Series(["Ant", "Bear", "Cow"]) 

444 >>> s 

445 0 Ant 

446 1 Bear 

447 2 Cow 

448 dtype: str 

449 >>> s.nbytes 

450 34 

451 

452 For Index: 

453 

454 >>> idx = pd.Index([1, 2, 3]) 

455 >>> idx 

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

457 >>> idx.nbytes 

458 24 

459 """ 

460 return self._values.nbytes 

461 

462 @property 

463 def size(self) -> int: 

464 """ 

465 Return the number of elements in the underlying data. 

466 

467 See Also 

468 -------- 

469 Series.ndim: Number of dimensions of the underlying data, by definition 1. 

470 Series.shape: Return a tuple of the shape of the underlying data. 

471 Series.dtype: Return the dtype object of the underlying data. 

472 Series.values: Return Series as ndarray or ndarray-like depending on the dtype. 

473 

474 Examples 

475 -------- 

476 For Series: 

477 

478 >>> s = pd.Series(["Ant", "Bear", "Cow"]) 

479 >>> s 

480 0 Ant 

481 1 Bear 

482 2 Cow 

483 dtype: str 

484 >>> s.size 

485 3 

486 

487 For Index: 

488 

489 >>> idx = pd.Index([1, 2, 3]) 

490 >>> idx 

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

492 >>> idx.size 

493 3 

494 """ 

495 return len(self._values) 

496 

497 @property 

498 def array(self) -> ExtensionArray: 

499 """ 

500 The ExtensionArray of the data backing this Series or Index. 

501 

502 This property provides direct access to the underlying array data of a 

503 Series or Index without requiring conversion to a NumPy array. It 

504 returns an ExtensionArray, which is the native storage format for 

505 pandas extension dtypes. 

506 

507 Returns 

508 ------- 

509 ExtensionArray 

510 An ExtensionArray of the values stored within. For extension 

511 types, this is the actual array. For NumPy native types, this 

512 is a thin (no copy) wrapper around :class:`numpy.ndarray`. 

513 

514 ``.array`` differs from ``.values``, which may require converting 

515 the data to a different form. 

516 

517 See Also 

518 -------- 

519 Index.to_numpy : Similar method that always returns a NumPy array. 

520 Series.to_numpy : Similar method that always returns a NumPy array. 

521 

522 Notes 

523 ----- 

524 This table lays out the different array types for each extension 

525 dtype within pandas. 

526 

527 ================== ============================= 

528 dtype array type 

529 ================== ============================= 

530 category Categorical 

531 period PeriodArray 

532 interval IntervalArray 

533 IntegerNA IntegerArray 

534 string StringArray 

535 boolean BooleanArray 

536 datetime64[ns, tz] DatetimeArray 

537 ================== ============================= 

538 

539 For any 3rd-party extension types, the array type will be an 

540 ExtensionArray. 

541 

542 For all remaining dtypes ``.array`` will be a 

543 :class:`arrays.NumpyExtensionArray` wrapping the actual ndarray 

544 stored within. If you absolutely need a NumPy array (possibly with 

545 copying / coercing data), then use :meth:`Series.to_numpy` instead. 

546 

547 Examples 

548 -------- 

549 For regular NumPy types like int, and float, a NumpyExtensionArray 

550 is returned. 

551 

552 >>> pd.Series([1, 2, 3]).array 

553 <NumpyExtensionArray> 

554 [1, 2, 3] 

555 Length: 3, dtype: int64 

556 

557 For extension types, like Categorical, the actual ExtensionArray 

558 is returned 

559 

560 >>> ser = pd.Series(pd.Categorical(["a", "b", "a"])) 

561 >>> ser.array 

562 ['a', 'b', 'a'] 

563 Categories (2, str): ['a', 'b'] 

564 """ 

565 raise AbstractMethodError(self) 

566 

567 def to_numpy( 

568 self, 

569 dtype: npt.DTypeLike | None = None, 

570 copy: bool = False, 

571 na_value: object = lib.no_default, 

572 **kwargs, 

573 ) -> np.ndarray: 

574 """ 

575 A NumPy ndarray representing the values in this Series or Index. 

576 

577 Parameters 

578 ---------- 

579 dtype : str or numpy.dtype, optional 

580 The dtype to pass to :meth:`numpy.asarray`. 

581 copy : bool, default False 

582 Whether to ensure that the returned value is not a view on 

583 another array. Note that ``copy=False`` does not *ensure* that 

584 ``to_numpy()`` is no-copy. Rather, ``copy=True`` ensure that 

585 a copy is made, even if not strictly necessary. 

586 na_value : Any, optional 

587 The value to use for missing values. The default value depends 

588 on `dtype` and the type of the array. 

589 **kwargs 

590 Additional keywords passed through to the ``to_numpy`` method 

591 of the underlying array (for extension arrays). 

592 

593 Returns 

594 ------- 

595 numpy.ndarray 

596 The NumPy ndarray holding the values from this Series or Index. 

597 The dtype of the array may differ. See Notes. 

598 

599 See Also 

600 -------- 

601 Series.array : Get the actual data stored within. 

602 Index.array : Get the actual data stored within. 

603 DataFrame.to_numpy : Similar method for DataFrame. 

604 

605 Notes 

606 ----- 

607 The returned array will be the same up to equality (values equal 

608 in `self` will be equal in the returned array; likewise for values 

609 that are not equal). When `self` contains an ExtensionArray, the 

610 dtype may be different. For example, for a category-dtype Series, 

611 ``to_numpy()`` will return a NumPy array and the categorical dtype 

612 will be lost. 

613 

614 For NumPy dtypes, this will be a reference to the actual data stored 

615 in this Series or Index (assuming ``copy=False``). Modifying the result 

616 in place will modify the data stored in the Series or Index (not that 

617 we recommend doing that). 

618 

619 For extension types, ``to_numpy()`` *may* require copying data and 

620 coercing the result to a NumPy type (possibly object), which may be 

621 expensive. When you need a no-copy reference to the underlying data, 

622 :attr:`Series.array` should be used instead. 

623 

624 This table lays out the different dtypes and default return types of 

625 ``to_numpy()`` for various dtypes within pandas. 

626 

627 ================== ================================ 

628 dtype array type 

629 ================== ================================ 

630 category[T] ndarray[T] (same dtype as input) 

631 period ndarray[object] (Periods) 

632 interval ndarray[object] (Intervals) 

633 IntegerNA ndarray[object] 

634 datetime64[ns] datetime64[ns] 

635 datetime64[ns, tz] ndarray[object] (Timestamps) 

636 ================== ================================ 

637 

638 Examples 

639 -------- 

640 >>> ser = pd.Series(pd.Categorical(["a", "b", "a"])) 

641 >>> ser.to_numpy() 

642 array(['a', 'b', 'a'], dtype=object) 

643 

644 Specify the `dtype` to control how datetime-aware data is represented. 

645 Use ``dtype=object`` to return an ndarray of pandas :class:`Timestamp` 

646 objects, each with the correct ``tz``. 

647 

648 >>> ser = pd.Series(pd.date_range("2000", periods=2, tz="CET")) 

649 >>> ser.to_numpy(dtype=object) 

650 array([Timestamp('2000-01-01 00:00:00+0100', tz='CET'), 

651 Timestamp('2000-01-02 00:00:00+0100', tz='CET')], 

652 dtype=object) 

653 

654 Or ``dtype='datetime64[ns]'`` to return an ndarray of native 

655 datetime64 values. The values are converted to UTC and the timezone 

656 info is dropped. 

657 

658 >>> ser.to_numpy(dtype="datetime64[ns]") 

659 ... # doctest: +ELLIPSIS 

660 array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00...'], 

661 dtype='datetime64[ns]') 

662 """ 

663 if isinstance(self.dtype, ExtensionDtype): 

664 return self.array.to_numpy(dtype, copy=copy, na_value=na_value, **kwargs) 

665 elif kwargs: 

666 bad_keys = next(iter(kwargs.keys())) 

667 raise TypeError( 

668 f"to_numpy() got an unexpected keyword argument '{bad_keys}'" 

669 ) 

670 

671 fillna = ( 

672 na_value is not lib.no_default 

673 # no need to fillna with np.nan if we already have a float dtype 

674 and not (na_value is np.nan and np.issubdtype(self.dtype, np.floating)) 

675 ) 

676 

677 values = self._values 

678 if fillna and self.hasnans: 

679 if not can_hold_element(values, na_value): 

680 # if we can't hold the na_value asarray either makes a copy or we 

681 # error before modifying values. The asarray later on thus won't make 

682 # another copy 

683 values = np.asarray(values, dtype=dtype) 

684 else: 

685 values = values.copy() 

686 

687 values[np.asanyarray(isna(self))] = na_value 

688 

689 result = np.asarray(values, dtype=dtype) 

690 

691 if (copy and not fillna) or not copy: 

692 if np.shares_memory(self._values[:2], result[:2]): 

693 # Take slices to improve performance of check 

694 if not copy: 

695 result = result.view() 

696 result.flags.writeable = False 

697 else: 

698 result = result.copy() 

699 

700 return result 

701 

702 @final 

703 @property 

704 def empty(self) -> bool: 

705 """ 

706 Indicator whether Index is empty. 

707 

708 An Index is considered empty if it has no elements. This property can be 

709 useful for quickly checking the state of an Index, especially in data 

710 processing and analysis workflows where handling of empty datasets might 

711 be required. 

712 

713 Returns 

714 ------- 

715 bool 

716 If Index is empty, return True, if not return False. 

717 

718 See Also 

719 -------- 

720 Index.size : Return the number of elements in the underlying data. 

721 

722 Examples 

723 -------- 

724 >>> idx = pd.Index([1, 2, 3]) 

725 >>> idx 

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

727 >>> idx.empty 

728 False 

729 

730 >>> idx_empty = pd.Index([]) 

731 >>> idx_empty 

732 Index([], dtype='object') 

733 >>> idx_empty.empty 

734 True 

735 

736 If we only have NaNs in our DataFrame, it is not considered empty! 

737 

738 >>> idx = pd.Index([np.nan, np.nan]) 

739 >>> idx 

740 Index([nan, nan], dtype='float64') 

741 >>> idx.empty 

742 False 

743 """ 

744 return not self.size 

745 

746 def argmax( 

747 self, axis: AxisInt | None = None, skipna: bool = True, *args, **kwargs 

748 ) -> int: 

749 """ 

750 Return int position of the largest value in the Series. 

751 

752 If the maximum is achieved in multiple locations, 

753 the first row position is returned. 

754 

755 Parameters 

756 ---------- 

757 axis : None 

758 Unused. Parameter needed for compatibility with DataFrame. 

759 skipna : bool, default True 

760 Exclude NA/null values. If the entire Series is NA, or if ``skipna=False`` 

761 and there is an NA value, this method will raise a ``ValueError``. 

762 *args, **kwargs 

763 Additional arguments and keywords for compatibility with NumPy. 

764 

765 Returns 

766 ------- 

767 int 

768 Row position of the maximum value. 

769 

770 See Also 

771 -------- 

772 Series.argmax : Return position of the maximum value. 

773 Series.argmin : Return position of the minimum value. 

774 numpy.ndarray.argmax : Equivalent method for numpy arrays. 

775 Series.idxmax : Return index label of the maximum values. 

776 Series.idxmin : Return index label of the minimum values. 

777 

778 Examples 

779 -------- 

780 Consider dataset containing cereal calories 

781 

782 >>> s = pd.Series( 

783 ... [100.0, 110.0, 120.0, 110.0], 

784 ... index=[ 

785 ... "Corn Flakes", 

786 ... "Almond Delight", 

787 ... "Cinnamon Toast Crunch", 

788 ... "Cocoa Puff", 

789 ... ], 

790 ... ) 

791 >>> s 

792 Corn Flakes 100.0 

793 Almond Delight 110.0 

794 Cinnamon Toast Crunch 120.0 

795 Cocoa Puff 110.0 

796 dtype: float64 

797 

798 >>> s.argmax() 

799 np.int64(2) 

800 >>> s.argmin() 

801 np.int64(0) 

802 

803 The maximum cereal calories is the third element and 

804 the minimum cereal calories is the first element, 

805 since series is zero-indexed. 

806 """ 

807 delegate = self._values 

808 nv.validate_minmax_axis(axis) 

809 skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) 

810 

811 if isinstance(delegate, ExtensionArray): 

812 return delegate.argmax(skipna=skipna) 

813 else: 

814 result = nanops.nanargmax(delegate, skipna=skipna) 

815 # error: Incompatible return value type (got "Union[int, ndarray]", expected 

816 # "int") 

817 return result # type: ignore[return-value] 

818 

819 def argmin( 

820 self, axis: AxisInt | None = None, skipna: bool = True, *args, **kwargs 

821 ) -> int: 

822 """ 

823 Return int position of the smallest value in the Series. 

824 

825 If the minimum is achieved in multiple locations, 

826 the first row position is returned. 

827 

828 Parameters 

829 ---------- 

830 axis : None 

831 Unused. Parameter needed for compatibility with DataFrame. 

832 skipna : bool, default True 

833 Exclude NA/null values. If the entire Series is NA, or if ``skipna=False`` 

834 and there is an NA value, this method will raise a ``ValueError``. 

835 *args, **kwargs 

836 Additional arguments and keywords for compatibility with NumPy. 

837 

838 Returns 

839 ------- 

840 int 

841 Row position of the minimum value. 

842 

843 See Also 

844 -------- 

845 Series.argmin : Return position of the minimum value. 

846 Series.argmax : Return position of the maximum value. 

847 numpy.ndarray.argmin : Equivalent method for numpy arrays. 

848 Series.idxmin : Return index label of the minimum values. 

849 Series.idxmax : Return index label of the maximum values. 

850 

851 Examples 

852 -------- 

853 Consider dataset containing cereal calories 

854 

855 >>> s = pd.Series( 

856 ... [100.0, 110.0, 120.0, 110.0], 

857 ... index=[ 

858 ... "Corn Flakes", 

859 ... "Almond Delight", 

860 ... "Cinnamon Toast Crunch", 

861 ... "Cocoa Puff", 

862 ... ], 

863 ... ) 

864 >>> s 

865 Corn Flakes 100.0 

866 Almond Delight 110.0 

867 Cinnamon Toast Crunch 120.0 

868 Cocoa Puff 110.0 

869 dtype: float64 

870 

871 >>> s.argmax() 

872 np.int64(2) 

873 >>> s.argmin() 

874 np.int64(0) 

875 

876 The maximum cereal calories is the third element and 

877 the minimum cereal calories is the first element, 

878 since series is zero-indexed. 

879 """ 

880 delegate = self._values 

881 nv.validate_minmax_axis(axis) 

882 skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) 

883 

884 if isinstance(delegate, ExtensionArray): 

885 return delegate.argmin(skipna=skipna) 

886 else: 

887 result = nanops.nanargmin(delegate, skipna=skipna) 

888 # error: Incompatible return value type (got "Union[int, ndarray]", expected 

889 # "int") 

890 return result # type: ignore[return-value] 

891 

892 def tolist(self) -> list: 

893 """ 

894 Return a list of the values. 

895 

896 These are each a scalar type, which is a Python scalar 

897 (for str, int, float) or a pandas scalar 

898 (for Timestamp/Timedelta/Interval/Period) 

899 

900 Returns 

901 ------- 

902 list 

903 List containing the values as Python or pandas scalers. 

904 

905 See Also 

906 -------- 

907 numpy.ndarray.tolist : Return the array as an a.ndim-levels deep 

908 nested list of Python scalars. 

909 

910 Examples 

911 -------- 

912 For Series 

913 

914 >>> s = pd.Series([1, 2, 3]) 

915 >>> s.to_list() 

916 [1, 2, 3] 

917 

918 For Index: 

919 

920 >>> idx = pd.Index([1, 2, 3]) 

921 >>> idx 

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

923 

924 >>> idx.to_list() 

925 [1, 2, 3] 

926 """ 

927 return self._values.tolist() 

928 

929 to_list = tolist 

930 

931 def __iter__(self) -> Iterator: 

932 """ 

933 Return an iterator of the values. 

934 

935 These are each a scalar type, which is a Python scalar 

936 (for str, int, float) or a pandas scalar 

937 (for Timestamp/Timedelta/Interval/Period) 

938 

939 Returns 

940 ------- 

941 iterator 

942 An iterator yielding scalar values from the Series. 

943 

944 See Also 

945 -------- 

946 Series.items : Lazily iterate over (index, value) tuples. 

947 

948 Examples 

949 -------- 

950 >>> s = pd.Series([1, 2, 3]) 

951 >>> for x in s: 

952 ... print(x) 

953 1 

954 2 

955 3 

956 """ 

957 # We are explicitly making element iterators. 

958 if not isinstance(self._values, np.ndarray): 

959 # Check type instead of dtype to catch DTA/TDA 

960 return iter(self._values) 

961 else: 

962 return map(self._values.item, range(self._values.size)) 

963 

964 @cache_readonly 

965 def hasnans(self) -> bool: 

966 """ 

967 Return True if there are any NaNs. 

968 

969 Enables various performance speedups. 

970 

971 Returns 

972 ------- 

973 bool 

974 

975 See Also 

976 -------- 

977 Series.isna : Detect missing values. 

978 Series.notna : Detect existing (non-missing) values. 

979 

980 Examples 

981 -------- 

982 >>> s = pd.Series([1, 2, 3, None]) 

983 >>> s 

984 0 1.0 

985 1 2.0 

986 2 3.0 

987 3 NaN 

988 dtype: float64 

989 >>> s.hasnans 

990 True 

991 """ 

992 # error: Item "bool" of "Union[bool, ndarray[Any, dtype[bool_]], NDFrame]" 

993 # has no attribute "any" 

994 return bool(isna(self).any()) # type: ignore[union-attr] 

995 

996 @final 

997 def _map_values(self, mapper, na_action=None): 

998 """ 

999 An internal function that maps values using the input 

1000 correspondence (which can be a dict, Series, or function). 

1001 

1002 Parameters 

1003 ---------- 

1004 mapper : function, dict, or Series 

1005 The input correspondence object 

1006 na_action : {None, 'ignore'} 

1007 If 'ignore', propagate NA values, without passing them to the 

1008 mapping function 

1009 

1010 Returns 

1011 ------- 

1012 Union[Index, MultiIndex], inferred 

1013 The output of the mapping function applied to the index. 

1014 If the function returns a tuple with more than one element 

1015 a MultiIndex will be returned. 

1016 """ 

1017 arr = self._values 

1018 

1019 if isinstance(arr, ExtensionArray): 

1020 return arr.map(mapper, na_action=na_action) 

1021 

1022 return algorithms.map_array(arr, mapper, na_action=na_action) 

1023 

1024 def value_counts( 

1025 self, 

1026 normalize: bool = False, 

1027 sort: bool = True, 

1028 ascending: bool = False, 

1029 bins=None, 

1030 dropna: bool = True, 

1031 ) -> Series: 

1032 """ 

1033 Return a Series containing counts of unique values. 

1034 

1035 The resulting object will be in descending order so that the 

1036 first element is the most frequently-occurring element. 

1037 Excludes NA values by default. 

1038 

1039 Parameters 

1040 ---------- 

1041 normalize : bool, default False 

1042 If True then the object returned will contain the relative 

1043 frequencies of the unique values. 

1044 sort : bool, default True 

1045 Stable sort by frequencies when True. Preserve the order of the data 

1046 when False. 

1047 

1048 .. versionchanged:: 3.0.0 

1049 

1050 Prior to 3.0.0, the sort was unstable. 

1051 ascending : bool, default False 

1052 Sort in ascending order. 

1053 bins : int, optional 

1054 Rather than count values, group them into half-open bins, 

1055 a convenience for ``pd.cut``, only works with numeric data. 

1056 dropna : bool, default True 

1057 Don't include counts of NaN. 

1058 

1059 Returns 

1060 ------- 

1061 Series 

1062 Series containing counts of unique values. 

1063 

1064 See Also 

1065 -------- 

1066 Series.count: Number of non-NA elements in a Series. 

1067 DataFrame.count: Number of non-NA elements in a DataFrame. 

1068 DataFrame.value_counts: Equivalent method on DataFrames. 

1069 

1070 Examples 

1071 -------- 

1072 >>> index = pd.Index([3, 1, 2, 3, 4, np.nan]) 

1073 >>> index.value_counts() 

1074 3.0 2 

1075 1.0 1 

1076 2.0 1 

1077 4.0 1 

1078 Name: count, dtype: int64 

1079 

1080 With `normalize` set to `True`, returns the relative frequency by 

1081 dividing all values by the sum of values. 

1082 

1083 >>> s = pd.Series([3, 1, 2, 3, 4, np.nan]) 

1084 >>> s.value_counts(normalize=True) 

1085 3.0 0.4 

1086 1.0 0.2 

1087 2.0 0.2 

1088 4.0 0.2 

1089 Name: proportion, dtype: float64 

1090 

1091 **bins** 

1092 

1093 Bins can be useful for going from a continuous variable to a 

1094 categorical variable; instead of counting unique 

1095 apparitions of values, divide the index in the specified 

1096 number of half-open bins. 

1097 

1098 >>> s.value_counts(bins=3) 

1099 (0.996, 2.0] 2 

1100 (2.0, 3.0] 2 

1101 (3.0, 4.0] 1 

1102 Name: count, dtype: int64 

1103 

1104 **dropna** 

1105 

1106 With `dropna` set to `False` we can also see NaN index values. 

1107 

1108 >>> s.value_counts(dropna=False) 

1109 3.0 2 

1110 1.0 1 

1111 2.0 1 

1112 4.0 1 

1113 NaN 1 

1114 Name: count, dtype: int64 

1115 

1116 **Categorical Dtypes** 

1117 

1118 Rows with categorical type will be counted as one group 

1119 if they have same categories and order. 

1120 In the example below, even though ``a``, ``c``, and ``d`` 

1121 all have the same data types of ``category``, 

1122 only ``c`` and ``d`` will be counted as one group 

1123 since ``a`` doesn't have the same categories. 

1124 

1125 >>> df = pd.DataFrame({"a": [1], "b": ["2"], "c": [3], "d": [3]}) 

1126 >>> df = df.astype({"a": "category", "c": "category", "d": "category"}) 

1127 >>> df 

1128 a b c d 

1129 0 1 2 3 3 

1130 

1131 >>> df.dtypes 

1132 a category 

1133 b str 

1134 c category 

1135 d category 

1136 dtype: object 

1137 

1138 >>> df.dtypes.value_counts() 

1139 category 2 

1140 category 1 

1141 str 1 

1142 Name: count, dtype: int64 

1143 """ 

1144 return algorithms.value_counts_internal( 

1145 self, 

1146 sort=sort, 

1147 ascending=ascending, 

1148 normalize=normalize, 

1149 bins=bins, 

1150 dropna=dropna, 

1151 ) 

1152 

1153 def unique(self): 

1154 values = self._values 

1155 if not isinstance(values, np.ndarray): 

1156 # i.e. ExtensionArray 

1157 result = values.unique() 

1158 else: 

1159 result = algorithms.unique1d(values) # type: ignore[assignment] 

1160 return result 

1161 

1162 @final 

1163 def nunique(self, dropna: bool = True) -> int: 

1164 """ 

1165 Return number of unique elements in the object. 

1166 

1167 Excludes NA values by default. 

1168 

1169 Parameters 

1170 ---------- 

1171 dropna : bool, default True 

1172 Don't include NaN in the count. 

1173 

1174 Returns 

1175 ------- 

1176 int 

1177 An integer indicating the number of unique elements in the object. 

1178 

1179 See Also 

1180 -------- 

1181 DataFrame.nunique: Method nunique for DataFrame. 

1182 Series.count: Count non-NA/null observations in the Series. 

1183 

1184 Examples 

1185 -------- 

1186 >>> s = pd.Series([1, 3, 5, 7, 7]) 

1187 >>> s 

1188 0 1 

1189 1 3 

1190 2 5 

1191 3 7 

1192 4 7 

1193 dtype: int64 

1194 

1195 >>> s.nunique() 

1196 4 

1197 """ 

1198 uniqs = self.unique() 

1199 if dropna: 

1200 uniqs = remove_na_arraylike(uniqs) 

1201 return len(uniqs) 

1202 

1203 @property 

1204 def is_unique(self) -> bool: 

1205 """ 

1206 Return True if values in the object are unique. 

1207 

1208 Returns 

1209 ------- 

1210 bool 

1211 

1212 See Also 

1213 -------- 

1214 Series.unique : Return unique values of Series object. 

1215 Series.drop_duplicates : Return Series with duplicate values removed. 

1216 Series.duplicated : Indicate duplicate Series values. 

1217 

1218 Examples 

1219 -------- 

1220 >>> s = pd.Series([1, 2, 3]) 

1221 >>> s.is_unique 

1222 True 

1223 

1224 >>> s = pd.Series([1, 2, 3, 1]) 

1225 >>> s.is_unique 

1226 False 

1227 """ 

1228 return self.nunique(dropna=False) == len(self) 

1229 

1230 @property 

1231 def is_monotonic_increasing(self) -> bool: 

1232 """ 

1233 Return True if values in the object are monotonically increasing. 

1234 

1235 Returns 

1236 ------- 

1237 bool 

1238 

1239 See Also 

1240 -------- 

1241 Series.is_monotonic_decreasing : Return boolean if values in the object are 

1242 monotonically decreasing. 

1243 

1244 Examples 

1245 -------- 

1246 >>> s = pd.Series([1, 2, 2]) 

1247 >>> s.is_monotonic_increasing 

1248 True 

1249 

1250 >>> s = pd.Series([3, 2, 1]) 

1251 >>> s.is_monotonic_increasing 

1252 False 

1253 """ 

1254 from pandas import Index 

1255 

1256 return Index(self).is_monotonic_increasing 

1257 

1258 @property 

1259 def is_monotonic_decreasing(self) -> bool: 

1260 """ 

1261 Return True if values in the object are monotonically decreasing. 

1262 

1263 Returns 

1264 ------- 

1265 bool 

1266 

1267 See Also 

1268 -------- 

1269 Series.is_monotonic_increasing : Return boolean if values in the object are 

1270 monotonically increasing. 

1271 

1272 Examples 

1273 -------- 

1274 >>> s = pd.Series([3, 2, 2, 1]) 

1275 >>> s.is_monotonic_decreasing 

1276 True 

1277 

1278 >>> s = pd.Series([1, 2, 3]) 

1279 >>> s.is_monotonic_decreasing 

1280 False 

1281 """ 

1282 from pandas import Index 

1283 

1284 return Index(self).is_monotonic_decreasing 

1285 

1286 @final 

1287 def _memory_usage(self, deep: bool = False) -> int: 

1288 """ 

1289 Memory usage of the values. 

1290 

1291 Parameters 

1292 ---------- 

1293 deep : bool, default False 

1294 Introspect the data deeply, interrogate 

1295 `object` dtypes for system-level memory consumption. 

1296 

1297 Returns 

1298 ------- 

1299 bytes used 

1300 Returns memory usage of the values in the Index in bytes. 

1301 

1302 See Also 

1303 -------- 

1304 numpy.ndarray.nbytes : Total bytes consumed by the elements of the 

1305 array. 

1306 

1307 Notes 

1308 ----- 

1309 Memory usage does not include memory consumed by elements that 

1310 are not components of the array if deep=False or if used on PyPy 

1311 

1312 Examples 

1313 -------- 

1314 >>> idx = pd.Index([1, 2, 3]) 

1315 >>> idx.memory_usage() 

1316 24 

1317 """ 

1318 if hasattr(self.array, "memory_usage"): 

1319 return self.array.memory_usage( # pyright: ignore[reportAttributeAccessIssue] 

1320 deep=deep, 

1321 ) 

1322 

1323 v = self.array.nbytes 

1324 if deep and is_object_dtype(self.dtype) and not PYPY: 

1325 values = cast(np.ndarray, self._values) 

1326 v += lib.memory_usage_of_objects(values) 

1327 return v 

1328 

1329 def factorize( 

1330 self, 

1331 sort: bool = False, 

1332 use_na_sentinel: bool = True, 

1333 ) -> tuple[npt.NDArray[np.intp], Index]: 

1334 """ 

1335 Encode the object as an enumerated type or categorical variable. 

1336 

1337 This method is useful for obtaining a numeric representation of an 

1338 array when all that matters is identifying distinct values. `factorize` 

1339 is available as both a top-level function :func:`pandas.factorize`, 

1340 and as a method :meth:`Series.factorize` and :meth:`Index.factorize`. 

1341 

1342 Parameters 

1343 ---------- 

1344 sort : bool, default False 

1345 Sort `uniques` and shuffle `codes` to maintain the 

1346 relationship. 

1347 use_na_sentinel : bool, default True 

1348 If True, the sentinel -1 will be used for NaN values. If False, 

1349 NaN values will be encoded as non-negative integers and will not drop the 

1350 NaN from the uniques of the values. 

1351 

1352 Returns 

1353 ------- 

1354 codes : ndarray 

1355 An integer ndarray that's an indexer into `uniques`. 

1356 ``uniques.take(codes)`` will have the same values as `values`. 

1357 uniques : ndarray, Index, or Categorical 

1358 The unique valid values. When `values` is Categorical, `uniques` 

1359 is a Categorical. When `values` is some other pandas object, an 

1360 `Index` is returned. Otherwise, a 1-D ndarray is returned. 

1361 

1362 .. note:: 

1363 

1364 Even if there's a missing value in `values`, `uniques` will 

1365 *not* contain an entry for it. 

1366 

1367 See Also 

1368 -------- 

1369 cut : Discretize continuous-valued array. 

1370 unique : Find the unique value in an array. 

1371 

1372 Notes 

1373 ----- 

1374 Reference :ref:`the user guide <reshaping.factorize>` for more examples. 

1375 

1376 Examples 

1377 -------- 

1378 These examples all show factorize as a top-level method like 

1379 ``pd.factorize(values)``. The results are identical for methods like 

1380 :meth:`Series.factorize`. 

1381 

1382 >>> codes, uniques = pd.factorize( 

1383 ... np.array(["b", "b", "a", "c", "b"], dtype="O") 

1384 ... ) 

1385 >>> codes 

1386 array([0, 0, 1, 2, 0]) 

1387 >>> uniques 

1388 array(['b', 'a', 'c'], dtype=object) 

1389 

1390 With ``sort=True``, the `uniques` will be sorted, and `codes` will be 

1391 shuffled so that the relationship is the maintained. 

1392 

1393 >>> codes, uniques = pd.factorize( 

1394 ... np.array(["b", "b", "a", "c", "b"], dtype="O"), sort=True 

1395 ... ) 

1396 >>> codes 

1397 array([1, 1, 0, 2, 1]) 

1398 >>> uniques 

1399 array(['a', 'b', 'c'], dtype=object) 

1400 

1401 When ``use_na_sentinel=True`` (the default), missing values are indicated in 

1402 the `codes` with the sentinel value ``-1`` and missing values are not 

1403 included in `uniques`. 

1404 

1405 >>> codes, uniques = pd.factorize( 

1406 ... np.array(["b", None, "a", "c", "b"], dtype="O") 

1407 ... ) 

1408 >>> codes 

1409 array([ 0, -1, 1, 2, 0]) 

1410 >>> uniques 

1411 array(['b', 'a', 'c'], dtype=object) 

1412 

1413 Thus far, we've only factorized lists (which are internally coerced to 

1414 NumPy arrays). When factorizing pandas objects, the type of `uniques` 

1415 will differ. For Categoricals, a `Categorical` is returned. 

1416 

1417 >>> cat = pd.Categorical(["a", "a", "c"], categories=["a", "b", "c"]) 

1418 >>> codes, uniques = pd.factorize(cat) 

1419 >>> codes 

1420 array([0, 0, 1]) 

1421 >>> uniques 

1422 ['a', 'c'] 

1423 Categories (3, str): ['a', 'b', 'c'] 

1424 

1425 Notice that ``'b'`` is in ``uniques.categories``, despite not being 

1426 present in ``cat.values``. 

1427 

1428 For all other pandas objects, an Index of the appropriate type is 

1429 returned. 

1430 

1431 >>> cat = pd.Series(["a", "a", "c"]) 

1432 >>> codes, uniques = pd.factorize(cat) 

1433 >>> codes 

1434 array([0, 0, 1]) 

1435 >>> uniques 

1436 Index(['a', 'c'], dtype='str') 

1437 

1438 If NaN is in the values, and we want to include NaN in the uniques of the 

1439 values, it can be achieved by setting ``use_na_sentinel=False``. 

1440 

1441 >>> values = np.array([1, 2, 1, np.nan]) 

1442 >>> codes, uniques = pd.factorize(values) # default: use_na_sentinel=True 

1443 >>> codes 

1444 array([ 0, 1, 0, -1]) 

1445 >>> uniques 

1446 array([1., 2.]) 

1447 

1448 >>> codes, uniques = pd.factorize(values, use_na_sentinel=False) 

1449 >>> codes 

1450 array([0, 1, 0, 2]) 

1451 >>> uniques 

1452 array([ 1., 2., nan]) 

1453 """ 

1454 codes, uniques = algorithms.factorize( 

1455 self._values, sort=sort, use_na_sentinel=use_na_sentinel 

1456 ) 

1457 if uniques.dtype == np.float16: 

1458 uniques = uniques.astype(np.float32) 

1459 

1460 if isinstance(self, ABCMultiIndex): 

1461 # preserve MultiIndex 

1462 if len(self) == 0: 

1463 # GH#57517 

1464 uniques = self[:0] 

1465 else: 

1466 uniques = self._constructor(uniques) 

1467 else: 

1468 from pandas import Index 

1469 

1470 try: 

1471 uniques = Index(uniques, dtype=self.dtype, copy=False) 

1472 except NotImplementedError: 

1473 # not all dtypes are supported in Index that are allowed for Series 

1474 # e.g. float16 or bytes 

1475 uniques = Index(uniques, copy=False) 

1476 return codes, uniques 

1477 

1478 # This overload is needed so that the call to searchsorted in 

1479 # pandas.core.resample.TimeGrouper._get_period_bins picks the correct result 

1480 

1481 # error: Overloaded function signatures 1 and 2 overlap with incompatible 

1482 # return types 

1483 @overload 

1484 def searchsorted( # type: ignore[overload-overlap] 

1485 self, 

1486 value: ScalarLike_co, 

1487 side: Literal["left", "right"] = ..., 

1488 sorter: NumpySorter = ..., 

1489 ) -> np.intp: ... 

1490 

1491 @overload 

1492 def searchsorted( 

1493 self, 

1494 value: npt.ArrayLike | ExtensionArray, 

1495 side: Literal["left", "right"] = ..., 

1496 sorter: NumpySorter = ..., 

1497 ) -> npt.NDArray[np.intp]: ... 

1498 

1499 def searchsorted( 

1500 self, 

1501 value: NumpyValueArrayLike | ExtensionArray, 

1502 side: Literal["left", "right"] = "left", 

1503 sorter: NumpySorter | None = None, 

1504 ) -> npt.NDArray[np.intp] | np.intp: 

1505 """ 

1506 Find indices where elements should be inserted to maintain order. 

1507 

1508 Find the indices into a sorted Index `self` such that, if the 

1509 corresponding elements in `value` were inserted before the indices, 

1510 the order of `self` would be preserved. 

1511 

1512 .. note:: 

1513 

1514 The Index *must* be monotonically sorted, otherwise 

1515 wrong locations will likely be returned. Pandas does *not* 

1516 check this for you. 

1517 

1518 Parameters 

1519 ---------- 

1520 value : array-like or scalar 

1521 Values to insert into `self`. 

1522 side : {'left', 'right'}, optional 

1523 If 'left', the index of the first suitable location found is given. 

1524 If 'right', return the last such index. If there is no suitable 

1525 index, return either 0 or N (where N is the length of `self`). 

1526 sorter : 1-D array-like, optional 

1527 Optional array of integer indices that sort `self` into ascending 

1528 order. They are typically the result of ``np.argsort``. 

1529 

1530 Returns 

1531 ------- 

1532 int or array of int 

1533 A scalar or array of insertion points with the 

1534 same shape as `value`. 

1535 

1536 See Also 

1537 -------- 

1538 sort_values : Sort by the values along either axis. 

1539 numpy.searchsorted : Similar method from NumPy. 

1540 

1541 Notes 

1542 ----- 

1543 Binary search is used to find the required insertion points. 

1544 

1545 Examples 

1546 -------- 

1547 >>> ser = pd.Series([1, 2, 3]) 

1548 >>> ser 

1549 0 1 

1550 1 2 

1551 2 3 

1552 dtype: int64 

1553 

1554 >>> ser.searchsorted(4) 

1555 np.int64(3) 

1556 

1557 >>> ser.searchsorted([0, 4]) 

1558 array([0, 3]) 

1559 

1560 >>> ser.searchsorted([1, 3], side="left") 

1561 array([0, 2]) 

1562 

1563 >>> ser.searchsorted([1, 3], side="right") 

1564 array([1, 3]) 

1565 

1566 >>> ser = pd.Series(pd.to_datetime(["3/11/2000", "3/12/2000", "3/13/2000"])) 

1567 >>> ser 

1568 0 2000-03-11 

1569 1 2000-03-12 

1570 2 2000-03-13 

1571 dtype: datetime64[us] 

1572 

1573 >>> ser.searchsorted("3/14/2000") 

1574 np.int64(3) 

1575 

1576 >>> ser = pd.Categorical( 

1577 ... ["apple", "bread", "bread", "cheese", "milk"], ordered=True 

1578 ... ) 

1579 >>> ser 

1580 ['apple', 'bread', 'bread', 'cheese', 'milk'] 

1581 Categories (4, str): ['apple' < 'bread' < 'cheese' < 'milk'] 

1582 

1583 >>> ser.searchsorted("bread") 

1584 np.int64(1) 

1585 

1586 >>> ser.searchsorted(["bread"], side="right") 

1587 array([3]) 

1588 

1589 If the values are not monotonically sorted, wrong locations 

1590 may be returned: 

1591 

1592 >>> ser = pd.Series([2, 1, 3]) 

1593 >>> ser 

1594 0 2 

1595 1 1 

1596 2 3 

1597 dtype: int64 

1598 

1599 >>> ser.searchsorted(1) # doctest: +SKIP 

1600 0 # wrong result, correct would be 1 

1601 """ 

1602 if isinstance(value, ABCDataFrame): 

1603 msg = ( 

1604 "Value must be 1-D array-like or scalar, " 

1605 f"{type(value).__name__} is not supported" 

1606 ) 

1607 raise ValueError(msg) 

1608 

1609 values = self._values 

1610 if not isinstance(values, np.ndarray): 

1611 # Going through EA.searchsorted directly improves performance GH#38083 

1612 return values.searchsorted(value, side=side, sorter=sorter) 

1613 

1614 return algorithms.searchsorted( 

1615 values, 

1616 value, 

1617 side=side, 

1618 sorter=sorter, 

1619 ) 

1620 

1621 def drop_duplicates(self, *, keep: DropKeep = "first") -> Self: 

1622 duplicated = self._duplicated(keep=keep) 

1623 # error: Value of type "IndexOpsMixin" is not indexable 

1624 return self[~duplicated] # type: ignore[index] 

1625 

1626 @final 

1627 def _duplicated(self, keep: DropKeep = "first") -> npt.NDArray[np.bool_]: 

1628 arr = self._values 

1629 if isinstance(arr, ExtensionArray): 

1630 return arr.duplicated(keep=keep) 

1631 return algorithms.duplicated(arr, keep=keep) 

1632 

1633 def _arith_method(self, other, op): 

1634 res_name = ops.get_op_result_name(self, other) 

1635 

1636 lvalues = self._values 

1637 rvalues = extract_array(other, extract_numpy=True, extract_range=True) 

1638 rvalues = ops.maybe_prepare_scalar_for_op(rvalues, lvalues.shape) 

1639 rvalues = ensure_wrapped_if_datetimelike(rvalues) 

1640 if isinstance(rvalues, range): 

1641 rvalues = np.arange(rvalues.start, rvalues.stop, rvalues.step) 

1642 

1643 with np.errstate(all="ignore"): 

1644 result = ops.arithmetic_op(lvalues, rvalues, op) 

1645 

1646 return self._construct_result(result, name=res_name, other=other) 

1647 

1648 def _construct_result(self, result, name, other): 

1649 """ 

1650 Construct an appropriately-wrapped result from the ArrayLike result 

1651 of an arithmetic-like operation. 

1652 """ 

1653 raise AbstractMethodError(self)