diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 41d519e0765dc..bfe2dcee40d5e 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -590,6 +590,9 @@ Deprecations - :func:`pandas.api.types.is_categorical` is deprecated and will be removed in a future version; use `:func:pandas.api.types.is_categorical_dtype` instead (:issue:`33385`) - :meth:`Index.get_value` is deprecated and will be removed in a future version (:issue:`19728`) - :meth:`DateOffset.__call__` is deprecated and will be removed in a future version, use ``offset + other`` instead (:issue:`34171`) +- Indexing an :class:`Index` object with a float key is deprecated, and will + raise an ``IndexError`` in the future. You can manually convert to an integer key + instead (:issue:`34191`). - The ``squeeze`` keyword in the ``groupby`` function is deprecated and will be removed in a future version (:issue:`32380`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/common.py b/pandas/core/common.py index 4ec9c6f9e3a83..af24f8d707abd 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -9,6 +9,7 @@ from functools import partial import inspect from typing import Any, Collection, Iterable, List, Union +import warnings import numpy as np @@ -144,13 +145,15 @@ def is_bool_indexer(key: Any) -> bool: return False -def cast_scalar_indexer(val): +def cast_scalar_indexer(val, warn_float=False): """ To avoid numpy DeprecationWarnings, cast float to integer where valid. Parameters ---------- val : scalar + warn_float : bool, default False + If True, issue deprecation warning for a float indexer. Returns ------- @@ -158,6 +161,13 @@ def cast_scalar_indexer(val): """ # assumes lib.is_scalar(val) if lib.is_float(val) and val.is_integer(): + if warn_float: + warnings.warn( + "Indexing with a float is deprecated, and will raise an IndexError " + "in pandas 2.0. You can manually convert to an integer key instead.", + FutureWarning, + stacklevel=3, + ) return int(val) return val diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index a97407394e7d7..1cf319a11766d 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -4041,7 +4041,7 @@ def __getitem__(self, key): promote = self._shallow_copy if is_scalar(key): - key = com.cast_scalar_indexer(key) + key = com.cast_scalar_indexer(key, warn_float=True) return getitem(key) if isinstance(key, slice): diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index fa6ea60cec4c9..1f0df0d26a58e 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1896,7 +1896,7 @@ def __reduce__(self): def __getitem__(self, key): if is_scalar(key): - key = com.cast_scalar_indexer(key) + key = com.cast_scalar_indexer(key, warn_float=True) retval = [] for lev, level_codes in zip(self.levels, self.codes): diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index a049ac99f0e08..1d87c56ab959a 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -8,6 +8,7 @@ from pandas.util._decorators import cache_readonly from pandas.core.dtypes.common import ( + is_float, is_hashable, is_integer, is_iterator, @@ -1188,6 +1189,8 @@ def _post_plot_logic(self, ax, data): from matplotlib.ticker import FixedLocator def get_label(i): + if is_float(i) and i.is_integer(): + i = int(i) try: return pprint_thing(data.index[i]) except Exception: diff --git a/pandas/tests/indexes/test_indexing.py b/pandas/tests/indexes/test_indexing.py index a79bde9fd04e1..8910a3731cf8a 100644 --- a/pandas/tests/indexes/test_indexing.py +++ b/pandas/tests/indexes/test_indexing.py @@ -17,6 +17,7 @@ import pytest from pandas import Float64Index, Index, Int64Index, UInt64Index +import pandas._testing as tm class TestContains: @@ -82,3 +83,16 @@ def test_contains_with_float_index(self): assert 1.1 in float_index assert 1.0 not in float_index assert 1 not in float_index + + +@pytest.mark.parametrize( + "idx", [Index([1, 2, 3]), Index([0.1, 0.2, 0.3]), Index(["a", "b", "c"])] +) +def test_getitem_deprecated_float(idx): + # https://github.com/pandas-dev/pandas/issues/34191 + + with tm.assert_produces_warning(FutureWarning): + result = idx[1.0] + + expected = idx[1] + assert result == expected