Skip to content

Commit 676bb50

Browse files
Lucas KushnerTomAugspurger
Lucas Kushner
authored andcommitted
Deprecating Series.argmin and Series.argmax (pandas-dev#16830)
Added statements about correcting behavior in future commit Add reference to github ticket Fixing placement of github comment Made test code more explicit Fixing unrelated tests that are also throwing warnings Updating whatsnew to give more detail about deprecation Fixing whatsnew and breaking out tests to catch warnings Additional comments and more concise whatsnew Updating deprecate decorator to support custom message DOC: Update docstrings, depr message, and whatsnew
1 parent 3795272 commit 676bb50

File tree

6 files changed

+107
-28
lines changed

6 files changed

+107
-28
lines changed

doc/source/whatsnew/v0.21.0.txt

+24-1
Original file line numberDiff line numberDiff line change
@@ -439,12 +439,35 @@ Other API Changes
439439

440440
Deprecations
441441
~~~~~~~~~~~~
442-
- :func:`read_excel()` has deprecated ``sheetname`` in favor of ``sheet_name`` for consistency with ``.to_excel()`` (:issue:`10559`).
443442

443+
- :func:`read_excel()` has deprecated ``sheetname`` in favor of ``sheet_name`` for consistency with ``.to_excel()`` (:issue:`10559`).
444444
- ``pd.options.html.border`` has been deprecated in favor of ``pd.options.display.html.border`` (:issue:`15793`).
445445

446446
- :func:`SeriesGroupBy.nth` has deprecated ``True`` in favor of ``'all'`` for its kwarg ``dropna`` (:issue:`11038`).
447447

448+
Series.argmax and Series.argmin
449+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
450+
451+
- The behavior of :func:`Series.argmax` has been deprecated in favor of :func:`Series.idxmax` (:issue:`16830`)
452+
- The behavior of :func:`Series.argmin` has been deprecated in favor of :func:`Series.idxmin` (:issue:`16830`)
453+
454+
For compatibility with NumPy arrays, ``pd.Series`` implements ``argmax`` and
455+
``argmin``. Since pandas 0.13 :func:`Series.argmax` and
456+
:func:`Series.argmin` returned the *label* of the maximum and minimum,
457+
rather than the *position*.
458+
459+
Since returning the positional ``argmax`` and ``argmin`` can be useful,
460+
we've deprecated the current behavior of :func:`Series.argmax` and
461+
:func:`Series.argmin`. Using either of these will emit a ``FutureWarning``.
462+
463+
If you were using ``Series.argmin`` and ``Series.argmax``, please switch to using
464+
``idxmin`` and ``idxmax``. In the future, ``Series.argmin`` will return the
465+
*position* of the minimum. Likewise for ``Series.argmax``.
466+
467+
Until this functionality is implemented in the future, we recommend using
468+
``Series.values.argmin()`` and ``Series.values.argmax()`` for returning
469+
the positional ``argmin`` and ``argmax``.
470+
448471
.. _whatsnew_0210.prior_deprecations:
449472

450473
Removal of prior version deprecations/changes

pandas/core/series.py

+22-7
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
import pandas.core.common as com
6969
import pandas.core.nanops as nanops
7070
import pandas.io.formats.format as fmt
71-
from pandas.util._decorators import Appender, deprecate_kwarg, Substitution
71+
from pandas.util._decorators import (
72+
Appender, deprecate, deprecate_kwarg, Substitution)
7273
from pandas.util._validators import validate_bool_kwarg
7374

7475
from pandas._libs import index as libindex, tslib as libts, lib, iNaT
@@ -1271,7 +1272,7 @@ def duplicated(self, keep='first'):
12711272

12721273
def idxmin(self, axis=None, skipna=True, *args, **kwargs):
12731274
"""
1274-
Index of first occurrence of minimum of values.
1275+
Index *label* of the first occurrence of minimum of values.
12751276
12761277
Parameters
12771278
----------
@@ -1284,7 +1285,9 @@ def idxmin(self, axis=None, skipna=True, *args, **kwargs):
12841285
12851286
Notes
12861287
-----
1287-
This method is the Series version of ``ndarray.argmin``.
1288+
This method is the Series version of ``ndarray.argmin``. This method
1289+
returns the label of the minimum, while ``ndarray.argmin`` returns
1290+
the position. To get the position, use ``series.values.argmin()``.
12881291
12891292
See Also
12901293
--------
@@ -1299,7 +1302,7 @@ def idxmin(self, axis=None, skipna=True, *args, **kwargs):
12991302

13001303
def idxmax(self, axis=None, skipna=True, *args, **kwargs):
13011304
"""
1302-
Index of first occurrence of maximum of values.
1305+
Index *label* of the first occurrence of maximum of values.
13031306
13041307
Parameters
13051308
----------
@@ -1312,7 +1315,9 @@ def idxmax(self, axis=None, skipna=True, *args, **kwargs):
13121315
13131316
Notes
13141317
-----
1315-
This method is the Series version of ``ndarray.argmax``.
1318+
This method is the Series version of ``ndarray.argmax``. This method
1319+
returns the label of the maximum, while ``ndarray.argmax`` returns
1320+
the position. To get the position, use ``series.values.argmax()``.
13161321
13171322
See Also
13181323
--------
@@ -1326,8 +1331,18 @@ def idxmax(self, axis=None, skipna=True, *args, **kwargs):
13261331
return self.index[i]
13271332

13281333
# ndarray compat
1329-
argmin = idxmin
1330-
argmax = idxmax
1334+
argmin = deprecate('argmin', idxmin,
1335+
msg="'argmin' is deprecated. Use 'idxmin' instead. "
1336+
"The behavior of 'argmin' will be corrected to "
1337+
"return the positional minimum in the future. "
1338+
"Use 'series.values.argmin' to get the position of "
1339+
"the minimum now.")
1340+
argmax = deprecate('argmax', idxmax,
1341+
msg="'argmax' is deprecated. Use 'idxmax' instead. "
1342+
"The behavior of 'argmax' will be corrected to "
1343+
"return the positional maximum in the future. "
1344+
"Use 'series.values.argmax' to get the position of "
1345+
"the maximum now.")
13311346

13321347
def round(self, decimals=0, *args, **kwargs):
13331348
"""

pandas/tests/series/test_analytics.py

+44-15
Original file line numberDiff line numberDiff line change
@@ -1242,16 +1242,31 @@ def test_idxmin(self):
12421242
result = s.idxmin()
12431243
assert result == 1
12441244

1245-
def test_numpy_argmin(self):
1246-
# argmin is aliased to idxmin
1245+
def test_numpy_argmin_deprecated(self):
1246+
# See gh-16830
12471247
data = np.random.randint(0, 11, size=10)
1248-
result = np.argmin(Series(data))
1249-
assert result == np.argmin(data)
1248+
1249+
s = Series(data)
1250+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
1251+
# The deprecation of Series.argmin also causes a deprecation
1252+
# warning when calling np.argmin. This behavior is temporary
1253+
# until the implemention of Series.argmin is corrected.
1254+
result = np.argmin(s)
1255+
expected = np.argmin(data)
1256+
assert result == expected
1257+
1258+
with tm.assert_produces_warning(FutureWarning):
1259+
# argmin is aliased to idxmin
1260+
result = s.argmin()
1261+
expected = s.idxmin()
1262+
assert result == expected
12501263

12511264
if not _np_version_under1p10:
1252-
msg = "the 'out' parameter is not supported"
1253-
tm.assert_raises_regex(ValueError, msg, np.argmin,
1254-
Series(data), out=data)
1265+
with tm.assert_produces_warning(FutureWarning,
1266+
check_stacklevel=False):
1267+
msg = "the 'out' parameter is not supported"
1268+
tm.assert_raises_regex(ValueError, msg, np.argmin,
1269+
s, out=data)
12551270

12561271
def test_idxmax(self):
12571272
# test idxmax
@@ -1297,17 +1312,31 @@ def test_idxmax(self):
12971312
result = s.idxmin()
12981313
assert result == 1.1
12991314

1300-
def test_numpy_argmax(self):
1301-
1302-
# argmax is aliased to idxmax
1315+
def test_numpy_argmax_deprecated(self):
1316+
# See gh-16830
13031317
data = np.random.randint(0, 11, size=10)
1304-
result = np.argmax(Series(data))
1305-
assert result == np.argmax(data)
1318+
1319+
s = Series(data)
1320+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
1321+
# The deprecation of Series.argmax also causes a deprecation
1322+
# warning when calling np.argmax. This behavior is temporary
1323+
# until the implemention of Series.argmax is corrected.
1324+
result = np.argmax(s)
1325+
expected = np.argmax(data)
1326+
assert result == expected
1327+
1328+
with tm.assert_produces_warning(FutureWarning):
1329+
# argmax is aliased to idxmax
1330+
result = s.argmax()
1331+
expected = s.idxmax()
1332+
assert result == expected
13061333

13071334
if not _np_version_under1p10:
1308-
msg = "the 'out' parameter is not supported"
1309-
tm.assert_raises_regex(ValueError, msg, np.argmax,
1310-
Series(data), out=data)
1335+
with tm.assert_produces_warning(FutureWarning,
1336+
check_stacklevel=False):
1337+
msg = "the 'out' parameter is not supported"
1338+
tm.assert_raises_regex(ValueError, msg, np.argmax,
1339+
s, out=data)
13111340

13121341
def test_ptp(self):
13131342
N = 1000

pandas/tests/series/test_api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ def test_ndarray_compat(self):
345345
index=date_range('1/1/2000', periods=1000))
346346

347347
def f(x):
348-
return x[x.argmax()]
348+
return x[x.idxmax()]
349349

350350
result = tsdf.apply(f)
351351
expected = tsdf.max()

pandas/tests/sparse/test_series.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -1379,11 +1379,21 @@ def test_numpy_func_call(self):
13791379
# numpy passes in 'axis=None' or `axis=-1'
13801380
funcs = ['sum', 'cumsum', 'var', 'mean',
13811381
'prod', 'cumprod', 'std', 'argsort',
1382-
'argmin', 'argmax', 'min', 'max']
1382+
'min', 'max']
13831383
for func in funcs:
13841384
for series in ('bseries', 'zbseries'):
13851385
getattr(np, func)(getattr(self, series))
13861386

1387+
def test_deprecated_numpy_func_call(self):
1388+
# NOTE: These should be add to the 'test_numpy_func_call' test above
1389+
# once the behavior of argmin/argmax is corrected.
1390+
funcs = ['argmin', 'argmax']
1391+
for func in funcs:
1392+
for series in ('bseries', 'zbseries'):
1393+
with tm.assert_produces_warning(FutureWarning,
1394+
check_stacklevel=False):
1395+
getattr(np, func)(getattr(self, series))
1396+
13871397

13881398
@pytest.mark.parametrize(
13891399
'datetime_type', (np.datetime64,

pandas/util/_decorators.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
def deprecate(name, alternative, alt_name=None, klass=None,
10-
stacklevel=2):
10+
stacklevel=2, msg=None):
1111
"""
1212
Return a new function that emits a deprecation warning on use.
1313
@@ -21,14 +21,16 @@ def deprecate(name, alternative, alt_name=None, klass=None,
2121
Name to use in preference of alternative.__name__
2222
klass : Warning, default FutureWarning
2323
stacklevel : int, default 2
24+
msg : str
25+
The message to display in the warning.
26+
Default is '{name} is deprecated. Use {alt_name} instead.'
2427
"""
2528

2629
alt_name = alt_name or alternative.__name__
2730
klass = klass or FutureWarning
31+
msg = msg or "{} is deprecated. Use {} instead".format(name, alt_name)
2832

2933
def wrapper(*args, **kwargs):
30-
msg = "{name} is deprecated. Use {alt_name} instead".format(
31-
name=name, alt_name=alt_name)
3234
warnings.warn(msg, klass, stacklevel=stacklevel)
3335
return alternative(*args, **kwargs)
3436
return wrapper

0 commit comments

Comments
 (0)