From c0006689286b8d2cc224be6aed4583bb4dee9a19 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Tue, 17 Nov 2020 23:30:56 +0000 Subject: [PATCH 01/21] fix idxmax/min for Series with underlying 'Int' datatype --- pandas/core/arrays/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index bd5cf43e19e9f..7a4faadf07986 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -591,7 +591,7 @@ def argsort( mask=np.asarray(self.isna()), ) - def argmin(self): + def argmin(self, *args): """ Return the index of minimum value. @@ -608,7 +608,7 @@ def argmin(self): """ return nargminmax(self, "argmin") - def argmax(self): + def argmax(self, *args): """ Return the index of maximum value. From 163208105fe1abe2c579879878ea349bfa82de26 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Wed, 18 Nov 2020 02:01:31 +0000 Subject: [PATCH 02/21] test added --- pandas/tests/series/test_reductions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index c3c58f29fcbf6..2736e774aee52 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -114,3 +114,7 @@ def test_validate_stat_keepdims(): ) with pytest.raises(ValueError, match=msg): np.sum(ser, keepdims=True) + +def test_idxmax_with_nullable_integer_dtype(): + s = Series([1, 2, 3], dtype="Int64") + s.idxmax() \ No newline at end of file From 8018586e15688e44f82e0daf0f5d46a357a533e5 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Wed, 18 Nov 2020 02:18:41 +0000 Subject: [PATCH 03/21] editted test --- pandas/tests/series/test_reductions.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index 2736e774aee52..c301deb908d19 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -115,6 +115,8 @@ def test_validate_stat_keepdims(): with pytest.raises(ValueError, match=msg): np.sum(ser, keepdims=True) -def test_idxmax_with_nullable_integer_dtype(): - s = Series([1, 2, 3], dtype="Int64") - s.idxmax() \ No newline at end of file + +def test_idxmax_idxmin_with_nullable_integer_dtype(): + ser = Series([1, 2, 3], dtype="Int64") + assert ser.idxmax() == 2 + assert ser.idxmin() == 0 From b076d23849b2b150a748d50c8dcfc0f2f2ddf4cf Mon Sep 17 00:00:00 2001 From: jy3515 Date: Thu, 19 Nov 2020 12:15:44 +0000 Subject: [PATCH 04/21] added test for argmax argmin --- pandas/tests/series/test_reductions.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index c301deb908d19..961f8fe7138b2 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -120,3 +120,9 @@ def test_idxmax_idxmin_with_nullable_integer_dtype(): ser = Series([1, 2, 3], dtype="Int64") assert ser.idxmax() == 2 assert ser.idxmin() == 0 + + +def test_argmax_argmin_with_nullable_integer_dtype(): + ser = Series([1, 2, 3], dtype="Int64") + assert ser.argmax() == 2 + assert ser.argmin() == 0 From 664e4ecd0a05e3c86a65da2541c2a8f4b0cb00e4 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Fri, 20 Nov 2020 00:23:28 +0000 Subject: [PATCH 05/21] added validations --- pandas/core/arrays/base.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index 7a4faadf07986..51526df89bced 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -591,7 +591,7 @@ def argsort( mask=np.asarray(self.isna()), ) - def argmin(self, *args): + def argmin(self, axis=None, *args, **kwargs): """ Return the index of minimum value. @@ -606,9 +606,10 @@ def argmin(self, *args): -------- ExtensionArray.argmax """ + nv.validate_argmin(axis, args, kwargs) return nargminmax(self, "argmin") - def argmax(self, *args): + def argmax(self, axis=None, *args, **kwargs): """ Return the index of maximum value. @@ -623,6 +624,7 @@ def argmax(self, *args): -------- ExtensionArray.argmin """ + nv.validate_argmax(axis, args, kwargs) return nargminmax(self, "argmax") def fillna(self, value=None, method=None, limit=None): From 8403c38d09f8007f97941f891c9440c5a9d1d649 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Fri, 20 Nov 2020 11:35:43 +0000 Subject: [PATCH 06/21] The overhaul --- pandas/core/arrays/base.py | 6 ++--- pandas/core/base.py | 28 +++++++++++++++++----- pandas/core/series.py | 32 +++++++++++++++++++------- pandas/tests/series/test_reductions.py | 8 +++++++ 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index 51526df89bced..bd5cf43e19e9f 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -591,7 +591,7 @@ def argsort( mask=np.asarray(self.isna()), ) - def argmin(self, axis=None, *args, **kwargs): + def argmin(self): """ Return the index of minimum value. @@ -606,10 +606,9 @@ def argmin(self, axis=None, *args, **kwargs): -------- ExtensionArray.argmax """ - nv.validate_argmin(axis, args, kwargs) return nargminmax(self, "argmin") - def argmax(self, axis=None, *args, **kwargs): + def argmax(self): """ Return the index of maximum value. @@ -624,7 +623,6 @@ def argmax(self, axis=None, *args, **kwargs): -------- ExtensionArray.argmin """ - nv.validate_argmax(axis, args, kwargs) return nargminmax(self, "argmax") def fillna(self, value=None, method=None, limit=None): diff --git a/pandas/core/base.py b/pandas/core/base.py index 54dec90c08aa2..8d06c7f8a7ac2 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -715,9 +715,17 @@ def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int: the minimum cereal calories is the first element, since series is zero-indexed. """ - nv.validate_minmax_axis(axis) - nv.validate_argmax_with_skipna(skipna, args, kwargs) - return nanops.nanargmax(self._values, skipna=skipna) + delegate = self._values + + if isinstance(delegate, ExtensionArray): + # dispatch to ExtensionArray interface + return delegate.argmax() + + else: + # dispatch to numpy arrays + nv.validate_minmax_axis(axis) + skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) + return nanops.nanargmax(delegate, skipna=skipna) def min(self, axis=None, skipna: bool = True, *args, **kwargs): """ @@ -765,9 +773,17 @@ def min(self, axis=None, skipna: bool = True, *args, **kwargs): @doc(argmax, op="min", oppose="max", value="smallest") def argmin(self, axis=None, skipna=True, *args, **kwargs) -> int: - nv.validate_minmax_axis(axis) - nv.validate_argmax_with_skipna(skipna, args, kwargs) - return nanops.nanargmin(self._values, skipna=skipna) + delegate = self._values + + if isinstance(delegate, ExtensionArray): + # dispatch to ExtensionArray interface + return delegate.argmin() + + else: + # dispatch to numpy arrays + nv.validate_minmax_axis(axis) + skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) + return nanops.nanargmin(delegate, skipna=skipna) def tolist(self): """ diff --git a/pandas/core/series.py b/pandas/core/series.py index d4ae5c2245b5b..b0a60b4a9de1c 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2076,10 +2076,18 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmin(skipna=False) nan """ - skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) - i = nanops.nanargmin(self._values, skipna=skipna) - if i == -1: - return np.nan + delegate = self._values + + if isinstance(delegate, ExtensionArray): + # dispatch to ExtensionArray interface + i = delegate.argmin() + + else: + # dispatch to numpy arrays + skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) + i = nanops.nanargmin(delegate, skipna=skipna) + if i == -1: + return np.nan return self.index[i] def idxmax(self, axis=0, skipna=True, *args, **kwargs): @@ -2147,10 +2155,18 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmax(skipna=False) nan """ - skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) - i = nanops.nanargmax(self._values, skipna=skipna) - if i == -1: - return np.nan + delegate = self._values + + if isinstance(delegate, ExtensionArray): + # dispatch to ExtensionArray interface + i = delegate.argmax() + + else: + # dispatch to numpy arrays + skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) + i = nanops.nanargmax(delegate, skipna=skipna) + if i == -1: + return np.nan return self.index[i] def round(self, decimals=0, *args, **kwargs) -> "Series": diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index 961f8fe7138b2..b9aa0acc50db3 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -126,3 +126,11 @@ def test_argmax_argmin_with_nullable_integer_dtype(): ser = Series([1, 2, 3], dtype="Int64") assert ser.argmax() == 2 assert ser.argmin() == 0 + + +def test_with_nullable_float_dtype(): + ser = Series([1, 2, 3], dtype="Float64") + assert ser.idxmax() == 2 + assert ser.idxmin() == 0 + assert ser.argmax() == 2 + assert ser.argmin() == 0 From 741c97adcc630ce087f8058b9df4970da2f49917 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Tue, 24 Nov 2020 20:19:46 +0000 Subject: [PATCH 07/21] 2nd attempt --- pandas/core/base.py | 24 ++++++++++++------------ pandas/core/series.py | 17 ++++++++++------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index 8d06c7f8a7ac2..8b1ee8a04b822 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -716,15 +716,15 @@ def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int: since series is zero-indexed. """ delegate = self._values + nv.validate_minmax_axis(axis) + skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - # dispatch to ExtensionArray interface - return delegate.argmax() - + if not skipna and isna(delegate).any(): + return -1 + else: + return delegate.argmax() else: - # dispatch to numpy arrays - nv.validate_minmax_axis(axis) - skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) return nanops.nanargmax(delegate, skipna=skipna) def min(self, axis=None, skipna: bool = True, *args, **kwargs): @@ -774,15 +774,15 @@ def min(self, axis=None, skipna: bool = True, *args, **kwargs): @doc(argmax, op="min", oppose="max", value="smallest") def argmin(self, axis=None, skipna=True, *args, **kwargs) -> int: delegate = self._values + nv.validate_minmax_axis(axis) + skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - # dispatch to ExtensionArray interface - return delegate.argmin() - + if not skipna and isna(delegate).any(): + return -1 + else: + return delegate.argmin() else: - # dispatch to numpy arrays - nv.validate_minmax_axis(axis) - skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) return nanops.nanargmin(delegate, skipna=skipna) def tolist(self): diff --git a/pandas/core/series.py b/pandas/core/series.py index b0a60b4a9de1c..7384ef73e6f58 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2077,13 +2077,15 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): nan """ delegate = self._values + skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - # dispatch to ExtensionArray interface - i = delegate.argmin() + if not skipna and isna(delegate).any(): + return np.nan + else: + i = delegate.argmin() else: - # dispatch to numpy arrays skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) i = nanops.nanargmin(delegate, skipna=skipna) if i == -1: @@ -2156,14 +2158,15 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs): nan """ delegate = self._values + skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - # dispatch to ExtensionArray interface - i = delegate.argmax() + if not skipna and isna(delegate).any(): + return np.nan + else: + i = delegate.argmax() else: - # dispatch to numpy arrays - skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) i = nanops.nanargmax(delegate, skipna=skipna) if i == -1: return np.nan From 131ae836801fcf0f1097db449bc910d71ed919af Mon Sep 17 00:00:00 2001 From: jy3515 Date: Tue, 24 Nov 2020 20:24:49 +0000 Subject: [PATCH 08/21] 2nd attempt --- pandas/core/series.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 7384ef73e6f58..bb215c7a3f4c3 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2086,7 +2086,6 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): i = delegate.argmin() else: - skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) i = nanops.nanargmin(delegate, skipna=skipna) if i == -1: return np.nan From 3269fb5dc519ead5acb91755119a3f520835ed2b Mon Sep 17 00:00:00 2001 From: jy3515 Date: Wed, 25 Nov 2020 23:48:49 +0000 Subject: [PATCH 09/21] simplified idxmaxmin and added parameterised tests --- pandas/core/series.py | 32 +++---------- pandas/tests/series/test_reductions.py | 65 +++++++++++++++++++------- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index bb215c7a3f4c3..00a246b7ce34e 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2076,19 +2076,9 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmin(skipna=False) nan """ - delegate = self._values - skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) - - if isinstance(delegate, ExtensionArray): - if not skipna and isna(delegate).any(): - return np.nan - else: - i = delegate.argmin() - - else: - i = nanops.nanargmin(delegate, skipna=skipna) - if i == -1: - return np.nan + i = self.argmin(axis=None, skipna=skipna, *args, **kwargs) + if i == -1: + return np.nan return self.index[i] def idxmax(self, axis=0, skipna=True, *args, **kwargs): @@ -2156,19 +2146,9 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmax(skipna=False) nan """ - delegate = self._values - skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) - - if isinstance(delegate, ExtensionArray): - if not skipna and isna(delegate).any(): - return np.nan - else: - i = delegate.argmax() - - else: - i = nanops.nanargmax(delegate, skipna=skipna) - if i == -1: - return np.nan + i = self.argmax(axis=None, skipna=skipna, *args, **kwargs) + if i == -1: + return np.nan return self.index[i] def round(self, decimals=0, *args, **kwargs) -> "Series": diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index b9aa0acc50db3..b9ef947373a71 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -1,5 +1,6 @@ import numpy as np import pytest +from datetime import datetime import pandas as pd from pandas import MultiIndex, Series @@ -116,21 +117,49 @@ def test_validate_stat_keepdims(): np.sum(ser, keepdims=True) -def test_idxmax_idxmin_with_nullable_integer_dtype(): - ser = Series([1, 2, 3], dtype="Int64") - assert ser.idxmax() == 2 - assert ser.idxmin() == 0 - - -def test_argmax_argmin_with_nullable_integer_dtype(): - ser = Series([1, 2, 3], dtype="Int64") - assert ser.argmax() == 2 - assert ser.argmin() == 0 - - -def test_with_nullable_float_dtype(): - ser = Series([1, 2, 3], dtype="Float64") - assert ser.idxmax() == 2 - assert ser.idxmin() == 0 - assert ser.argmax() == 2 - assert ser.argmin() == 0 +@pytest.mark.parametrize('dtype', ['Int64', 'Float64']) +@pytest.mark.parametrize( + "op_name, skipna, expected", + [ + ('idxmax', True, 'b'), + ('idxmin', True, 'a'), + ('argmax', True, 1), + ('argmin', True, 0), + ('idxmax', False, np.nan), + ('idxmin', False, np.nan), + ('argmax', False, -1), + ('argmin', False, -1) + ] +) +def test_argminmax_idxminmax(dtype, op_name, skipna, expected): + ser = Series([1, 2, None], index=['a', 'b', 'c'], dtype=dtype) + result = getattr(ser, op_name)(skipna=skipna) + if pd.isna(expected): + assert np.isnan(result) + else: + assert result == expected + + +@pytest.mark.parametrize( + "op_name, skipna, expected", + [ + ('idxmax', True, 'b'), + ('idxmin', True, 'a'), + ('argmax', True, 1), + ('argmin', True, 0), + ('idxmax', False, np.nan), + ('idxmin', False, np.nan), + ('argmax', False, -1), + ('argmin', False, -1) + ] +) +def test_argminmax_idxminmax_with_datetime64_dtype(op_name, skipna, expected): + ser = Series( + [datetime(2020, 1, 1), datetime(2020, 1, 2), None], + index=['a', 'b', 'c'] + ) + result = getattr(ser, op_name)(skipna=skipna) + if pd.isna(expected): + assert np.isnan(result) + else: + assert result == expected From 0cfb621f1d561e862916b22ba96114e98bac9da6 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Thu, 26 Nov 2020 00:50:25 +0000 Subject: [PATCH 10/21] fixed newbie mistake --- pandas/core/series.py | 4 ++-- pandas/tests/series/test_reductions.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 00a246b7ce34e..80d9437267bdd 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2076,7 +2076,7 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmin(skipna=False) nan """ - i = self.argmin(axis=None, skipna=skipna, *args, **kwargs) + i = self.argmin(None, skipna, *args, *kwargs) if i == -1: return np.nan return self.index[i] @@ -2146,7 +2146,7 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmax(skipna=False) nan """ - i = self.argmax(axis=None, skipna=skipna, *args, **kwargs) + i = self.argmax(None, skipna, *args, *kwargs) if i == -1: return np.nan return self.index[i] diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index b9ef947373a71..ff1a38d6a4085 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -1,6 +1,7 @@ +from datetime import datetime + import numpy as np import pytest -from datetime import datetime import pandas as pd from pandas import MultiIndex, Series @@ -128,8 +129,8 @@ def test_validate_stat_keepdims(): ('idxmax', False, np.nan), ('idxmin', False, np.nan), ('argmax', False, -1), - ('argmin', False, -1) - ] + ('argmin', False, -1), + ], ) def test_argminmax_idxminmax(dtype, op_name, skipna, expected): ser = Series([1, 2, None], index=['a', 'b', 'c'], dtype=dtype) @@ -150,13 +151,12 @@ def test_argminmax_idxminmax(dtype, op_name, skipna, expected): ('idxmax', False, np.nan), ('idxmin', False, np.nan), ('argmax', False, -1), - ('argmin', False, -1) - ] + ('argmin', False, -1), + ], ) def test_argminmax_idxminmax_with_datetime64_dtype(op_name, skipna, expected): ser = Series( - [datetime(2020, 1, 1), datetime(2020, 1, 2), None], - index=['a', 'b', 'c'] + [datetime(2020, 1, 1), datetime(2020, 1, 2), None], index=['a', 'b', 'c'] ) result = getattr(ser, op_name)(skipna=skipna) if pd.isna(expected): From d4b13ac52fb0faded2163c7b7a3caf05427a4f6d Mon Sep 17 00:00:00 2001 From: jy3515 Date: Thu, 26 Nov 2020 01:02:43 +0000 Subject: [PATCH 11/21] fixed newbie mistake --- pandas/core/series.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 80d9437267bdd..dafc10d42c702 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2076,7 +2076,7 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmin(skipna=False) nan """ - i = self.argmin(None, skipna, *args, *kwargs) + i = self.argmin(None, skipna, *args, **kwargs) if i == -1: return np.nan return self.index[i] @@ -2146,7 +2146,7 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmax(skipna=False) nan """ - i = self.argmax(None, skipna, *args, *kwargs) + i = self.argmax(None, skipna, *args, **kwargs) if i == -1: return np.nan return self.index[i] From 5648eb936f7f4f5655bac3c95443c952ac5d150f Mon Sep 17 00:00:00 2001 From: jy3515 Date: Thu, 26 Nov 2020 02:43:25 +0000 Subject: [PATCH 12/21] used any_numeric_dtype in test --- pandas/tests/series/test_reductions.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index ff1a38d6a4085..962491dd7bebf 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -118,7 +118,6 @@ def test_validate_stat_keepdims(): np.sum(ser, keepdims=True) -@pytest.mark.parametrize('dtype', ['Int64', 'Float64']) @pytest.mark.parametrize( "op_name, skipna, expected", [ @@ -132,8 +131,8 @@ def test_validate_stat_keepdims(): ('argmin', False, -1), ], ) -def test_argminmax_idxminmax(dtype, op_name, skipna, expected): - ser = Series([1, 2, None], index=['a', 'b', 'c'], dtype=dtype) +def test_argminmax_idxminmax(any_numeric_dtype, op_name, skipna, expected): + ser = Series([1, 2, None], index=['a', 'b', 'c'], dtype=any_numeric_dtype) result = getattr(ser, op_name)(skipna=skipna) if pd.isna(expected): assert np.isnan(result) From 9a1b332f9f2ebc457789763f873a89cc8b4c8bb2 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Thu, 26 Nov 2020 03:17:58 +0000 Subject: [PATCH 13/21] does this solve the pre-commit check failure now? --- pandas/tests/series/test_reductions.py | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index 962491dd7bebf..dc391d3069531 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -121,18 +121,18 @@ def test_validate_stat_keepdims(): @pytest.mark.parametrize( "op_name, skipna, expected", [ - ('idxmax', True, 'b'), - ('idxmin', True, 'a'), - ('argmax', True, 1), - ('argmin', True, 0), - ('idxmax', False, np.nan), - ('idxmin', False, np.nan), - ('argmax', False, -1), - ('argmin', False, -1), + ("idxmax", True, "b"), + ("idxmin", True, "a"), + ("argmax", True, 1), + ("argmin", True, 0), + ("idxmax", False, np.nan), + ("idxmin", False, np.nan), + ("argmax", False, -1), + ("argmin", False, -1), ], ) def test_argminmax_idxminmax(any_numeric_dtype, op_name, skipna, expected): - ser = Series([1, 2, None], index=['a', 'b', 'c'], dtype=any_numeric_dtype) + ser = Series([1, 2, None], index=["a", "b", "c"], dtype=any_numeric_dtype) result = getattr(ser, op_name)(skipna=skipna) if pd.isna(expected): assert np.isnan(result) @@ -143,19 +143,19 @@ def test_argminmax_idxminmax(any_numeric_dtype, op_name, skipna, expected): @pytest.mark.parametrize( "op_name, skipna, expected", [ - ('idxmax', True, 'b'), - ('idxmin', True, 'a'), - ('argmax', True, 1), - ('argmin', True, 0), - ('idxmax', False, np.nan), - ('idxmin', False, np.nan), - ('argmax', False, -1), - ('argmin', False, -1), + ("idxmax", True, "b"), + ("idxmin", True, "a"), + ("argmax", True, 1), + ("argmin", True, 0), + ("idxmax", False, np.nan), + ("idxmin", False, np.nan), + ("argmax", False, -1), + ("argmin", False, -1), ], ) def test_argminmax_idxminmax_with_datetime64_dtype(op_name, skipna, expected): ser = Series( - [datetime(2020, 1, 1), datetime(2020, 1, 2), None], index=['a', 'b', 'c'] + [datetime(2020, 1, 1), datetime(2020, 1, 2), None], index=["a", "b", "c"] ) result = getattr(ser, op_name)(skipna=skipna) if pd.isna(expected): From 9f5e683b846c4c417b0d29ea78348c40f07e83cb Mon Sep 17 00:00:00 2001 From: jy3515 Date: Thu, 26 Nov 2020 15:25:46 +0000 Subject: [PATCH 14/21] moved EA's skipna logic from Series.argmin/max to EA.argmin/max --- pandas/core/arrays/base.py | 24 ++++++++++++++++++++---- pandas/core/base.py | 10 ++-------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index bd5cf43e19e9f..dd8a4088c06b7 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -591,13 +591,18 @@ def argsort( mask=np.asarray(self.isna()), ) - def argmin(self): + def argmin(self, skipna: bool = True) -> int: """ Return the index of minimum value. In case of multiple occurrences of the minimum value, the index corresponding to the first occurrence is returned. + Parameters + ---------- + skipna : bool, default True + Exclude NA values when showing the result. + Returns ------- int @@ -606,15 +611,23 @@ def argmin(self): -------- ExtensionArray.argmax """ - return nargminmax(self, "argmin") + if not skipna and self.isna().any(): + return -1 + else: + return nargminmax(self, "argmin") - def argmax(self): + def argmax(self, skipna: bool = True) -> int: """ Return the index of maximum value. In case of multiple occurrences of the maximum value, the index corresponding to the first occurrence is returned. + Parameters + ---------- + skipna : bool, default True + Exclude NA values when showing the result. + Returns ------- int @@ -623,7 +636,10 @@ def argmax(self): -------- ExtensionArray.argmin """ - return nargminmax(self, "argmax") + if not skipna and self.isna().any(): + return -1 + else: + return nargminmax(self, "argmax") def fillna(self, value=None, method=None, limit=None): """ diff --git a/pandas/core/base.py b/pandas/core/base.py index 8b1ee8a04b822..3d0b5e54d1438 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -720,10 +720,7 @@ def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int: skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - if not skipna and isna(delegate).any(): - return -1 - else: - return delegate.argmax() + return delegate.argmax(skipna) else: return nanops.nanargmax(delegate, skipna=skipna) @@ -778,10 +775,7 @@ def argmin(self, axis=None, skipna=True, *args, **kwargs) -> int: skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - if not skipna and isna(delegate).any(): - return -1 - else: - return delegate.argmin() + return delegate.argmin(skipna) else: return nanops.nanargmin(delegate, skipna=skipna) From e73a3d119ae4d7bf86c039ad2b98c8ade5a721ea Mon Sep 17 00:00:00 2001 From: jy3515 Date: Fri, 27 Nov 2020 22:07:51 +0000 Subject: [PATCH 15/21] moved EA's skipna logic back to Series --- pandas/core/arrays/base.py | 24 ++++-------------------- pandas/core/base.py | 10 ++++++++-- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index dd8a4088c06b7..bd5cf43e19e9f 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -591,18 +591,13 @@ def argsort( mask=np.asarray(self.isna()), ) - def argmin(self, skipna: bool = True) -> int: + def argmin(self): """ Return the index of minimum value. In case of multiple occurrences of the minimum value, the index corresponding to the first occurrence is returned. - Parameters - ---------- - skipna : bool, default True - Exclude NA values when showing the result. - Returns ------- int @@ -611,23 +606,15 @@ def argmin(self, skipna: bool = True) -> int: -------- ExtensionArray.argmax """ - if not skipna and self.isna().any(): - return -1 - else: - return nargminmax(self, "argmin") + return nargminmax(self, "argmin") - def argmax(self, skipna: bool = True) -> int: + def argmax(self): """ Return the index of maximum value. In case of multiple occurrences of the maximum value, the index corresponding to the first occurrence is returned. - Parameters - ---------- - skipna : bool, default True - Exclude NA values when showing the result. - Returns ------- int @@ -636,10 +623,7 @@ def argmax(self, skipna: bool = True) -> int: -------- ExtensionArray.argmin """ - if not skipna and self.isna().any(): - return -1 - else: - return nargminmax(self, "argmax") + return nargminmax(self, "argmax") def fillna(self, value=None, method=None, limit=None): """ diff --git a/pandas/core/base.py b/pandas/core/base.py index 3d0b5e54d1438..8b1ee8a04b822 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -720,7 +720,10 @@ def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int: skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - return delegate.argmax(skipna) + if not skipna and isna(delegate).any(): + return -1 + else: + return delegate.argmax() else: return nanops.nanargmax(delegate, skipna=skipna) @@ -775,7 +778,10 @@ def argmin(self, axis=None, skipna=True, *args, **kwargs) -> int: skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - return delegate.argmin(skipna) + if not skipna and isna(delegate).any(): + return -1 + else: + return delegate.argmin() else: return nanops.nanargmin(delegate, skipna=skipna) From d78e28ccbbacf48542722ffa9e93a03837485950 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Fri, 27 Nov 2020 22:15:59 +0000 Subject: [PATCH 16/21] moved EA's skipna logic back to Series --- pandas/core/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index 8b1ee8a04b822..afc22a8446dce 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -720,7 +720,7 @@ def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int: skipna = nv.validate_argmax_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - if not skipna and isna(delegate).any(): + if not skipna and delegate.isna().any(): return -1 else: return delegate.argmax() @@ -778,7 +778,7 @@ def argmin(self, axis=None, skipna=True, *args, **kwargs) -> int: skipna = nv.validate_argmin_with_skipna(skipna, args, kwargs) if isinstance(delegate, ExtensionArray): - if not skipna and isna(delegate).any(): + if not skipna and delegate.isna().any(): return -1 else: return delegate.argmin() From 66f318785d66f62fc350171f82f8fab442da8f8c Mon Sep 17 00:00:00 2001 From: jy3515 Date: Sat, 5 Dec 2020 17:23:30 +0000 Subject: [PATCH 17/21] added whatsnew entry and extra tests --- pandas/tests/series/test_reductions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index dc391d3069531..ed9bdb7c343ab 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -162,3 +162,15 @@ def test_argminmax_idxminmax_with_datetime64_dtype(op_name, skipna, expected): assert np.isnan(result) else: assert result == expected + + +def test_github_issues(): + # GH#36566 + ser = Series("a", dtype="string").value_counts() + assert ser.idxmax() == "a" + + # GH#32749 + from pandas.tests.extension.decimal import DecimalArray, make_data + + ser = Series(DecimalArray(make_data()[:1])) + assert ser.idxmax() == 0 From 6f01069af6e86cf409caa2bdb850ab42011537e1 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Sat, 5 Dec 2020 19:47:05 +0000 Subject: [PATCH 18/21] moved tests to tests/reductions/rest_reductions.py --- pandas/tests/reductions/test_reductions.py | 54 +++++++++++++++++++ pandas/tests/series/test_reductions.py | 60 ---------------------- 2 files changed, 54 insertions(+), 60 deletions(-) diff --git a/pandas/tests/reductions/test_reductions.py b/pandas/tests/reductions/test_reductions.py index 8c2297699807d..e77357c31dfaa 100644 --- a/pandas/tests/reductions/test_reductions.py +++ b/pandas/tests/reductions/test_reductions.py @@ -856,6 +856,16 @@ def test_idxmax(self): result = s.idxmin() assert result == 1.1 + # GH#36566 + s = Series("a", dtype="string").value_counts() + assert s.idxmax() == "a" + + # GH#32749 + from pandas.tests.extension.decimal import DecimalArray, make_data + + s = Series(DecimalArray(make_data()[:1])) + assert s.idxmax() == 0 + def test_all_any(self): ts = tm.makeTimeSeries() bool_series = ts > 0 @@ -1007,6 +1017,27 @@ def test_idxminmax_with_inf(self): assert s.idxmax() == 0 np.isnan(s.idxmax(skipna=False)) + @pytest.mark.parametrize( + "op_name, skipna, expected", + [ + ("idxmax", True, "b"), + ("idxmin", True, "a"), + ("argmax", True, 1), + ("argmin", True, 0), + ("idxmax", False, np.nan), + ("idxmin", False, np.nan), + ("argmax", False, -1), + ("argmin", False, -1), + ], + ) + def test_argminmax_idxminmax(self, any_numeric_dtype, op_name, skipna, expected): + s = Series([1, 2, None], index=["a", "b", "c"], dtype=any_numeric_dtype) + result = getattr(s, op_name)(skipna=skipna) + if pd.isna(expected): + assert np.isnan(result) + else: + assert result == expected + class TestDatetime64SeriesReductions: # Note: the name TestDatetime64SeriesReductions indicates these tests @@ -1072,6 +1103,29 @@ def test_min_max_series(self): assert isinstance(result, Timestamp) assert result == exp + @pytest.mark.parametrize( + "op_name, skipna, expected", + [ + ("idxmax", True, "b"), + ("idxmin", True, "a"), + ("argmax", True, 1), + ("argmin", True, 0), + ("idxmax", False, np.nan), + ("idxmin", False, np.nan), + ("argmax", False, -1), + ("argmin", False, -1), + ], + ) + def test_argminmax_idxminmax(self, op_name, skipna, expected): + s = Series( + [datetime(2020, 1, 1), datetime(2020, 1, 2), None], index=["a", "b", "c"] + ) + result = getattr(s, op_name)(skipna=skipna) + if pd.isna(expected): + assert np.isnan(result) + else: + assert result == expected + class TestCategoricalSeriesReductions: # Note: the name TestCategoricalSeriesReductions indicates these tests diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index ed9bdb7c343ab..c3c58f29fcbf6 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -1,5 +1,3 @@ -from datetime import datetime - import numpy as np import pytest @@ -116,61 +114,3 @@ def test_validate_stat_keepdims(): ) with pytest.raises(ValueError, match=msg): np.sum(ser, keepdims=True) - - -@pytest.mark.parametrize( - "op_name, skipna, expected", - [ - ("idxmax", True, "b"), - ("idxmin", True, "a"), - ("argmax", True, 1), - ("argmin", True, 0), - ("idxmax", False, np.nan), - ("idxmin", False, np.nan), - ("argmax", False, -1), - ("argmin", False, -1), - ], -) -def test_argminmax_idxminmax(any_numeric_dtype, op_name, skipna, expected): - ser = Series([1, 2, None], index=["a", "b", "c"], dtype=any_numeric_dtype) - result = getattr(ser, op_name)(skipna=skipna) - if pd.isna(expected): - assert np.isnan(result) - else: - assert result == expected - - -@pytest.mark.parametrize( - "op_name, skipna, expected", - [ - ("idxmax", True, "b"), - ("idxmin", True, "a"), - ("argmax", True, 1), - ("argmin", True, 0), - ("idxmax", False, np.nan), - ("idxmin", False, np.nan), - ("argmax", False, -1), - ("argmin", False, -1), - ], -) -def test_argminmax_idxminmax_with_datetime64_dtype(op_name, skipna, expected): - ser = Series( - [datetime(2020, 1, 1), datetime(2020, 1, 2), None], index=["a", "b", "c"] - ) - result = getattr(ser, op_name)(skipna=skipna) - if pd.isna(expected): - assert np.isnan(result) - else: - assert result == expected - - -def test_github_issues(): - # GH#36566 - ser = Series("a", dtype="string").value_counts() - assert ser.idxmax() == "a" - - # GH#32749 - from pandas.tests.extension.decimal import DecimalArray, make_data - - ser = Series(DecimalArray(make_data()[:1])) - assert ser.idxmax() == 0 From 3540797bb1ff820e2d6385e9f901ecd37364b23d Mon Sep 17 00:00:00 2001 From: jy3515 Date: Mon, 28 Dec 2020 23:23:41 +0000 Subject: [PATCH 19/21] added 1.3 whatsnew entry --- doc/source/whatsnew/v1.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 35785613fb1e2..464e89abc6270 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -295,7 +295,7 @@ Sparse ExtensionArray ^^^^^^^^^^^^^^ -- +- 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`) - Other From 6d0b68ee4baa88db453ec6ef2139c4b8554c7078 Mon Sep 17 00:00:00 2001 From: jy3515 Date: Wed, 30 Dec 2020 19:21:10 +0000 Subject: [PATCH 20/21] moved and restructured tests --- pandas/core/series.py | 4 +- pandas/tests/extension/base/__init__.py | 1 + pandas/tests/extension/base/reduce.py | 8 +++ .../tests/extension/decimal/test_decimal.py | 4 ++ pandas/tests/extension/test_boolean.py | 4 ++ pandas/tests/extension/test_datetime.py | 14 +++++ pandas/tests/extension/test_floating.py | 4 ++ pandas/tests/extension/test_integer.py | 4 ++ pandas/tests/extension/test_numpy.py | 5 ++ pandas/tests/reductions/test_reductions.py | 54 ------------------- 10 files changed, 46 insertions(+), 56 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index dafc10d42c702..9fe0e8e356252 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2076,7 +2076,7 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmin(skipna=False) nan """ - i = self.argmin(None, skipna, *args, **kwargs) + i = self.argmin(None, skipna=skipna) if i == -1: return np.nan return self.index[i] @@ -2146,7 +2146,7 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs): >>> s.idxmax(skipna=False) nan """ - i = self.argmax(None, skipna, *args, **kwargs) + i = self.argmax(None, skipna=skipna) if i == -1: return np.nan return self.index[i] diff --git a/pandas/tests/extension/base/__init__.py b/pandas/tests/extension/base/__init__.py index 323cb843b2d74..63228cf752a09 100644 --- a/pandas/tests/extension/base/__init__.py +++ b/pandas/tests/extension/base/__init__.py @@ -58,6 +58,7 @@ class TestMyDtype(BaseDtypeTests): ) from .printing import BasePrintingTests # noqa from .reduce import ( # noqa + BaseArgReduceTests, BaseBooleanReduceTests, BaseNoReduceTests, BaseNumericReduceTests, diff --git a/pandas/tests/extension/base/reduce.py b/pandas/tests/extension/base/reduce.py index 55f8aca1b8ae0..2c587ff83be79 100644 --- a/pandas/tests/extension/base/reduce.py +++ b/pandas/tests/extension/base/reduce.py @@ -68,3 +68,11 @@ def test_reduce_series(self, data, all_boolean_reductions, skipna): op_name = all_boolean_reductions s = pd.Series(data) self.check_reduce(s, op_name, skipna) + + +class BaseArgReduceTests(BaseReduceTests): + @pytest.mark.parametrize("skipna", [True, False]) + @pytest.mark.parametrize("op_name", ["argmin", "argmax", "idxmin", "idxmax"]) + def test_reduce_series(self, data, op_name, skipna): + s = pd.Series(data) + self.check_reduce(s, op_name, skipna) diff --git a/pandas/tests/extension/decimal/test_decimal.py b/pandas/tests/extension/decimal/test_decimal.py index 233b658d29782..5a5a4824b1394 100644 --- a/pandas/tests/extension/decimal/test_decimal.py +++ b/pandas/tests/extension/decimal/test_decimal.py @@ -166,6 +166,10 @@ class TestBooleanReduce(Reduce, base.BaseBooleanReduceTests): pass +class TestArgReduce(base.BaseArgReduceTests): + pass + + class TestMethods(BaseDecimal, base.BaseMethodsTests): @pytest.mark.parametrize("dropna", [True, False]) @pytest.mark.xfail(reason="value_counts not implemented yet.") diff --git a/pandas/tests/extension/test_boolean.py b/pandas/tests/extension/test_boolean.py index ced7ea9261310..81a42a6ecd392 100644 --- a/pandas/tests/extension/test_boolean.py +++ b/pandas/tests/extension/test_boolean.py @@ -381,6 +381,10 @@ class TestBooleanReduce(base.BaseBooleanReduceTests): pass +class TestArgReduce(base.BaseArgReduceTests): + pass + + class TestPrinting(base.BasePrintingTests): pass diff --git a/pandas/tests/extension/test_datetime.py b/pandas/tests/extension/test_datetime.py index 0fde1e8a2fdb8..4a5558bb0c45d 100644 --- a/pandas/tests/extension/test_datetime.py +++ b/pandas/tests/extension/test_datetime.py @@ -4,6 +4,7 @@ from pandas.core.dtypes.dtypes import DatetimeTZDtype import pandas as pd +import pandas._testing as tm from pandas.core.arrays import DatetimeArray from pandas.tests.extension import base @@ -223,3 +224,16 @@ class TestGroupby(BaseDatetimeTests, base.BaseGroupbyTests): class TestPrinting(BaseDatetimeTests, base.BasePrintingTests): pass + + +class TestArgReduce(base.BaseArgReduceTests): + def check_reduce(self, s, op_name, skipna): + result = getattr(s, op_name)(skipna=skipna) + if not skipna and s.isna().any(): + if op_name in ["argmin", "argmax"]: + expected = -1 + else: + expected = np.nan + else: + expected = getattr(s.dropna().astype("int64"), op_name)(skipna=skipna) + tm.assert_almost_equal(result, expected) diff --git a/pandas/tests/extension/test_floating.py b/pandas/tests/extension/test_floating.py index c08c31e90fecc..e9275686eb55e 100644 --- a/pandas/tests/extension/test_floating.py +++ b/pandas/tests/extension/test_floating.py @@ -215,6 +215,10 @@ class TestBooleanReduce(base.BaseBooleanReduceTests): pass +class TestArgReduce(base.BaseArgReduceTests): + pass + + class TestPrinting(base.BasePrintingTests): pass diff --git a/pandas/tests/extension/test_integer.py b/pandas/tests/extension/test_integer.py index 99a32203053c6..a4c260686002c 100644 --- a/pandas/tests/extension/test_integer.py +++ b/pandas/tests/extension/test_integer.py @@ -249,6 +249,10 @@ class TestBooleanReduce(base.BaseBooleanReduceTests): pass +class TestArgReduce(base.BaseArgReduceTests): + pass + + class TestPrinting(base.BasePrintingTests): pass diff --git a/pandas/tests/extension/test_numpy.py b/pandas/tests/extension/test_numpy.py index 29790d14f93cc..80d3406a3ccaf 100644 --- a/pandas/tests/extension/test_numpy.py +++ b/pandas/tests/extension/test_numpy.py @@ -315,6 +315,11 @@ class TestBooleanReduce(BaseNumPyTests, base.BaseBooleanReduceTests): pass +@skip_nested +class TestArgReduce(base.BaseArgReduceTests): + pass + + class TestMissing(BaseNumPyTests, base.BaseMissingTests): @skip_nested def test_fillna_scalar(self, data_missing): diff --git a/pandas/tests/reductions/test_reductions.py b/pandas/tests/reductions/test_reductions.py index e77357c31dfaa..8c2297699807d 100644 --- a/pandas/tests/reductions/test_reductions.py +++ b/pandas/tests/reductions/test_reductions.py @@ -856,16 +856,6 @@ def test_idxmax(self): result = s.idxmin() assert result == 1.1 - # GH#36566 - s = Series("a", dtype="string").value_counts() - assert s.idxmax() == "a" - - # GH#32749 - from pandas.tests.extension.decimal import DecimalArray, make_data - - s = Series(DecimalArray(make_data()[:1])) - assert s.idxmax() == 0 - def test_all_any(self): ts = tm.makeTimeSeries() bool_series = ts > 0 @@ -1017,27 +1007,6 @@ def test_idxminmax_with_inf(self): assert s.idxmax() == 0 np.isnan(s.idxmax(skipna=False)) - @pytest.mark.parametrize( - "op_name, skipna, expected", - [ - ("idxmax", True, "b"), - ("idxmin", True, "a"), - ("argmax", True, 1), - ("argmin", True, 0), - ("idxmax", False, np.nan), - ("idxmin", False, np.nan), - ("argmax", False, -1), - ("argmin", False, -1), - ], - ) - def test_argminmax_idxminmax(self, any_numeric_dtype, op_name, skipna, expected): - s = Series([1, 2, None], index=["a", "b", "c"], dtype=any_numeric_dtype) - result = getattr(s, op_name)(skipna=skipna) - if pd.isna(expected): - assert np.isnan(result) - else: - assert result == expected - class TestDatetime64SeriesReductions: # Note: the name TestDatetime64SeriesReductions indicates these tests @@ -1103,29 +1072,6 @@ def test_min_max_series(self): assert isinstance(result, Timestamp) assert result == exp - @pytest.mark.parametrize( - "op_name, skipna, expected", - [ - ("idxmax", True, "b"), - ("idxmin", True, "a"), - ("argmax", True, 1), - ("argmin", True, 0), - ("idxmax", False, np.nan), - ("idxmin", False, np.nan), - ("argmax", False, -1), - ("argmin", False, -1), - ], - ) - def test_argminmax_idxminmax(self, op_name, skipna, expected): - s = Series( - [datetime(2020, 1, 1), datetime(2020, 1, 2), None], index=["a", "b", "c"] - ) - result = getattr(s, op_name)(skipna=skipna) - if pd.isna(expected): - assert np.isnan(result) - else: - assert result == expected - class TestCategoricalSeriesReductions: # Note: the name TestCategoricalSeriesReductions indicates these tests From 4f6d11152b2753598adb27efb160b59c55cf8bce Mon Sep 17 00:00:00 2001 From: jy3515 Date: Thu, 31 Dec 2020 00:49:13 +0000 Subject: [PATCH 21/21] moved tests to pandas/tests/extensions/methods.py --- pandas/tests/extension/base/__init__.py | 1 - pandas/tests/extension/base/methods.py | 21 +++++++++++++++++++ pandas/tests/extension/base/reduce.py | 8 ------- .../tests/extension/decimal/test_decimal.py | 4 ---- pandas/tests/extension/test_boolean.py | 4 ---- pandas/tests/extension/test_datetime.py | 14 ------------- pandas/tests/extension/test_floating.py | 4 ---- pandas/tests/extension/test_integer.py | 4 ---- pandas/tests/extension/test_numpy.py | 5 ----- 9 files changed, 21 insertions(+), 44 deletions(-) diff --git a/pandas/tests/extension/base/__init__.py b/pandas/tests/extension/base/__init__.py index 63228cf752a09..323cb843b2d74 100644 --- a/pandas/tests/extension/base/__init__.py +++ b/pandas/tests/extension/base/__init__.py @@ -58,7 +58,6 @@ class TestMyDtype(BaseDtypeTests): ) from .printing import BasePrintingTests # noqa from .reduce import ( # noqa - BaseArgReduceTests, BaseBooleanReduceTests, BaseNoReduceTests, BaseNumericReduceTests, diff --git a/pandas/tests/extension/base/methods.py b/pandas/tests/extension/base/methods.py index 1cc03d4f4f2bd..472e783c977f0 100644 --- a/pandas/tests/extension/base/methods.py +++ b/pandas/tests/extension/base/methods.py @@ -107,6 +107,27 @@ def test_argmin_argmax_all_na(self, method, data, na_value): with pytest.raises(ValueError, match=err_msg): getattr(data_na, method)() + @pytest.mark.parametrize( + "op_name, skipna, expected", + [ + ("idxmax", True, 0), + ("idxmin", True, 2), + ("argmax", True, 0), + ("argmin", True, 2), + ("idxmax", False, np.nan), + ("idxmin", False, np.nan), + ("argmax", False, -1), + ("argmin", False, -1), + ], + ) + def test_argreduce_series( + self, data_missing_for_sorting, op_name, skipna, expected + ): + # data_missing_for_sorting -> [B, NA, A] with A < B and NA missing. + ser = pd.Series(data_missing_for_sorting) + result = getattr(ser, op_name)(skipna=skipna) + tm.assert_almost_equal(result, expected) + @pytest.mark.parametrize( "na_position, expected", [ diff --git a/pandas/tests/extension/base/reduce.py b/pandas/tests/extension/base/reduce.py index 2c587ff83be79..55f8aca1b8ae0 100644 --- a/pandas/tests/extension/base/reduce.py +++ b/pandas/tests/extension/base/reduce.py @@ -68,11 +68,3 @@ def test_reduce_series(self, data, all_boolean_reductions, skipna): op_name = all_boolean_reductions s = pd.Series(data) self.check_reduce(s, op_name, skipna) - - -class BaseArgReduceTests(BaseReduceTests): - @pytest.mark.parametrize("skipna", [True, False]) - @pytest.mark.parametrize("op_name", ["argmin", "argmax", "idxmin", "idxmax"]) - def test_reduce_series(self, data, op_name, skipna): - s = pd.Series(data) - self.check_reduce(s, op_name, skipna) diff --git a/pandas/tests/extension/decimal/test_decimal.py b/pandas/tests/extension/decimal/test_decimal.py index 5a5a4824b1394..233b658d29782 100644 --- a/pandas/tests/extension/decimal/test_decimal.py +++ b/pandas/tests/extension/decimal/test_decimal.py @@ -166,10 +166,6 @@ class TestBooleanReduce(Reduce, base.BaseBooleanReduceTests): pass -class TestArgReduce(base.BaseArgReduceTests): - pass - - class TestMethods(BaseDecimal, base.BaseMethodsTests): @pytest.mark.parametrize("dropna", [True, False]) @pytest.mark.xfail(reason="value_counts not implemented yet.") diff --git a/pandas/tests/extension/test_boolean.py b/pandas/tests/extension/test_boolean.py index 81a42a6ecd392..ced7ea9261310 100644 --- a/pandas/tests/extension/test_boolean.py +++ b/pandas/tests/extension/test_boolean.py @@ -381,10 +381,6 @@ class TestBooleanReduce(base.BaseBooleanReduceTests): pass -class TestArgReduce(base.BaseArgReduceTests): - pass - - class TestPrinting(base.BasePrintingTests): pass diff --git a/pandas/tests/extension/test_datetime.py b/pandas/tests/extension/test_datetime.py index 4a5558bb0c45d..0fde1e8a2fdb8 100644 --- a/pandas/tests/extension/test_datetime.py +++ b/pandas/tests/extension/test_datetime.py @@ -4,7 +4,6 @@ from pandas.core.dtypes.dtypes import DatetimeTZDtype import pandas as pd -import pandas._testing as tm from pandas.core.arrays import DatetimeArray from pandas.tests.extension import base @@ -224,16 +223,3 @@ class TestGroupby(BaseDatetimeTests, base.BaseGroupbyTests): class TestPrinting(BaseDatetimeTests, base.BasePrintingTests): pass - - -class TestArgReduce(base.BaseArgReduceTests): - def check_reduce(self, s, op_name, skipna): - result = getattr(s, op_name)(skipna=skipna) - if not skipna and s.isna().any(): - if op_name in ["argmin", "argmax"]: - expected = -1 - else: - expected = np.nan - else: - expected = getattr(s.dropna().astype("int64"), op_name)(skipna=skipna) - tm.assert_almost_equal(result, expected) diff --git a/pandas/tests/extension/test_floating.py b/pandas/tests/extension/test_floating.py index e9275686eb55e..c08c31e90fecc 100644 --- a/pandas/tests/extension/test_floating.py +++ b/pandas/tests/extension/test_floating.py @@ -215,10 +215,6 @@ class TestBooleanReduce(base.BaseBooleanReduceTests): pass -class TestArgReduce(base.BaseArgReduceTests): - pass - - class TestPrinting(base.BasePrintingTests): pass diff --git a/pandas/tests/extension/test_integer.py b/pandas/tests/extension/test_integer.py index a4c260686002c..99a32203053c6 100644 --- a/pandas/tests/extension/test_integer.py +++ b/pandas/tests/extension/test_integer.py @@ -249,10 +249,6 @@ class TestBooleanReduce(base.BaseBooleanReduceTests): pass -class TestArgReduce(base.BaseArgReduceTests): - pass - - class TestPrinting(base.BasePrintingTests): pass diff --git a/pandas/tests/extension/test_numpy.py b/pandas/tests/extension/test_numpy.py index 80d3406a3ccaf..29790d14f93cc 100644 --- a/pandas/tests/extension/test_numpy.py +++ b/pandas/tests/extension/test_numpy.py @@ -315,11 +315,6 @@ class TestBooleanReduce(BaseNumPyTests, base.BaseBooleanReduceTests): pass -@skip_nested -class TestArgReduce(base.BaseArgReduceTests): - pass - - class TestMissing(BaseNumPyTests, base.BaseMissingTests): @skip_nested def test_fillna_scalar(self, data_missing):