Skip to content

Commit 3886bf5

Browse files
Manual Backport PR pandas-dev#48057 on branch 1.4.x (REGR: fix regression in scalar setitem with setting a length-1 array-like) (pandas-dev#48161)
Backport PR pandas-dev#48057: REGR: fix regression in scalar setitem with setting a length-1 array-like Co-authored-by: Joris Van den Bossche <[email protected]>
1 parent 12b22b0 commit 3886bf5

File tree

3 files changed

+98
-2
lines changed

3 files changed

+98
-2
lines changed

doc/source/whatsnew/v1.4.4.rst

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Fixed regressions
1818
- Fixed regression in :func:`concat` materializing :class:`Index` during sorting even if :class:`Index` was already sorted (:issue:`47501`)
1919
- Fixed regression in :meth:`DataFrame.loc` not updating the cache correctly after values were set (:issue:`47867`)
2020
- Fixed regression in :meth:`DataFrame.loc` not aligning index in some cases when setting a :class:`DataFrame` (:issue:`47578`)
21+
- Fixed regression in :meth:`DataFrame.loc` setting a length-1 array like value to a single value in the DataFrame (:issue:`46268`)
2122
- Fixed regression in setting ``None`` or non-string value into a ``string``-dtype Series using a mask (:issue:`47628`)
2223
- Fixed regression in :func:`merge` throwing an error when passing a :class:`Series` with a multi-level name (:issue:`47946`)
2324
- Fixed regression in :meth:`DataFrame.eval` creating a copy when updating inplace (:issue:`47449`)

pandas/core/indexing.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1749,8 +1749,10 @@ def _setitem_with_indexer_split_path(self, indexer, value, name: str):
17491749
# We get here in one case via .loc with a all-False mask
17501750
pass
17511751

1752-
elif self._is_scalar_access(indexer):
1753-
# We are setting nested data
1752+
elif self._is_scalar_access(indexer) and is_object_dtype(
1753+
self.obj.dtypes[ilocs[0]]
1754+
):
1755+
# We are setting nested data, only possible for object dtype data
17541756
self._setitem_single_column(indexer[1], value, pi)
17551757

17561758
elif len(ilocs) == len(value):

pandas/tests/indexing/test_indexing.py

+93
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
""" test fancy indexing & misc """
22

3+
import array
34
from datetime import datetime
45
import re
56
import weakref
@@ -985,3 +986,95 @@ def test_extension_array_cross_section_converts():
985986

986987
result = df.iloc[0]
987988
tm.assert_series_equal(result, expected)
989+
990+
991+
@pytest.mark.parametrize(
992+
"value", [(0, 1), [0, 1], np.array([0, 1]), array.array("b", [0, 1])]
993+
)
994+
def test_scalar_setitem_with_nested_value(value):
995+
# For numeric data, we try to unpack and thus raise for mismatching length
996+
df = DataFrame({"A": [1, 2, 3]})
997+
msg = "|".join(
998+
[
999+
"Must have equal len keys and value",
1000+
"setting an array element with a sequence",
1001+
]
1002+
)
1003+
with pytest.raises(ValueError, match=msg):
1004+
df.loc[0, "B"] = value
1005+
1006+
# TODO For object dtype this happens as well, but should we rather preserve
1007+
# the nested data and set as such?
1008+
df = DataFrame({"A": [1, 2, 3], "B": np.array([1, "a", "b"], dtype=object)})
1009+
with pytest.raises(ValueError, match="Must have equal len keys and value"):
1010+
df.loc[0, "B"] = value
1011+
# if isinstance(value, np.ndarray):
1012+
# assert (df.loc[0, "B"] == value).all()
1013+
# else:
1014+
# assert df.loc[0, "B"] == value
1015+
1016+
1017+
@pytest.mark.parametrize(
1018+
"value", [(0, 1), [0, 1], np.array([0, 1]), array.array("b", [0, 1])]
1019+
)
1020+
def test_scalar_setitem_series_with_nested_value(value, indexer_sli):
1021+
# For numeric data, we try to unpack and thus raise for mismatching length
1022+
ser = Series([1, 2, 3])
1023+
with pytest.raises(ValueError, match="setting an array element with a sequence"):
1024+
indexer_sli(ser)[0] = value
1025+
1026+
# but for object dtype we preserve the nested data and set as such
1027+
ser = Series([1, "a", "b"], dtype=object)
1028+
indexer_sli(ser)[0] = value
1029+
if isinstance(value, np.ndarray):
1030+
assert (ser.loc[0] == value).all()
1031+
else:
1032+
assert ser.loc[0] == value
1033+
1034+
1035+
@pytest.mark.parametrize(
1036+
"value", [(0.0,), [0.0], np.array([0.0]), array.array("d", [0.0])]
1037+
)
1038+
def test_scalar_setitem_with_nested_value_length1(value):
1039+
# https://github.com/pandas-dev/pandas/issues/46268
1040+
1041+
# For numeric data, assigning length-1 array to scalar position gets unpacked
1042+
df = DataFrame({"A": [1, 2, 3]})
1043+
df.loc[0, "B"] = value
1044+
expected = DataFrame({"A": [1, 2, 3], "B": [0.0, np.nan, np.nan]})
1045+
tm.assert_frame_equal(df, expected)
1046+
1047+
# but for object dtype we preserve the nested data
1048+
df = DataFrame({"A": [1, 2, 3], "B": np.array([1, "a", "b"], dtype=object)})
1049+
df.loc[0, "B"] = value
1050+
if isinstance(value, np.ndarray):
1051+
assert (df.loc[0, "B"] == value).all()
1052+
else:
1053+
assert df.loc[0, "B"] == value
1054+
1055+
1056+
@pytest.mark.parametrize(
1057+
"value", [(0.0,), [0.0], np.array([0.0]), array.array("d", [0.0])]
1058+
)
1059+
def test_scalar_setitem_series_with_nested_value_length1(value, indexer_sli):
1060+
# For numeric data, assigning length-1 array to scalar position gets unpacked
1061+
# TODO this only happens in case of ndarray, should we make this consistent
1062+
# for all list-likes? (as happens for DataFrame.(i)loc, see test above)
1063+
ser = Series([1.0, 2.0, 3.0])
1064+
if isinstance(value, np.ndarray):
1065+
indexer_sli(ser)[0] = value
1066+
expected = Series([0.0, 2.0, 3.0])
1067+
tm.assert_series_equal(ser, expected)
1068+
else:
1069+
with pytest.raises(
1070+
ValueError, match="setting an array element with a sequence"
1071+
):
1072+
indexer_sli(ser)[0] = value
1073+
1074+
# but for object dtype we preserve the nested data
1075+
ser = Series([1, "a", "b"], dtype=object)
1076+
indexer_sli(ser)[0] = value
1077+
if isinstance(value, np.ndarray):
1078+
assert (ser.loc[0] == value).all()
1079+
else:
1080+
assert ser.loc[0] == value

0 commit comments

Comments
 (0)