Skip to content

DEPR: Series.__setitem__ with Float64Index falling back to positional #42215

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

Merged
merged 6 commits into from
Jul 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v1.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Deprecations
~~~~~~~~~~~~
- Deprecated :meth:`Index.is_type_compatible` (:issue:`42113`)
- Deprecated ``method`` argument in :meth:`Index.get_loc`, use ``index.get_indexer([label], method=...)`` instead (:issue:`42269`)
- Deprecated treating integer keys in :meth:`Series.__setitem__` as positional when the index is a :class:`Float64Index` not containing the key, a :class:`IntervalIndex` with no entries containing the key, or a :class:`MultiIndex` with leading :class:`Float64Index` level not containing the key (:issue:`33469`)
-

.. ---------------------------------------------------------------------------

Expand Down
11 changes: 11 additions & 0 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,17 @@ def __setitem__(self, key, value) -> None:
values = self._values
if is_integer(key) and self.index.inferred_type != "integer":
# positional setter
if not self.index._should_fallback_to_positional:
# GH#33469
warnings.warn(
"Treating integers as positional in Series.__setitem__ "
"with a Float64Index is deprecated. In a future version, "
"`series[an_int] = val` will insert a new key into the "
"Series. Use `series.iloc[an_int] = val` to treat the "
"key as positional.",
FutureWarning,
stacklevel=2,
)
values[key] = value
else:
# GH#12862 adding a new key to the Series
Expand Down
12 changes: 10 additions & 2 deletions pandas/tests/indexing/test_coercion.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,12 @@ def _assert_setitem_index_conversion(
):
"""test index's coercion triggered by assign key"""
temp = original_series.copy()
temp[loc_key] = 5
warn = None
if isinstance(loc_key, int) and temp.index.dtype == np.float64:
# GH#33469
warn = FutureWarning
with tm.assert_produces_warning(warn):
temp[loc_key] = 5
exp = pd.Series([1, 2, 3, 4, 5], index=expected_index)
tm.assert_series_equal(temp, exp)
# check dtype explicitly for sure
Expand Down Expand Up @@ -324,7 +329,10 @@ def test_setitem_index_float64(self, val, exp_dtype, request):
temp = obj.copy()
msg = "index 5 is out of bounds for axis 0 with size 4"
with pytest.raises(exp_dtype, match=msg):
temp[5] = 5
# GH#33469
depr_msg = "Treating integers as positional"
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
temp[5] = 5
mark = pytest.mark.xfail(reason="TODO_GH12747 The result must be float")
request.node.add_marker(mark)
exp_index = pd.Index([1.1, 2.1, 3.1, 4.1, val])
Expand Down
39 changes: 39 additions & 0 deletions pandas/tests/series/indexing/test_setitem.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
Categorical,
DatetimeIndex,
Index,
IntervalIndex,
MultiIndex,
NaT,
Series,
Expand Down Expand Up @@ -906,3 +907,41 @@ def val(self):
def is_inplace(self, obj):
# This is specific to the 4 cases currently implemented for this class.
return obj.dtype.kind != "i"


def test_setitem_int_as_positional_fallback_deprecation():
# GH#42215 deprecated falling back to positional on __setitem__ with an
# int not contained in the index
ser = Series([1, 2, 3, 4], index=[1.1, 2.1, 3.0, 4.1])
assert not ser.index._should_fallback_to_positional
# assert not ser.index.astype(object)._should_fallback_to_positional

with tm.assert_produces_warning(None):
# 3.0 is in our index, so future behavior is unchanged
ser[3] = 10
expected = Series([1, 2, 10, 4], index=ser.index)
tm.assert_series_equal(ser, expected)

msg = "Treating integers as positional in Series.__setitem__"
with tm.assert_produces_warning(FutureWarning, match=msg):
with pytest.raises(IndexError, match="index 5 is out of bounds"):
ser[5] = 5
# Once the deprecation is enforced, we will have
# expected = Series([1, 2, 3, 4, 5], index=[1.1, 2.1, 3.0, 4.1, 5.0])

ii = IntervalIndex.from_breaks(range(10))[::2]
ser2 = Series(range(len(ii)), index=ii)
expected2 = ser2.copy()
expected2.iloc[-1] = 9
with tm.assert_produces_warning(FutureWarning, match=msg):
ser2[4] = 9
tm.assert_series_equal(ser2, expected2)

mi = MultiIndex.from_product([ser.index, ["A", "B"]])
ser3 = Series(range(len(mi)), index=mi)
expected3 = ser3.copy()
expected3.iloc[4] = 99

with tm.assert_produces_warning(FutureWarning, match=msg):
ser3[4] = 99
tm.assert_series_equal(ser3, expected3)
2 changes: 1 addition & 1 deletion pandas/tests/series/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,7 @@ def test_none_comparison(series_with_simple_index):

# bug brought up by #1079
# changed from TypeError in 0.17.0
series[0] = np.nan
series.iloc[0] = np.nan

# noinspection PyComparisonWithNone
result = series == None # noqa
Expand Down