From 270dd2621576c69604919c6f9b324ecd4624e59a Mon Sep 17 00:00:00 2001 From: jreback Date: Sun, 13 Oct 2013 18:13:57 -0400 Subject: [PATCH] BUG: Bug in loc setting with multiple indexers and a rhs of a Series that needs broadcasting (GH5206) --- doc/source/release.rst | 2 ++ pandas/core/indexing.py | 16 ++++++++++++++-- pandas/tests/test_indexing.py | 11 +++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/doc/source/release.rst b/doc/source/release.rst index f899849475df8..2fb3a231660a4 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -594,6 +594,8 @@ Bug Fixes - Bug in comparing duplicate frames (:issue:`4421`) related - Bug in describe on duplicate frames - Bug in ``to_datetime`` with a format and ``coerce=True`` not raising (:issue:`5195`) + - Bug in ``loc`` setting with multiple indexers and a rhs of a Series that needs + broadcasting (:issue:`5206`) pandas 0.12.0 ------------- diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 69114166b3406..fa58d82a3b580 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -370,9 +370,11 @@ def _align_series(self, indexer, ser): if isinstance(indexer, tuple): aligners = [ not _is_null_slice(idx) for idx in indexer ] - single_aligner = sum(aligners) == 1 + sum_aligners = sum(aligners) + single_aligner = sum_aligners == 1 is_frame = self.obj.ndim == 2 is_panel = self.obj.ndim >= 3 + obj = self.obj # are we a single alignable value on a non-primary # dim (e.g. panel: 1,2, or frame: 0) ? @@ -387,7 +389,15 @@ def _align_series(self, indexer, ser): elif is_panel: single_aligner = single_aligner and (aligners[1] or aligners[2]) - obj = self.obj + # we have a frame, with multiple indexers on both axes; and a series, + # so need to broadcast (see GH5206) + if sum_aligners == self.ndim and all([ com._is_sequence(_) for _ in indexer ]): + + ser = ser.reindex(obj.axes[0][indexer[0].ravel()],copy=True).values + l = len(indexer[1].ravel()) + ser = np.tile(ser,l).reshape(l,-1).T + return ser + for i, idx in enumerate(indexer): ax = obj.axes[i] @@ -398,6 +408,8 @@ def _align_series(self, indexer, ser): new_ix = ax[idx] if not is_list_like(new_ix): new_ix = Index([new_ix]) + else: + new_ix = Index(new_ix.ravel()) if ser.index.equals(new_ix): return ser.values.copy() return ser.reindex(new_ix).values diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py index 6292c5874772f..c649e73184aa3 100644 --- a/pandas/tests/test_indexing.py +++ b/pandas/tests/test_indexing.py @@ -1037,6 +1037,17 @@ def test_multi_assign(self): df2.ix[mask, cols]= dft.ix[mask, cols].values assert_frame_equal(df2,expected) + # broadcasting on the rhs is required + df = DataFrame(dict(A = [1,2,0,0,0],B=[0,0,0,10,11],C=[0,0,0,10,11],D=[3,4,5,6,7])) + + expected = df.copy() + mask = expected['A'] == 0 + for col in ['A','B']: + expected.loc[mask,col] = df['D'] + + df.loc[df['A']==0,['A','B']] = df['D'] + assert_frame_equal(df,expected) + def test_ix_assign_column_mixed(self): # GH #1142 df = DataFrame(tm.getSeriesData())