Skip to content

Commit 3a148a7

Browse files
committed
API: allow scalar setting/getting via float indexer on integer indexes
closes pandas-dev#12333
1 parent 07c84d5 commit 3a148a7

18 files changed

+1260
-1120
lines changed

doc/source/advanced.rst

+7-4
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,10 @@ values NOT in the categories, similarly to how you can reindex ANY pandas index.
717717
Int64Index and RangeIndex
718718
~~~~~~~~~~~~~~~~~~~~~~~~~
719719
720+
.. warning::
721+
722+
Indexing on an integer-based Index with floats has been clarified in 0.18.0, for a summary of the changes, see :ref:`here <whatsnew_0180.float_indexers>`.
723+
720724
``Int64Index`` is a fundamental basic index in *pandas*. This is an Immutable array implementing an ordered, sliceable set.
721725
Prior to 0.18.0, the ``Int64Index`` would provide the default index for all ``NDFrame`` objects.
722726
@@ -736,7 +740,6 @@ Float64Index
736740
operations by about 30x and boolean indexing operations on the
737741
``Float64Index`` itself are about 2x as fast.
738742
739-
740743
.. versionadded:: 0.13.0
741744
742745
By default a ``Float64Index`` will be automatically created when passing floating, or mixed-integer-floating values in index creation.
@@ -797,12 +800,12 @@ In non-float indexes, slicing using floats will raise a ``TypeError``
797800
798801
.. warning::
799802
800-
Using a scalar float indexer has been removed in 0.18.0, so the following will raise a ``TypeError``
803+
Using a scalar float indexer for ``.iloc`` has been removed in 0.18.0, so the following will raise a ``TypeError``
801804
802805
.. code-block:: python
803806
804-
In [3]: pd.Series(range(5))[3.0]
805-
TypeError: cannot do label indexing on <class 'pandas.indexes.range.RangeIndex'> with these indexers [3.0] of <type 'float'>
807+
In [3]: pd.Series(range(5)).iloc[3.0]
808+
TypeError: cannot do positional indexing on <class 'pandas.indexes.range.RangeIndex'> with these indexers [3.0] of <type 'float'>
806809
807810
Further the treatment of ``.ix`` with a float indexer on a non-float index, will be label based, and thus coerce the index.
808811

doc/source/indexing.rst

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ advanced indexing.
5353
but instead subclass ``PandasObject``, similarly to the rest of the pandas objects. This should be
5454
a transparent change with only very limited API implications (See the :ref:`Internal Refactoring <whatsnew_0150.refactoring>`)
5555

56+
.. warning::
57+
58+
Indexing on an integer-based Index with floats has been clarified in 0.18.0, for a summary of the changes, see :ref:`here <whatsnew_0180.float_indexers>`.
59+
5660
See the :ref:`MultiIndex / Advanced Indexing <advanced>` for ``MultiIndex`` and more advanced indexing documentation.
5761

5862
See the :ref:`cookbook<cookbook.selection>` for some advanced strategies

doc/source/whatsnew/v0.18.0.txt

+44-20
Original file line numberDiff line numberDiff line change
@@ -1024,11 +1024,11 @@ Removal of deprecated float indexers
10241024
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10251025

10261026
In :issue:`4892` indexing with floating point numbers on a non-``Float64Index`` was deprecated (in version 0.14.0).
1027-
In 0.18.0, this deprecation warning is removed and these will now raise a ``TypeError``. (:issue:`12165`)
1027+
In 0.18.0, this deprecation warning is removed and these will now raise a ``TypeError``. (:issue:`12165`, :issue:`12333`)
10281028

10291029
.. ipython:: python
10301030

1031-
s = pd.Series([1,2,3])
1031+
s = pd.Series([1, 2, 3], index=[4, 5, 6])
10321032
s
10331033
s2 = pd.Series([1, 2, 3], index=list('abc'))
10341034
s2
@@ -1037,15 +1037,18 @@ Previous Behavior:
10371037

10381038
.. code-block:: python
10391039

1040-
In [2]: s[1.0]
1040+
# this is label indexing
1041+
In [2]: s[5.0]
10411042
FutureWarning: scalar indexers for index type Int64Index should be integers and not floating point
10421043
Out[2]: 2
10431044

