Skip to content

Commit fa17d28

Browse files
authored
BUG: idxmin/max dtype (#54166)
1 parent 064781c commit fa17d28

File tree

6 files changed

+35
-11
lines changed

6 files changed

+35
-11
lines changed

doc/source/whatsnew/v2.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ Missing
503503
^^^^^^^
504504
- Bug in :meth:`DataFrame.interpolate` failing to fill across multiblock data when ``method`` is "pad", "ffill", "bfill", or "backfill" (:issue:`53898`)
505505
- Bug in :meth:`DataFrame.interpolate` ignoring ``inplace`` when :class:`DataFrame` is empty (:issue:`53199`)
506+
- Bug in :meth:`Series.idxmin`, :meth:`Series.idxmax`, :meth:`DataFrame.idxmin`, :meth:`DataFrame.idxmax` with a :class:`DatetimeIndex` index containing ``NaT`` incorrectly returning ``NaN`` instead of ``NaT`` (:issue:`43587`)
506507
- Bug in :meth:`Series.interpolate` and :meth:`DataFrame.interpolate` failing to raise on invalid ``downcast`` keyword, which can be only ``None`` or "infer" (:issue:`53103`)
507508
- Bug in :meth:`Series.interpolate` and :meth:`DataFrame.interpolate` with complex dtype incorrectly failing to fill ``NaN`` entries (:issue:`53635`)
508509
-

pandas/core/arrays/categorical.py

+3
Original file line numberDiff line numberDiff line change
@@ -2323,6 +2323,9 @@ def _reduce(
23232323
self, name: str, *, skipna: bool = True, keepdims: bool = False, **kwargs
23242324
):
23252325
result = super()._reduce(name, skipna=skipna, keepdims=keepdims, **kwargs)
2326+
if name in ["argmax", "argmin"]:
2327+
# don't wrap in Categorical!
2328+
return result
23262329
if keepdims:
23272330
return type(self)(result, dtype=self.dtype)
23282331
else:

pandas/core/frame.py

+7-9
Original file line numberDiff line numberDiff line change
@@ -11254,13 +11254,12 @@ def idxmin(
1125411254
nanops.nanargmin, "argmin", axis=axis, skipna=skipna, numeric_only=False
1125511255
)
1125611256
indices = res._values
11257-
11258-
# indices will always be 1d array since axis is not None and
11259-
# values is a 2d array for DataFrame
1126011257
# indices will always be np.ndarray since axis is not N
1126111258

1126211259
index = data._get_axis(axis)
11263-
result = [index[i] if i >= 0 else np.nan for i in indices]
11260+
result = algorithms.take(
11261+
index._values, indices, allow_fill=True, fill_value=index._na_value
11262+
)
1126411263
final_result = data._constructor_sliced(result, index=data._get_agg_axis(axis))
1126511264
return final_result.__finalize__(self, method="idxmin")
1126611265

@@ -11283,13 +11282,12 @@ def idxmax(
1128311282
nanops.nanargmax, "argmax", axis=axis, skipna=skipna, numeric_only=False
1128411283
)
1128511284
indices = res._values
11286-
11287-
# indices will always be 1d array since axis is not None and
11288-
# values is a 2d array for DataFrame
11289-
assert isinstance(indices, (np.ndarray, ExtensionArray)) # for mypy
11285+
# indices will always be 1d array since axis is not None
1129011286

1129111287
index = data._get_axis(axis)
11292-
result = [index[i] if i >= 0 else np.nan for i in indices]
11288+
result = algorithms.take(
11289+
index._values, indices, allow_fill=True, fill_value=index._na_value
11290+
)
1129311291
final_result = data._constructor_sliced(result, index=data._get_agg_axis(axis))
1129411292
return final_result.__finalize__(self, method="idxmax")
1129511293

pandas/core/series.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -2532,7 +2532,8 @@ def idxmin(self, axis: Axis = 0, skipna: bool = True, *args, **kwargs) -> Hashab
25322532
axis = self._get_axis_number(axis)
25332533
i = self.argmin(axis, skipna, *args, **kwargs)
25342534
if i == -1:
2535-
return np.nan
2535+
# GH#43587 give correct NA value for Index.
2536+
return self.index._na_value
25362537
return self.index[i]
25372538

25382539
def idxmax(self, axis: Axis = 0, skipna: bool = True, *args, **kwargs) -> Hashable:
@@ -2602,7 +2603,8 @@ def idxmax(self, axis: Axis = 0, skipna: bool = True, *args, **kwargs) -> Hashab
26022603
axis = self._get_axis_number(axis)
26032604
i = self.argmax(axis, skipna, *args, **kwargs)
26042605
if i == -1:
2605-
return np.nan
2606+
# GH#43587 give correct NA value for Index.
2607+
return self.index._na_value
26062608
return self.index[i]
26072609

26082610
def round(self, decimals: int = 0, *args, **kwargs) -> Series:

pandas/tests/frame/test_reductions.py

+2
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ def test_idxmin(self, float_frame, int_frame, skipna, axis):
970970
for df in [frame, int_frame]:
971971
result = df.idxmin(axis=axis, skipna=skipna)
972972
expected = df.apply(Series.idxmin, axis=axis, skipna=skipna)
973+
expected = expected.astype(df.index.dtype)
973974
tm.assert_series_equal(result, expected)
974975

975976
@pytest.mark.parametrize("axis", [0, 1])
@@ -1009,6 +1010,7 @@ def test_idxmax(self, float_frame, int_frame, skipna, axis):
10091010
for df in [frame, int_frame]:
10101011
result = df.idxmax(axis=axis, skipna=skipna)
10111012
expected = df.apply(Series.idxmax, axis=axis, skipna=skipna)
1013+
expected = expected.astype(df.index.dtype)
10121014
tm.assert_series_equal(result, expected)
10131015

10141016
@pytest.mark.parametrize("axis", [0, 1])

pandas/tests/reductions/test_reductions.py

+18
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,24 @@ def test_numpy_argmax(self):
809809
with pytest.raises(ValueError, match=msg):
810810
np.argmax(s, out=data)
811811

812+
def test_idxmin_dt64index(self):
813+
# GH#43587 should have NaT instead of NaN
814+
ser = Series(
815+
[1.0, 2.0, np.nan], index=DatetimeIndex(["NaT", "2015-02-08", "NaT"])
816+
)
817+
res = ser.idxmin(skipna=False)
818+
assert res is NaT
819+
res = ser.idxmax(skipna=False)
820+
assert res is NaT
821+
822+
df = ser.to_frame()
823+
res = df.idxmin(skipna=False)
824+
assert res.dtype == "M8[ns]"
825+
assert res.isna().all()
826+
res = df.idxmax(skipna=False)
827+
assert res.dtype == "M8[ns]"
828+
assert res.isna().all()
829+
812830
def test_idxmin(self):
813831
# test idxmin
814832
# _check_stat_op approach can not be used here because of isna check.

0 commit comments

Comments
 (0)