diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 7189c6e68d53d..6f169b2f73242 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -877,7 +877,7 @@ Sparse - Bug in :meth:`DataFrame.sparse.to_coo` raising ``KeyError`` with columns that are a numeric :class:`Index` without a 0 (:issue:`18414`) - Bug in :meth:`SparseArray.astype` with ``copy=False`` producing incorrect results when going from integer dtype to floating dtype (:issue:`34456`) -- +- Implemented :meth:`SparseArray.max` and :meth:`SparseArray.min` (:issue:`40921`) ExtensionArray ^^^^^^^^^^^^^^ diff --git a/pandas/core/arrays/sparse/array.py b/pandas/core/arrays/sparse/array.py index 37898ce682e4f..091efa68c67da 100644 --- a/pandas/core/arrays/sparse/array.py +++ b/pandas/core/arrays/sparse/array.py @@ -1392,6 +1392,24 @@ def mean(self, axis=0, *args, **kwargs): nsparse = self.sp_index.ngaps return (sp_sum + self.fill_value * nsparse) / (ct + nsparse) + def max(self, axis=0, *args, **kwargs): + nv.validate_max(args, kwargs) + + # This condition returns a nan if there are no valid values in the array. + if self.size > 0 and self._valid_sp_values.size == 0: + return np.nan + else: + return np.nanmax(self, axis) + + def min(self, axis=0, *args, **kwargs): + nv.validate_min(args, kwargs) + + # This condition returns a nan if there are no valid values in the array. + if self.size > 0 and self._valid_sp_values.size == 0: + return np.nan + else: + return np.nanmin(self, axis) + # ------------------------------------------------------------------------ # Ufuncs # ------------------------------------------------------------------------ diff --git a/pandas/tests/arrays/sparse/test_array.py b/pandas/tests/arrays/sparse/test_array.py index e073871f96bb4..a96e5b07b7f7e 100644 --- a/pandas/tests/arrays/sparse/test_array.py +++ b/pandas/tests/arrays/sparse/test_array.py @@ -1311,3 +1311,25 @@ def test_dropna(fill_value): df = pd.DataFrame({"a": [0, 1], "b": arr}) expected_df = pd.DataFrame({"a": [1], "b": exp}, index=pd.Int64Index([1])) tm.assert_equal(df.dropna(), expected_df) + + +class TestMinMax: + plain_data = np.arange(5).astype(float) + data_neg = plain_data * (-1) + data_NaN = SparseArray(np.array([0, 1, 2, np.nan, 4])) + data_all_NaN = SparseArray(np.array([np.nan, np.nan, np.nan, np.nan, np.nan])) + + @pytest.mark.parametrize( + "raw_data,max_expected,min_expected", + [ + (plain_data, [4], [0]), + (data_neg, [0], [-4]), + (data_NaN, [4], [0]), + (data_all_NaN, [np.nan], [np.nan]), + ], + ) + def test_maxmin(self, raw_data, max_expected, min_expected): + max_result = SparseArray(raw_data).max() + min_result = SparseArray(raw_data).min() + assert max_result in max_expected + assert min_result in min_expected