Skip to content

Commit a0bef0b

Browse files
committed
Merge pull request #8457 from behzadnouri/align-rhs
BUG: type diversity breaks alignment
2 parents 9872f31 + 30246a7 commit a0bef0b

File tree

4 files changed

+66
-18
lines changed

4 files changed

+66
-18
lines changed

doc/source/v0.15.0.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,3 +1011,4 @@ Bug Fixes
10111011
- Bug in Index.intersection on non-monotonic non-unique indexes (:issue:`8362`).
10121012
- Bug in masked series assignment where mismatching types would break alignment (:issue:`8387`)
10131013
- Bug in NDFrame.equals gives false negatives with dtype=object (:issue:`8437`)
1014+
- Bug in assignment with indexer where type diversity would break alignment (:issue:`8258`)

pandas/core/indexing.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -439,16 +439,10 @@ def can_do_equal_len():
439439
if isinstance(value, ABCDataFrame) and value.ndim > 1:
440440

441441
for item in labels:
442-
443442
# align to
444-
if item in value:
445-
v = value[item]
446-
i = self.obj[item].index
447-
v = v.reindex(i & v.index)
448-
449-
setter(item, v.values)
450-
else:
451-
setter(item, np.nan)
443+
v = np.nan if item not in value else \
444+
self._align_series(indexer[0], value[item])
445+
setter(item, v)
452446

453447
# we have an equal len ndarray/convertible to our labels
454448
elif np.array(value).ndim == 2:
@@ -511,6 +505,10 @@ def _align_series(self, indexer, ser):
511505

512506
if isinstance(indexer, tuple):
513507

508+
# flatten np.ndarray indexers
509+
ravel = lambda i: i.ravel() if isinstance(i, np.ndarray) else i
510+
indexer = tuple(map(ravel, indexer))
511+
514512
aligners = [not _is_null_slice(idx) for idx in indexer]
515513
sum_aligners = sum(aligners)
516514
single_aligner = sum_aligners == 1
@@ -536,12 +534,11 @@ def _align_series(self, indexer, ser):
536534
# series, so need to broadcast (see GH5206)
537535
if (sum_aligners == self.ndim and
538536
all([com._is_sequence(_) for _ in indexer])):
539-
ser = ser.reindex(obj.axes[0][indexer[0].ravel()],
540-
copy=True).values
537+
ser = ser.reindex(obj.axes[0][indexer[0]], copy=True).values
541538

542539
# single indexer
543540
if len(indexer) > 1:
544-
l = len(indexer[1].ravel())
541+
l = len(indexer[1])
545542
ser = np.tile(ser, l).reshape(l, -1).T
546543

547544
return ser
@@ -557,7 +554,7 @@ def _align_series(self, indexer, ser):
557554
if not is_list_like(new_ix):
558555
new_ix = Index([new_ix])
559556
else:
560-
new_ix = Index(new_ix.ravel())
557+
new_ix = Index(new_ix)
561558
if ser.index.equals(new_ix) or not len(new_ix):
562559
return ser.values.copy()
563560

@@ -1765,4 +1762,3 @@ def _maybe_droplevels(index, key):
17651762
pass
17661763

17671764
return index
1768-

pandas/tests/test_frame.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,7 @@ def test_setitem_frame(self):
14051405
# key is unaligned with values
14061406
f = self.mixed_frame.copy()
14071407
piece = f.ix[:2, ['A']]
1408+
piece.index = f.index[-2:]
14081409
key = (slice(-2, None), ['A', 'B'])
14091410
f.ix[key] = piece
14101411
piece['B'] = np.nan

pandas/tests/test_indexing.py

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,11 +1031,12 @@ def test_loc_setitem_frame(self):
10311031

10321032

10331033
def test_loc_setitem_frame_multiples(self):
1034-
10351034
# multiple setting
10361035
df = DataFrame({ 'A' : ['foo','bar','baz'],
10371036
'B' : Series(range(3),dtype=np.int64) })
1038-
df.loc[0:1] = df.loc[1:2]
1037+
rhs = df.loc[1:2]
1038+
rhs.index = df.index[0:2]
1039+
df.loc[0:1] = rhs
10391040
expected = DataFrame({ 'A' : ['bar','baz','baz'],
10401041
'B' : Series([1,2,2],dtype=np.int64) })
10411042
assert_frame_equal(df, expected)
@@ -1047,8 +1048,9 @@ def test_loc_setitem_frame_multiples(self):
10471048
expected = DataFrame({ 'date' : [Timestamp('20000101'),Timestamp('20000102'),Timestamp('20000101'),
10481049
Timestamp('20000102'),Timestamp('20000103')],
10491050
'val' : Series([0,1,0,1,2],dtype=np.int64) })
1050-
1051-
df.loc[2:4] = df.loc[0:2]
1051+
rhs = df.loc[0:2]
1052+
rhs.index = df.index[2:5]
1053+
df.loc[2:4] = rhs
10521054
assert_frame_equal(df, expected)
10531055

10541056
def test_iloc_getitem_frame(self):
@@ -3987,6 +3989,54 @@ def test_float_index_at_iat(self):
39873989
for i in range(len(s)):
39883990
self.assertEqual(s.iat[i], i + 1)
39893991

3992+
def test_rhs_alignment(self):
3993+
# GH8258, tests that both rows & columns are aligned to what is
3994+
# assigned to. covers both uniform data-type & multi-type cases
3995+
def run_tests(df, rhs, right):
3996+
# label, index, slice
3997+
r, i, s = list('bcd'), [1, 2, 3], slice(1, 4)
3998+
c, j, l = ['joe', 'jolie'], [1, 2], slice(1, 3)
3999+
4000+
left = df.copy()
4001+
left.loc[r, c] = rhs
4002+
assert_frame_equal(left, right)
4003+
4004+
left = df.copy()
4005+
left.iloc[i, j] = rhs
4006+
assert_frame_equal(left, right)
4007+
4008+
left = df.copy()
4009+
left.ix[s, l] = rhs
4010+
assert_frame_equal(left, right)
4011+
4012+
left = df.copy()
4013+
left.ix[i, j] = rhs
4014+
assert_frame_equal(left, right)
4015+
4016+
left = df.copy()
4017+
left.ix[r, c] = rhs
4018+
assert_frame_equal(left, right)
4019+
4020+
xs = np.arange(20).reshape(5, 4)
4021+
cols = ['jim', 'joe', 'jolie', 'joline']
4022+
df = pd.DataFrame(xs, columns=cols, index=list('abcde'))
4023+
4024+
# right hand side; permute the indices and multiplpy by -2
4025+
rhs = - 2 * df.iloc[3:0:-1, 2:0:-1]
4026+
4027+
# expected `right` result; just multiply by -2
4028+
right = df.copy()
4029+
right.iloc[1:4, 1:3] *= -2
4030+
4031+
# run tests with uniform dtypes
4032+
run_tests(df, rhs, right)
4033+
4034+
# make frames multi-type & re-run tests
4035+
for frame in [df, rhs, right]:
4036+
frame['joe'] = frame['joe'].astype('float64')
4037+
frame['jolie'] = frame['jolie'].map('@{0}'.format)
4038+
4039+
run_tests(df, rhs, right)
39904040

39914041
class TestSeriesNoneCoercion(tm.TestCase):
39924042
EXPECTED_RESULTS = [

0 commit comments

Comments
 (0)