Skip to content

Commit 97ed8ee

Browse files
authored
DEPR: Series.__setitem__ with Float64Index falling back to positional (#42215)
1 parent 9a6ce3c commit 97ed8ee

File tree

5 files changed

+63
-3
lines changed

5 files changed

+63
-3
lines changed

doc/source/whatsnew/v1.4.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ Deprecations
9898
~~~~~~~~~~~~
9999
- Deprecated :meth:`Index.is_type_compatible` (:issue:`42113`)
100100
- Deprecated ``method`` argument in :meth:`Index.get_loc`, use ``index.get_indexer([label], method=...)`` instead (:issue:`42269`)
101+
- 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`)
102+
-
101103

102104
.. ---------------------------------------------------------------------------
103105

pandas/core/series.py

+11
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,17 @@ def __setitem__(self, key, value) -> None:
10671067
values = self._values
10681068
if is_integer(key) and self.index.inferred_type != "integer":
10691069
# positional setter
1070+
if not self.index._should_fallback_to_positional:
1071+
# GH#33469
1072+
warnings.warn(
1073+
"Treating integers as positional in Series.__setitem__ "
1074+
"with a Float64Index is deprecated. In a future version, "
1075+
"`series[an_int] = val` will insert a new key into the "
1076+
"Series. Use `series.iloc[an_int] = val` to treat the "
1077+
"key as positional.",
1078+
FutureWarning,
1079+
stacklevel=2,
1080+
)
10701081
values[key] = value
10711082
else:
10721083
# GH#12862 adding a new key to the Series

pandas/tests/indexing/test_coercion.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,12 @@ def _assert_setitem_index_conversion(
273273
):
274274
"""test index's coercion triggered by assign key"""
275275
temp = original_series.copy()
276-
temp[loc_key] = 5
276+
warn = None
277+
if isinstance(loc_key, int) and temp.index.dtype == np.float64:
278+
# GH#33469
279+
warn = FutureWarning
280+
with tm.assert_produces_warning(warn):
281+
temp[loc_key] = 5
277282
exp = pd.Series([1, 2, 3, 4, 5], index=expected_index)
278283
tm.assert_series_equal(temp, exp)
279284
# check dtype explicitly for sure
@@ -324,7 +329,10 @@ def test_setitem_index_float64(self, val, exp_dtype, request):
324329
temp = obj.copy()
325330
msg = "index 5 is out of bounds for axis 0 with size 4"
326331
with pytest.raises(exp_dtype, match=msg):
327-
temp[5] = 5
332+
# GH#33469
333+
depr_msg = "Treating integers as positional"
334+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
335+
temp[5] = 5
328336
mark = pytest.mark.xfail(reason="TODO_GH12747 The result must be float")
329337
request.node.add_marker(mark)
330338
exp_index = pd.Index([1.1, 2.1, 3.1, 4.1, val])

pandas/tests/series/indexing/test_setitem.py

+39
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Categorical,
1111
DatetimeIndex,
1212
Index,
13+
IntervalIndex,
1314
MultiIndex,
1415
NaT,
1516
Series,
@@ -906,3 +907,41 @@ def val(self):
906907
def is_inplace(self, obj):
907908
# This is specific to the 4 cases currently implemented for this class.
908909
return obj.dtype.kind != "i"
910+
911+
912+
def test_setitem_int_as_positional_fallback_deprecation():
913+
# GH#42215 deprecated falling back to positional on __setitem__ with an
914+
# int not contained in the index
915+
ser = Series([1, 2, 3, 4], index=[1.1, 2.1, 3.0, 4.1])
916+
assert not ser.index._should_fallback_to_positional
917+
# assert not ser.index.astype(object)._should_fallback_to_positional
918+
919+
with tm.assert_produces_warning(None):
920+
# 3.0 is in our index, so future behavior is unchanged
921+
ser[3] = 10
922+
expected = Series([1, 2, 10, 4], index=ser.index)
923+
tm.assert_series_equal(ser, expected)
924+
925+
msg = "Treating integers as positional in Series.__setitem__"
926+
with tm.assert_produces_warning(FutureWarning, match=msg):
927+
with pytest.raises(IndexError, match="index 5 is out of bounds"):
928+
ser[5] = 5
929+
# Once the deprecation is enforced, we will have
930+
# expected = Series([1, 2, 3, 4, 5], index=[1.1, 2.1, 3.0, 4.1, 5.0])
931+
932+
ii = IntervalIndex.from_breaks(range(10))[::2]
933+
ser2 = Series(range(len(ii)), index=ii)
934+
expected2 = ser2.copy()
935+
expected2.iloc[-1] = 9
936+
with tm.assert_produces_warning(FutureWarning, match=msg):
937+
ser2[4] = 9
938+
tm.assert_series_equal(ser2, expected2)
939+
940+
mi = MultiIndex.from_product([ser.index, ["A", "B"]])
941+
ser3 = Series(range(len(mi)), index=mi)
942+
expected3 = ser3.copy()
943+
expected3.iloc[4] = 99
944+
945+
with tm.assert_produces_warning(FutureWarning, match=msg):
946+
ser3[4] = 99
947+
tm.assert_series_equal(ser3, expected3)

pandas/tests/series/test_arithmetic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@ def test_none_comparison(series_with_simple_index):
874874

875875
# bug brought up by #1079
876876
# changed from TypeError in 0.17.0
877-
series[0] = np.nan
877+
series.iloc[0] = np.nan
878878

879879
# noinspection PyComparisonWithNone
880880
result = series == None # noqa

0 commit comments

Comments
 (0)