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
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
1"""
2Base and utility classes for pandas objects.
3"""
5from __future__ import annotations
7from typing import (
8 TYPE_CHECKING,
9 Any,
10 Generic,
11 Literal,
12 Self,
13 cast,
14 final,
15 overload,
16)
18import numpy as np
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
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)
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)
64if TYPE_CHECKING:
65 from collections.abc import (
66 Hashable,
67 Iterator,
68 )
70 from pandas._typing import (
71 DropKeep,
72 NumpySorter,
73 NumpyValueArrayLike,
74 ScalarLike_co,
75 )
77 from pandas import (
78 DataFrame,
79 Index,
80 Series,
81 )
84class PandasObject(DirNamesMixin):
85 """
86 Base class for various pandas objects.
87 """
89 # results from calls to methods decorated with cache_readonly get added to _cache
90 _cache: dict[str, Any]
92 @property
93 def _constructor(self) -> type[Self]:
94 """
95 Class constructor (for this class it's just `__class__`).
96 """
97 return type(self)
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)
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)
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())
127 # no memory_usage attribute, so fall back to object's 'sizeof'
128 return super().__sizeof__()
131class NoNewAttributesMixin:
132 """
133 Mixin which prevents adding new attributes.
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`).
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 """
143 def _freeze(self) -> None:
144 """
145 Prevents setting additional attributes.
146 """
147 object.__setattr__(self, "__frozen", True)
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)
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 """
171 obj: NDFrameT
172 _selection: IndexLabel | None = None
173 exclusions: frozenset[Hashable]
174 _internal_names = ["_cache", "__setstate__"]
175 _internal_names_set = set(_internal_names)
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
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]
193 @final
194 @cache_readonly
195 def ndim(self) -> int:
196 return self._selected_obj.ndim
198 @final
199 @cache_readonly
200 def _obj_with_exclusions(self):
201 if isinstance(self.obj, ABCSeries):
202 return self.obj
204 if self._selection is not None:
205 return self.obj[self._selection_list]
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
216 def __getitem__(self, key):
217 if self._selection is not None:
218 raise IndexError(f"Column(s) {self._selection} already selected")
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)
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)
232 def _gotitem(self, key, ndim: int, subset=None):
233 """
234 sub-classes to define
235 return a sliced object
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)
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
262 def aggregate(self, func, *args, **kwargs):
263 raise AbstractMethodError(self)
265 agg = aggregate
268class IndexOpsMixin(OpsMixin):
269 """
270 Common ops mixin to support a unified interface / docs for Series / Index
271 """
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 )
279 @property
280 def dtype(self) -> DtypeObj:
281 # must be defined here as a property for mypy
282 raise AbstractMethodError(self)
284 @property
285 def _values(self) -> ExtensionArray | np.ndarray:
286 # must be defined here as a property for mypy
287 raise AbstractMethodError(self)
289 @final
290 def transpose(self, *args, **kwargs) -> Self:
291 """
292 Return the transpose, which is by definition self.
294 Returns
295 -------
296 %(klass)s
297 """
298 nv.validate_transpose(args, kwargs)
299 return self
301 T = property(
302 transpose,
303 doc="""
304 Return the transpose, which is by definition self.
306 See Also
307 --------
308 Index : Immutable sequence used for indexing and alignment.
310 Examples
311 --------
312 For Series:
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
326 For Index:
328 >>> idx = pd.Index([1, 2, 3])
329 >>> idx.T
330 Index([1, 2, 3], dtype='int64')
331 """,
332 )
334 @property
335 def shape(self) -> Shape:
336 """
337 Return a tuple of the shape of the underlying data.
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.
345 Examples
346 --------
347 >>> s = pd.Series([1, 2, 3])
348 >>> s.shape
349 (3,)
350 """
351 return self._values.shape
353 def __len__(self) -> int:
354 # We need this defined here for mypy
355 raise AbstractMethodError(self)
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.
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.
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
383 For Index:
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
393 @final
394 def item(self):
395 """
396 Return the first element of the underlying data as a Python scalar.
398 Returns
399 -------
400 scalar
401 The first element of Series or Index.
403 Raises
404 ------
405 ValueError
406 If the data is not length = 1.
408 See Also
409 --------
410 Index.values : Returns an array representing the data in the Index.
411 Series.head : Returns the first `n` rows.
413 Examples
414 --------
415 >>> s = pd.Series([1])
416 >>> s.item()
417 1
419 For an index:
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")
429 @property
430 def nbytes(self) -> int:
431 """
432 Return the number of bytes in the underlying data.
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.
439 Examples
440 --------
441 For Series:
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
452 For Index:
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
462 @property
463 def size(self) -> int:
464 """
465 Return the number of elements in the underlying data.
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.
474 Examples
475 --------
476 For Series:
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
487 For Index:
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)
497 @property
498 def array(self) -> ExtensionArray:
499 """
500 The ExtensionArray of the data backing this Series or Index.
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.
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`.
514 ``.array`` differs from ``.values``, which may require converting
515 the data to a different form.
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.
522 Notes
523 -----
524 This table lays out the different array types for each extension
525 dtype within pandas.
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 ================== =============================
539 For any 3rd-party extension types, the array type will be an
540 ExtensionArray.
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.
547 Examples
548 --------
549 For regular NumPy types like int, and float, a NumpyExtensionArray
550 is returned.
552 >>> pd.Series([1, 2, 3]).array
553 <NumpyExtensionArray>
554 [1, 2, 3]
555 Length: 3, dtype: int64
557 For extension types, like Categorical, the actual ExtensionArray
558 is returned
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)
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.
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).
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.
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.
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.
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).
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.
624 This table lays out the different dtypes and default return types of
625 ``to_numpy()`` for various dtypes within pandas.
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 ================== ================================
638 Examples
639 --------
640 >>> ser = pd.Series(pd.Categorical(["a", "b", "a"]))
641 >>> ser.to_numpy()
642 array(['a', 'b', 'a'], dtype=object)
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``.
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)
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.
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 )
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 )
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()
687 values[np.asanyarray(isna(self))] = na_value
689 result = np.asarray(values, dtype=dtype)
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()
700 return result
702 @final
703 @property
704 def empty(self) -> bool:
705 """
706 Indicator whether Index is empty.
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.
713 Returns
714 -------
715 bool
716 If Index is empty, return True, if not return False.
718 See Also
719 --------
720 Index.size : Return the number of elements in the underlying data.
722 Examples
723 --------
724 >>> idx = pd.Index([1, 2, 3])
725 >>> idx
726 Index([1, 2, 3], dtype='int64')
727 >>> idx.empty
728 False
730 >>> idx_empty = pd.Index([])
731 >>> idx_empty
732 Index([], dtype='object')
733 >>> idx_empty.empty
734 True
736 If we only have NaNs in our DataFrame, it is not considered empty!
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
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.
752 If the maximum is achieved in multiple locations,
753 the first row position is returned.
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.
765 Returns
766 -------
767 int
768 Row position of the maximum value.
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.
778 Examples
779 --------
780 Consider dataset containing cereal calories
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
798 >>> s.argmax()
799 np.int64(2)
800 >>> s.argmin()
801 np.int64(0)
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)
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]
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.
825 If the minimum is achieved in multiple locations,
826 the first row position is returned.
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.
838 Returns
839 -------
840 int
841 Row position of the minimum value.
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.
851 Examples
852 --------
853 Consider dataset containing cereal calories
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
871 >>> s.argmax()
872 np.int64(2)
873 >>> s.argmin()
874 np.int64(0)
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)
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]
892 def tolist(self) -> list:
893 """
894 Return a list of the values.
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)
900 Returns
901 -------
902 list
903 List containing the values as Python or pandas scalers.
905 See Also
906 --------
907 numpy.ndarray.tolist : Return the array as an a.ndim-levels deep
908 nested list of Python scalars.
910 Examples
911 --------
912 For Series
914 >>> s = pd.Series([1, 2, 3])
915 >>> s.to_list()
916 [1, 2, 3]
918 For Index:
920 >>> idx = pd.Index([1, 2, 3])
921 >>> idx
922 Index([1, 2, 3], dtype='int64')
924 >>> idx.to_list()
925 [1, 2, 3]
926 """
927 return self._values.tolist()
929 to_list = tolist
931 def __iter__(self) -> Iterator:
932 """
933 Return an iterator of the values.
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)
939 Returns
940 -------
941 iterator
942 An iterator yielding scalar values from the Series.
944 See Also
945 --------
946 Series.items : Lazily iterate over (index, value) tuples.
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))
964 @cache_readonly
965 def hasnans(self) -> bool:
966 """
967 Return True if there are any NaNs.
969 Enables various performance speedups.
971 Returns
972 -------
973 bool
975 See Also
976 --------
977 Series.isna : Detect missing values.
978 Series.notna : Detect existing (non-missing) values.
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]
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).
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
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
1019 if isinstance(arr, ExtensionArray):
1020 return arr.map(mapper, na_action=na_action)
1022 return algorithms.map_array(arr, mapper, na_action=na_action)
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.
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.
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.
1048 .. versionchanged:: 3.0.0
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.
1059 Returns
1060 -------
1061 Series
1062 Series containing counts of unique values.
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.
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
1080 With `normalize` set to `True`, returns the relative frequency by
1081 dividing all values by the sum of values.
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
1091 **bins**
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.
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
1104 **dropna**
1106 With `dropna` set to `False` we can also see NaN index values.
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
1116 **Categorical Dtypes**
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.
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
1131 >>> df.dtypes
1132 a category
1133 b str
1134 c category
1135 d category
1136 dtype: object
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 )
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
1162 @final
1163 def nunique(self, dropna: bool = True) -> int:
1164 """
1165 Return number of unique elements in the object.
1167 Excludes NA values by default.
1169 Parameters
1170 ----------
1171 dropna : bool, default True
1172 Don't include NaN in the count.
1174 Returns
1175 -------
1176 int
1177 An integer indicating the number of unique elements in the object.
1179 See Also
1180 --------
1181 DataFrame.nunique: Method nunique for DataFrame.
1182 Series.count: Count non-NA/null observations in the Series.
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
1195 >>> s.nunique()
1196 4
1197 """
1198 uniqs = self.unique()
1199 if dropna:
1200 uniqs = remove_na_arraylike(uniqs)
1201 return len(uniqs)
1203 @property
1204 def is_unique(self) -> bool:
1205 """
1206 Return True if values in the object are unique.
1208 Returns
1209 -------
1210 bool
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.
1218 Examples
1219 --------
1220 >>> s = pd.Series([1, 2, 3])
1221 >>> s.is_unique
1222 True
1224 >>> s = pd.Series([1, 2, 3, 1])
1225 >>> s.is_unique
1226 False
1227 """
1228 return self.nunique(dropna=False) == len(self)
1230 @property
1231 def is_monotonic_increasing(self) -> bool:
1232 """
1233 Return True if values in the object are monotonically increasing.
1235 Returns
1236 -------
1237 bool
1239 See Also
1240 --------
1241 Series.is_monotonic_decreasing : Return boolean if values in the object are
1242 monotonically decreasing.
1244 Examples
1245 --------
1246 >>> s = pd.Series([1, 2, 2])
1247 >>> s.is_monotonic_increasing
1248 True
1250 >>> s = pd.Series([3, 2, 1])
1251 >>> s.is_monotonic_increasing
1252 False
1253 """
1254 from pandas import Index
1256 return Index(self).is_monotonic_increasing
1258 @property
1259 def is_monotonic_decreasing(self) -> bool:
1260 """
1261 Return True if values in the object are monotonically decreasing.
1263 Returns
1264 -------
1265 bool
1267 See Also
1268 --------
1269 Series.is_monotonic_increasing : Return boolean if values in the object are
1270 monotonically increasing.
1272 Examples
1273 --------
1274 >>> s = pd.Series([3, 2, 2, 1])
1275 >>> s.is_monotonic_decreasing
1276 True
1278 >>> s = pd.Series([1, 2, 3])
1279 >>> s.is_monotonic_decreasing
1280 False
1281 """
1282 from pandas import Index
1284 return Index(self).is_monotonic_decreasing
1286 @final
1287 def _memory_usage(self, deep: bool = False) -> int:
1288 """
1289 Memory usage of the values.
1291 Parameters
1292 ----------
1293 deep : bool, default False
1294 Introspect the data deeply, interrogate
1295 `object` dtypes for system-level memory consumption.
1297 Returns
1298 -------
1299 bytes used
1300 Returns memory usage of the values in the Index in bytes.
1302 See Also
1303 --------
1304 numpy.ndarray.nbytes : Total bytes consumed by the elements of the
1305 array.
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
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 )
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
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.
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`.
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.
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.
1362 .. note::
1364 Even if there's a missing value in `values`, `uniques` will
1365 *not* contain an entry for it.
1367 See Also
1368 --------
1369 cut : Discretize continuous-valued array.
1370 unique : Find the unique value in an array.
1372 Notes
1373 -----
1374 Reference :ref:`the user guide <reshaping.factorize>` for more examples.
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`.
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)
1390 With ``sort=True``, the `uniques` will be sorted, and `codes` will be
1391 shuffled so that the relationship is the maintained.
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)
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`.
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)
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.
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']
1425 Notice that ``'b'`` is in ``uniques.categories``, despite not being
1426 present in ``cat.values``.
1428 For all other pandas objects, an Index of the appropriate type is
1429 returned.
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')
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``.
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.])
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)
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
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
1478 # This overload is needed so that the call to searchsorted in
1479 # pandas.core.resample.TimeGrouper._get_period_bins picks the correct result
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: ...
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]: ...
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.
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.
1512 .. note::
1514 The Index *must* be monotonically sorted, otherwise
1515 wrong locations will likely be returned. Pandas does *not*
1516 check this for you.
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``.
1530 Returns
1531 -------
1532 int or array of int
1533 A scalar or array of insertion points with the
1534 same shape as `value`.
1536 See Also
1537 --------
1538 sort_values : Sort by the values along either axis.
1539 numpy.searchsorted : Similar method from NumPy.
1541 Notes
1542 -----
1543 Binary search is used to find the required insertion points.
1545 Examples
1546 --------
1547 >>> ser = pd.Series([1, 2, 3])
1548 >>> ser
1549 0 1
1550 1 2
1551 2 3
1552 dtype: int64
1554 >>> ser.searchsorted(4)
1555 np.int64(3)
1557 >>> ser.searchsorted([0, 4])
1558 array([0, 3])
1560 >>> ser.searchsorted([1, 3], side="left")
1561 array([0, 2])
1563 >>> ser.searchsorted([1, 3], side="right")
1564 array([1, 3])
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]
1573 >>> ser.searchsorted("3/14/2000")
1574 np.int64(3)
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']
1583 >>> ser.searchsorted("bread")
1584 np.int64(1)
1586 >>> ser.searchsorted(["bread"], side="right")
1587 array([3])
1589 If the values are not monotonically sorted, wrong locations
1590 may be returned:
1592 >>> ser = pd.Series([2, 1, 3])
1593 >>> ser
1594 0 2
1595 1 1
1596 2 3
1597 dtype: int64
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)
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)
1614 return algorithms.searchsorted(
1615 values,
1616 value,
1617 side=side,
1618 sorter=sorter,
1619 )
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]
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)
1633 def _arith_method(self, other, op):
1634 res_name = ops.get_op_result_name(self, other)
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)
1643 with np.errstate(all="ignore"):
1644 result = ops.arithmetic_op(lvalues, rvalues, op)
1646 return self._construct_result(result, name=res_name, other=other)
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)