Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEPR: deprecate Index.__getitem__ with float key #34193

Merged
3 changes: 3 additions & 0 deletions doc/source/whatsnew/v1.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usually we dont have a trailing period. double ticks on IndexError?

- The ``squeeze`` keyword in the ``groupby`` function is deprecated and will be removed in a future version (:issue:`32380`)

.. ---------------------------------------------------------------------------
Expand Down
12 changes: 11 additions & 1 deletion pandas/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from functools import partial
import inspect
from typing import Any, Collection, Iterable, List, Union
import warnings

import numpy as np

Expand Down Expand Up @@ -144,20 +145,29 @@ 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
-------
outval : scalar
"""
# 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

Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
3 changes: 3 additions & 0 deletions pandas/plotting/_matplotlib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really sure this is correct, but it's what is currently actually is being done (the conversion to int is just happening inside the Index.__getitem__). So this change is needed to preserve the current behaviour.

It's a bit strange that those xticks are floats, though. But maybe that's just a matplotlib behaviour.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is really strange ,what is i here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It are the values from ax.get_xticks(), see 5 lines below. I suppose matplotlib just always returns floats

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, just checked, matplotlib simply always returns floats. And it's only for the "integer floats" that we have a value in the index to put as xtick label.

try:
return pprint_thing(data.index[i])
except Exception:
Expand Down
14 changes: 14 additions & 0 deletions pandas/tests/indexes/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import pytest

from pandas import Float64Index, Index, Int64Index, UInt64Index
import pandas._testing as tm


class TestContains:
Expand Down Expand Up @@ -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"])]
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could use indices fixture?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, as the deprecation is only for those 3 index types

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