Skip to content

Commit 2e09fcc

Browse files
committed
Merge pull request #4507 from jreback/panel_index
BUG/ER: various panel indexing fixes
2 parents 3841ae6 + f1aee70 commit 2e09fcc

File tree

5 files changed

+106
-16
lines changed

5 files changed

+106
-16
lines changed

doc/source/release.rst

+4
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ pandas 0.13
129129
(:issue:`4486`)
130130
- Fixed an issue where cumsum and cumprod didn't work with bool dtypes
131131
(:issue:`4170`, :issue:`4440`)
132+
- Fixed Panel slicing issued in ``xs`` that was returning an incorrect dimmed object
133+
(:issue:`4016`)
134+
- Fixed Panel assignment with a transposed frame (:issue:`3830`)
135+
- Raise on set indexing with a Panel and a Panel as a value which needs alignment (:issue:`3777`)
132136

133137
pandas 0.12
134138
===========

pandas/core/indexing.py

+32-5
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def _convert_tuple(self, key):
100100
return tuple(keyidx)
101101

102102
def _setitem_with_indexer(self, indexer, value):
103-
from pandas.core.frame import DataFrame, Series
103+
from pandas import Panel, DataFrame, Series
104104

105105
# also has the side effect of consolidating in-place
106106

@@ -181,6 +181,9 @@ def setter(item, v):
181181
if isinstance(value, DataFrame):
182182
value = self._align_frame(indexer, value)
183183

184+
if isinstance(value, Panel):
185+
value = self._align_panel(indexer, value)
186+
184187
# 2096
185188
values = self.obj.values
186189
if np.prod(values.shape):
@@ -208,12 +211,11 @@ def _align_series(self, indexer, ser):
208211
raise ValueError('Incompatible indexer with Series')
209212

210213
def _align_frame(self, indexer, df):
211-
from pandas import DataFrame
212-
is_frame = isinstance(self.obj, DataFrame)
213-
if not is_frame:
214-
df = df.T
214+
is_frame = self.obj.ndim == 2
215+
is_panel = self.obj.ndim >= 3
215216
if isinstance(indexer, tuple):
216217
idx, cols = None, None
218+
sindexers = []
217219
for i, ix in enumerate(indexer):
218220
ax = self.obj.axes[i]
219221
if com._is_sequence(ix) or isinstance(ix, slice):
@@ -223,6 +225,16 @@ def _align_frame(self, indexer, df):
223225
cols = ax[ix].ravel()
224226
else:
225227
break
228+
else:
229+
sindexers.append(i)
230+
231+
# panel
232+
if is_panel:
233+
if len(sindexers) == 1 and idx is None and cols is None:
234+
if sindexers[0] == 0:
235+
df = df.T
236+
return self.obj.conform(df,axis=sindexers[0])
237+
df = df.T
226238

227239
if idx is not None and cols is not None:
228240
if df.index.equals(idx) and df.columns.equals(cols):
@@ -244,12 +256,27 @@ def _align_frame(self, indexer, df):
244256
idx = self.obj.axes[1]
245257
cols = self.obj.axes[2]
246258

259+
# by definition we are indexing on the 0th axis
260+
if is_panel:
261+
df = df.T
262+
247263
if idx.equals(df.index) and cols.equals(df.columns):
248264
return df.copy().values
265+
266+
# a passed in dataframe which is actually a transpose
267+
# of what is needed
268+
elif idx.equals(df.columns) and cols.equals(df.index):
269+
return df.T.copy().values
270+
249271
return df.reindex(idx, columns=cols).values
250272

251273
raise ValueError('Incompatible indexer with DataFrame')
252274

275+
def _align_panel(self, indexer, df):
276+
is_frame = self.obj.ndim == 2
277+
is_panel = self.obj.ndim >= 3
278+
raise NotImplementedError("cannot set using an indexer with a Panel yet!")
279+
253280
def _getitem_tuple(self, tup):
254281
try:
255282
return self._getitem_lowerdim(tup)

pandas/core/panel.py

+20-11
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,7 @@ def xs(self, key, axis=1, copy=True):
10481048
self._consolidate_inplace()
10491049
axis_number = self._get_axis_number(axis)
10501050
new_data = self._data.xs(key, axis=axis_number, copy=copy)
1051-
return self._constructor_sliced(new_data)
1051+
return self._construct_return_type(new_data)
10521052

10531053
_xs = xs
10541054

@@ -1263,24 +1263,33 @@ def _reduce(self, op, axis=0, skipna=True):
12631263
if result.ndim == 2 and axis_name != self._info_axis:
12641264
result = result.T
12651265

