diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index cb96c7093c005..1c9849730edd6 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -1113,6 +1113,7 @@ Indexing - Bug in :meth:`DataFrame.first_valid_index` and :meth:`DataFrame.last_valid_index` in presence of entire rows of NaNs in the middle of values (:issue:`20499`). - Bug in :class:`IntervalIndex` where some indexing operations were not supported for overlapping or non-monotonic ``uint64`` data (:issue:`20636`) - Bug in ``Series.is_unique`` where extraneous output in stderr is shown if Series contains objects with ``__ne__`` defined (:issue:`20661`) +- Bug in ``.loc`` assignment with a single-element list-like incorrectly assigns as a list (:issue:`19474`) - Bug in partial string indexing on a ``Series/DataFrame`` with a monotonic decreasing ``DatetimeIndex`` (:issue:`19362`) MultiIndex diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 5240a4703c242..2eb52ecc6bcc7 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -532,7 +532,8 @@ def setter(item, v): def can_do_equal_len(): """ return True if we have an equal len settable """ - if not len(labels) == 1 or not np.iterable(value): + if (not len(labels) == 1 or not np.iterable(value) or + is_scalar(plane_indexer[0])): return False l = len(value) diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index a5506abe8f355..f1178d44dbfe0 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -10,6 +10,7 @@ from pandas import Series, DataFrame, date_range, concat, isna from pandas.util import testing as tm from pandas.tests.indexing.common import Base +from pandas.api.types import is_scalar class TestiLoc(Base): @@ -526,6 +527,21 @@ def test_iloc_setitem_list_of_lists(self): B=[5, 6, 11, 13, 9])) tm.assert_frame_equal(df, expected) + @pytest.mark.parametrize( + 'indexer', [[0], slice(None, 1, None), np.array([0])]) + @pytest.mark.parametrize( + 'value', [['Z'], np.array(['Z'])]) + def test_iloc_setitem_with_scalar_index(self, indexer, value): + # GH #19474 + # assigning like "df.iloc[0, [0]] = ['Z']" should be evaluated + # elementwisely, not using "setter('A', ['Z'])". + + df = pd.DataFrame([[1, 2], [3, 4]], columns=['A', 'B']) + df.iloc[0, indexer] = value + result = df.iloc[0, 0] + + assert is_scalar(result) and result == 'Z' + def test_iloc_mask(self): # GH 3631, iloc with a mask (of a series) should raise diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 86a5a82441ee8..39f4d2b7bd395 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -11,6 +11,7 @@ from pandas import Series, DataFrame, Timestamp, date_range, MultiIndex, Index from pandas.util import testing as tm from pandas.tests.indexing.common import Base +from pandas.api.types import is_scalar class TestLoc(Base): @@ -555,6 +556,21 @@ def test_loc_setitem_frame_multiples(self): df.loc[2:4] = rhs tm.assert_frame_equal(df, expected) + @pytest.mark.parametrize( + 'indexer', [['A'], slice(None, 'A', None), np.array(['A'])]) + @pytest.mark.parametrize( + 'value', [['Z'], np.array(['Z'])]) + def test_loc_setitem_with_scalar_index(self, indexer, value): + # GH #19474 + # assigning like "df.loc[0, ['A']] = ['Z']" should be evaluated + # elementwisely, not using "setter('A', ['Z'])". + + df = pd.DataFrame([[1, 2], [3, 4]], columns=['A', 'B']) + df.loc[0, indexer] = value + result = df.loc[0, 'A'] + + assert is_scalar(result) and result == 'Z' + def test_loc_coerceion(self): # 12411