1045+
# this is positional indexing
10441046
In [3]: s.iloc[1.0]
10451047
FutureWarning: scalar indexers for index type Int64Index should be integers and not floating point
10461048
Out[3]: 2
10471049

1048-
In [4]: s.loc[1.0]
1050+
# this is label indexing
1051+
In [4]: s.loc[5.0]
10491052
FutureWarning: scalar indexers for index type Int64Index should be integers and not floating point
10501053
Out[4]: 2
10511054

@@ -1062,33 +1065,54 @@ Previous Behavior:
10621065

10631066
New Behavior:
10641067

1068+
For iloc, getting & setting via a float scalar will always raise.
1069+
10651070
.. code-block:: python
10661071

1067-
In [2]: s[1.0]
1068-
TypeError: cannot do label indexing on <class 'pandas.indexes.range.RangeIndex'> with these indexers [1.0] of <type 'float'>
1072+
In [3]: s.iloc[2.0]
1073+
TypeError: cannot do label indexing on <class 'pandas.indexes.numeric.Int64Index'> with these indexers [2.0] of <type 'float'>
10691074

1070-
In [3]: s.iloc[1.0]
1071-
TypeError: cannot do label indexing on <class 'pandas.indexes.range.RangeIndex'> with these indexers [1.0] of <type 'float'>
1075+
Other indexers will coerce to a like integer for both getting and setting. The ``FutureWarning`` has been dropped for ``.loc``, ``.ix`` and ``[]``.
10721076

1073-
In [4]: s.loc[1.0]
1074-
TypeError: cannot do label indexing on <class 'pandas.indexes.range.RangeIndex'> with these indexers [1.0] of <type 'float'>
1077+
.. ipython:: python
10751078

1076-
# .ix will now cause this to be a label lookup and coerce to and Index
1077-
In [5]: s2.ix[1.0] = 10
1079+
s[5.0]
1080+
s.loc[5.0]
1081+
s.ix[5.0]
10781082

1079-
In [6]: s2
1080-
Out[3]:
1081-
a 1
1082-
b 2
1083-
c 3
1084-
1.0 10
1085-
dtype: int64
1083+
and setting
1084+
1085+
.. ipython:: python
1086+
1087+
s_copy = s.copy()
1088+
s_copy[5.0] = 10
1089+
s_copy
1090+
s_copy = s.copy()
1091+
s_copy.loc[5.0] = 10
1092+
s_copy
1093+
s_copy = s.copy()
1094+
s_copy.ix[5.0] = 10
1095+
s_copy
1096+
1097+
Slicing will also coerce integer-like floats to integers for a non-``Float64Index``.
1098+
1099+
.. ipython:: python
1100+
1101+
s.loc[5.0:6]
1102+
s.ix[5.0:6]
1103+
1104+
Note that for floats that are NOT coercible to ints, the label based bounds will be excluded
1105+
1106+
.. ipython:: python
1107+
1108+
s.loc[5.1:6]
1109+
s.ix[5.1:6]
10861110

10871111
Float indexing on a ``Float64Index`` is unchanged.
10881112

10891113
.. ipython:: python
10901114

1091-
s = pd.Series([1,2,3],index=np.arange(3.))
1115+
s = pd.Series([1, 2, 3], index=np.arange(3.))
10921116
s[1.0]
10931117
s[1.0:2.5]
10941118

pandas/core/frame.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2016,7 +2016,7 @@ def _getitem_array(self, key):
20162016
# with all other indexing behavior
20172017
if isinstance(key, Series) and not key.index.equals(self.index):
20182018
warnings.warn("Boolean Series key will be reindexed to match "
2019-
"DataFrame index.", UserWarning)
2019+
"DataFrame index.", UserWarning, stacklevel=3)
20202020
elif len(key) != len(self.index):
20212021
raise ValueError('Item wrong length %d instead of %d.' %
20222022
(len(key), len(self.index)))

pandas/core/indexing.py

+4
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,10 @@ def _getitem_axis(self, key, axis=0):
995995

996996
return self._getitem_iterable(key, axis=axis)
997997
else:
998+
999+
# maybe coerce a float scalar to integer
1000+
key = labels._maybe_cast_indexer(key)
1001+
9981002
if is_integer(key):
9991003
if axis == 0 and isinstance(labels, MultiIndex):
10001004
try:

0 commit comments

Comments
 (0)