Skip to content

Commit a380726

Browse files
authored
BUG: idxmax/min (and argmax/min) for Series with underlying ExtensionArray (#37924)
1 parent ff05fff commit a380726

File tree

4 files changed

+45
-9
lines changed

4 files changed

+45
-9
lines changed

doc/source/whatsnew/v1.3.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,9 @@ Sparse
306306

307307
ExtensionArray
308308
^^^^^^^^^^^^^^
309+
309310
- Bug in :meth:`DataFrame.where` when ``other`` is a :class:`Series` with ExtensionArray dtype (:issue:`38729`)
310-
-
311+
- Fixed bug where :meth:`Series.idxmax`, :meth:`Series.idxmin` and ``argmax/min`` fail when the underlying data is :class:`ExtensionArray` (:issue:`32749`, :issue:`33719`, :issue:`36566`)
311312
-
312313

313314
Other

pandas/core/base.py

+20-4
Original file line numberDiff line numberDiff line change
@@ -715,9 +715,17 @@ def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int:
715715
the minimum cereal calories is the first element,
716716
since series is zero-indexed.
717717
"""
718+
delegate = self._values
718719
nv.validate_minmax_axis(axis)
719-
nv.validate_argmax_with_skipna(skipna, args, kwargs)
720-
return nanops.nanargmax(self._values, skipna=skipna)
720+
skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs)
721+
722+
if isinstance(delegate, ExtensionArray):
723+
if not skipna and delegate.isna().any():
724+
return -1
725+
else:
726+
return delegate.argmax()
727+
else:
728+
return nanops.nanargmax(delegate, skipna=skipna)
721729

722730
def min(self, axis=None, skipna: bool = True, *args, **kwargs):
723731
"""
@@ -765,9 +773,17 @@ def min(self, axis=None, skipna: bool = True, *args, **kwargs):
765773

766774
@doc(argmax, op="min", oppose="max", value="smallest")
767775
def argmin(self, axis=None, skipna=True, *args, **kwargs) -> int:
776+
delegate = self._values
768777
nv.validate_minmax_axis(axis)
769-
nv.validate_argmax_with_skipna(skipna, args, kwargs)
770-
return nanops.nanargmin(self._values, skipna=skipna)
778+
skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs)
779+
780+
if isinstance(delegate, ExtensionArray):
781+
if not skipna and delegate.isna().any():
782+
return -1
783+
else:
784+
return delegate.argmin()
785+
else:
786+
return nanops.nanargmin(delegate, skipna=skipna)
771787

772788
def tolist(self):
773789
"""

pandas/core/series.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -2076,8 +2076,7 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs):
20762076
>>> s.idxmin(skipna=False)
20772077
nan
20782078
"""
2079-
skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs)
2080-
i = nanops.nanargmin(self._values, skipna=skipna)
2079+
i = self.argmin(None, skipna=skipna)
20812080
if i == -1:
20822081
return np.nan
20832082
return self.index[i]
@@ -2147,8 +2146,7 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs):
21472146
>>> s.idxmax(skipna=False)
21482147
nan
21492148
"""
2150-
skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs)
2151-
i = nanops.nanargmax(self._values, skipna=skipna)
2149+
i = self.argmax(None, skipna=skipna)
21522150
if i == -1:
21532151
return np.nan
21542152
return self.index[i]

pandas/tests/extension/base/methods.py

+21
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,27 @@ def test_argmin_argmax_all_na(self, method, data, na_value):
107107
with pytest.raises(ValueError, match=err_msg):
108108
getattr(data_na, method)()
109109

110+
@pytest.mark.parametrize(
111+
"op_name, skipna, expected",
112+
[
113+
("idxmax", True, 0),
114+
("idxmin", True, 2),
115+
("argmax", True, 0),
116+
("argmin", True, 2),
117+
("idxmax", False, np.nan),
118+
("idxmin", False, np.nan),
119+
("argmax", False, -1),
120+
("argmin", False, -1),
121+
],
122+
)
123+
def test_argreduce_series(
124+
self, data_missing_for_sorting, op_name, skipna, expected
125+
):
126+
# data_missing_for_sorting -> [B, NA, A] with A < B and NA missing.
127+
ser = pd.Series(data_missing_for_sorting)
128+
result = getattr(ser, op_name)(skipna=skipna)
129+
tm.assert_almost_equal(result, expected)
130+
110131
@pytest.mark.parametrize(
111132
"na_position, expected",
112133
[

0 commit comments

Comments
 (0)