1266-
return self._constructor_sliced(result,
1266+
return self._construct_return_type(result, axes)
1267+
1268+
def _construct_return_type(self, result, axes=None, **kwargs):
1269+
""" return the type for the ndim of the result """
1270+
ndim = result.ndim
1271+
if self.ndim == ndim:
1272+
""" return the construction dictionary for these axes """
1273+
if axes is None:
1274+
return self._constructor(result)
1275+
return self._constructor(result, **self._construct_axes_dict())
1276+
1277+
elif self.ndim == ndim + 1:
1278+
if axes is None:
1279+
return self._constructor_sliced(result)
1280+
return self._constructor_sliced(result,
12671281
**self._extract_axes_for_slice(self, axes))
12681282

1283+
raise PandasError("invalid _construct_return_type [self->%s] [result->%s]" %
1284+
(self.ndim, result.ndim))
1285+
12691286
def _wrap_result(self, result, axis):
12701287
axis = self._get_axis_name(axis)
12711288
axes = self._get_plane_axes(axis)
12721289
if result.ndim == 2 and axis != self._info_axis:
12731290
result = result.T
12741291

1275-
# do we have reduced dimensionalility?
1276-
if self.ndim == result.ndim:
1277-
return self._constructor(result, **self._construct_axes_dict())
1278-
elif self.ndim == result.ndim + 1:
1279-
return self._constructor_sliced(result,
1280-
**self._extract_axes_for_slice(self, axes))
1281-
1282-
raise PandasError("invalid _wrap_result [self->%s] [result->%s]" %
1283-
(self.ndim, result.ndim))
1292+
return self._construct_return_type(result, axes)
12841293

12851294
def count(self, axis='major'):
12861295
"""

pandas/tests/test_indexing.py

+25
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,31 @@ def test_iloc_panel_issue(self):
873873
self.assert_(p.iloc[1, :3, 1].shape == (3,))
874874
self.assert_(p.iloc[:3, 1, 1].shape == (3,))
875875

876+
def test_panel_getitem(self):
877+
# GH4016, date selection returns a frame when a partial string selection
878+
ind = date_range(start="2000", freq="D", periods=1000)
879+
df = DataFrame(np.random.randn(len(ind), 5), index=ind, columns=list('ABCDE'))
880+
panel = Panel(dict([ ('frame_'+c,df) for c in list('ABC') ]))
881+
882+
test2 = panel.ix[:, "2002":"2002-12-31"]
883+
test1 = panel.ix[:, "2002"]
884+
tm.assert_panel_equal(test1,test2)
885+
886+
def test_panel_assignment(self):
887+
888+
# GH3777
889+
wp = Panel(randn(2, 5, 4), items=['Item1', 'Item2'], major_axis=date_range('1/1/2000', periods=5), minor_axis=['A', 'B', 'C', 'D'])
890+
wp2 = Panel(randn(2, 5, 4), items=['Item1', 'Item2'], major_axis=date_range('1/1/2000', periods=5), minor_axis=['A', 'B', 'C', 'D'])
891+
expected = wp.loc[['Item1', 'Item2'], :, ['A', 'B']]
892+
893+
def f():
894+
wp.loc[['Item1', 'Item2'], :, ['A', 'B']] = wp2.loc[['Item1', 'Item2'], :, ['A', 'B']]
895+
self.assertRaises(NotImplementedError, f)
896+
897+
#wp.loc[['Item1', 'Item2'], :, ['A', 'B']] = wp2.loc[['Item1', 'Item2'], :, ['A', 'B']]
898+
#result = wp.loc[['Item1', 'Item2'], :, ['A', 'B']]
899+
#tm.assert_panel_equal(result,expected)
900+
876901
def test_multi_assign(self):
877902

878903
# GH 3626, an assignement of a sub-df to a df

pandas/tests/test_panel.py

+25
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,31 @@ def test_ix_frame_align(self):
655655
out = p.ix[0, [0, 1, 3, 5], -2:]
656656
assert_frame_equal(out, df.T.reindex([0, 1, 3, 5], p.minor_axis[-2:]))
657657

658+
# GH3830, panel assignent by values/frame
659+
for dtype in ['float64','int64']:
660+
661+
panel = Panel(np.arange(40).reshape((2,4,5)), items=['a1','a2'], dtype=dtype)
662+
df1 = panel.iloc[0]
663+
df2 = panel.iloc[1]
664+
665+
tm.assert_frame_equal(panel.loc['a1'], df1)
666+
tm.assert_frame_equal(panel.loc['a2'], df2)
667+
668+
# Assignment by Value Passes for 'a2'
669+
panel.loc['a2'] = df1.values
670+
tm.assert_frame_equal(panel.loc['a1'], df1)
671+
tm.assert_frame_equal(panel.loc['a2'], df1)
672+
673+
# Assignment by DataFrame Ok w/o loc 'a2'
674+
panel['a2'] = df2
675+
tm.assert_frame_equal(panel.loc['a1'], df1)
676+
tm.assert_frame_equal(panel.loc['a2'], df2)
677+
678+
# Assignment by DataFrame Fails for 'a2'
679+
panel.loc['a2'] = df2
680+
tm.assert_frame_equal(panel.loc['a1'], df1)
681+
tm.assert_frame_equal(panel.loc['a2'], df2)
682+
658683
def _check_view(self, indexer, comp):
659684
cp = self.panel.copy()
660685
obj = cp.ix[indexer]

0 commit comments

Comments
 (0)