diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index afb2f91f65ccd..f3eda90ee0d18 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -45,6 +45,7 @@ Other enhancements - :meth:`DataFrame.fillna` and :meth:`Series.fillna` can now accept ``value=None``; for non-object dtype the corresponding NA value will be used (:issue:`57723`) - :meth:`DataFrame.pivot_table` and :func:`pivot_table` now allow the passing of keyword arguments to ``aggfunc`` through ``**kwargs`` (:issue:`57884`) - :meth:`Series.cummin` and :meth:`Series.cummax` now supports :class:`CategoricalDtype` (:issue:`52335`) +- :meth:`Series.isconstant` checks if a :class:`Series` has constant values, with an optional parameter to drop NaN values (:issue:`58806`) - :meth:`Series.plot` now correctly handle the ``ylabel`` parameter for pie charts, allowing for explicit control over the y-axis label (:issue:`58239`) - Restore support for reading Stata 104-format and enable reading 103-format dta files (:issue:`58554`) - Support reading Stata 110-format (Stata 7) dta files (:issue:`47176`) diff --git a/pandas/core/series.py b/pandas/core/series.py index a22cc59b62499..25693bad6182e 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -5393,6 +5393,46 @@ def isnull(self) -> Series: """ return super().isnull() + def isconstant(self, dropna=False): + """ + Return if the series has constant values. + + Parameters + ---------- + dropna : bool, default False + If True, NaN values will be ignored. If False, NaN values will be considered + in the determination of whether the series has constant values. + + Returns + ------- + bool + True if the series has constant values, False otherwise. + + Examples + -------- + >>> s = pd.Series([2, 2, 2, 2]) + >>> s.isconstant() + True + + >>> s = pd.Series([2, 2, 3]) + >>> s.isconstant() + False + + >>> s = pd.Series([2, 2, 2, np.nan]) + >>> s.isconstant(dropna=True) + True + + >>> s = pd.Series([2, 2, 2, np.nan]) + >>> s.isconstant(dropna=False) + False + """ + v = self.to_numpy() + if dropna: + v = remove_na_arraylike(v) + if v.shape[0] == 0 or not notna(v).any(): + return True + return (v[0] == v).all() + # error: Cannot determine type of 'notna' @doc(NDFrame.notna, klass=_shared_doc_kwargs["klass"]) # type: ignore[has-type] def notna(self) -> Series: diff --git a/pandas/tests/series/methods/test_isconstant.py b/pandas/tests/series/methods/test_isconstant.py new file mode 100644 index 0000000000000..e3fd92604802a --- /dev/null +++ b/pandas/tests/series/methods/test_isconstant.py @@ -0,0 +1,65 @@ +import numpy as np + +from pandas import Series + + +class TestSeriesIsConstant: + def test_isconstant(self): + s = Series([2, 2, 2, 2]) + result = s.isconstant() + assert result + + s = Series([2, 2, 2, 3]) + result = s.isconstant() + assert not result + + s = Series([]) + result = s.isconstant() + assert result + + s = Series([5]) + result = s.isconstant() + assert result + + def test_isconstant_with_nan(self): + s = Series([np.nan]) + result = s.isconstant() + assert result + + s = Series([np.nan, np.nan]) + result = s.isconstant() + assert result + + s = Series([np.nan, 1]) + result = s.isconstant() + assert not result + + s = Series([np.nan, np.nan, 1]) + result = s.isconstant() + assert not result + + def test_isconstant_with_nan_dropna(self): + s = Series([np.nan]) + result = s.isconstant(True) + assert result + + s = Series([np.nan, np.nan]) + result = s.isconstant(True) + assert result + + s = Series([np.nan, 1]) + result = s.isconstant(True) + assert result + + s = Series([np.nan, np.nan, 1]) + result = s.isconstant(True) + assert result + + def test_isconstant_mixed_types(self): + s = Series([2, "2", 2]) + result = s.isconstant() + assert not result + + s = Series([2, 2.0, 2]) + result = s.isconstant() + assert result