Skip to content

BUG/ER: various panel indexing fixes #4507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 7, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/source/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ pandas 0.13
(:issue:`4486`)
- Fixed an issue where cumsum and cumprod didn't work with bool dtypes
(:issue:`4170`, :issue:`4440`)
- Fixed Panel slicing issued in ``xs`` that was returning an incorrect dimmed object
(:issue:`4016`)
- Fixed Panel assignment with a transposed frame (:issue:`3830`)
- Raise on set indexing with a Panel and a Panel as a value which needs alignment (:issue:`3777`)

pandas 0.12
===========
Expand Down
37 changes: 32 additions & 5 deletions pandas/core/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def _convert_tuple(self, key):
return tuple(keyidx)

def _setitem_with_indexer(self, indexer, value):
from pandas.core.frame import DataFrame, Series
from pandas import Panel, DataFrame, Series

# also has the side effect of consolidating in-place

Expand Down Expand Up @@ -181,6 +181,9 @@ def setter(item, v):
if isinstance(value, DataFrame):
value = self._align_frame(indexer, value)

if isinstance(value, Panel):
value = self._align_panel(indexer, value)

# 2096
values = self.obj.values
if np.prod(values.shape):
Expand Down Expand Up @@ -208,12 +211,11 @@ def _align_series(self, indexer, ser):
raise ValueError('Incompatible indexer with Series')

def _align_frame(self, indexer, df):
from pandas import DataFrame
is_frame = isinstance(self.obj, DataFrame)
if not is_frame:
df = df.T
is_frame = self.obj.ndim == 2
is_panel = self.obj.ndim >= 3
if isinstance(indexer, tuple):
idx, cols = None, None
sindexers = []
for i, ix in enumerate(indexer):
ax = self.obj.axes[i]
if com._is_sequence(ix) or isinstance(ix, slice):
Expand All @@ -223,6 +225,16 @@ def _align_frame(self, indexer, df):
cols = ax[ix].ravel()
else:
break
else:
sindexers.append(i)

# panel
if is_panel:
if len(sindexers) == 1 and idx is None and cols is None:
if sindexers[0] == 0:
df = df.T
return self.obj.conform(df,axis=sindexers[0])
df = df.T

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

# by definition we are indexing on the 0th axis
if is_panel:
df = df.T

if idx.equals(df.index) and cols.equals(df.columns):
return df.copy().values

# a passed in dataframe which is actually a transpose
# of what is needed
elif idx.equals(df.columns) and cols.equals(df.index):
return df.T.copy().values

return df.reindex(idx, columns=cols).values

raise ValueError('Incompatible indexer with DataFrame')

def _align_panel(self, indexer, df):
is_frame = self.obj.ndim == 2
is_panel = self.obj.ndim >= 3
raise NotImplementedError("cannot set using an indexer with a Panel yet!")

def _getitem_tuple(self, tup):
try:
return self._getitem_lowerdim(tup)
Expand Down
31 changes: 20 additions & 11 deletions pandas/core/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ def xs(self, key, axis=1, copy=True):
self._consolidate_inplace()
axis_number = self._get_axis_number(axis)
new_data = self._data.xs(key, axis=axis_number, copy=copy)
return self._constructor_sliced(new_data)
return self._construct_return_type(new_data)

_xs = xs

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

return self._constructor_sliced(result,
return self._construct_return_type(result, axes)

def _construct_return_type(self, result, axes=None, **kwargs):
""" return the type for the ndim of the result """
ndim = result.ndim
if self.ndim == ndim:
""" return the construction dictionary for these axes """
if axes is None:
return self._constructor(result)
return self._constructor(result, **self._construct_axes_dict())

elif self.ndim == ndim + 1:
if axes is None:
return self._constructor_sliced(result)
return self._constructor_sliced(result,
**self._extract_axes_for_slice(self, axes))

raise PandasError("invalid _construct_return_type [self->%s] [result->%s]" %
(self.ndim, result.ndim))

def _wrap_result(self, result, axis):
axis = self._get_axis_name(axis)
axes = self._get_plane_axes(axis)
if result.ndim == 2 and axis != self._info_axis:
result = result.T

# do we have reduced dimensionalility?
if self.ndim == result.ndim:
return self._constructor(result, **self._construct_axes_dict())
elif self.ndim == result.ndim + 1:
return self._constructor_sliced(result,
**self._extract_axes_for_slice(self, axes))

raise PandasError("invalid _wrap_result [self->%s] [result->%s]" %
(self.ndim, result.ndim))
return self._construct_return_type(result, axes)

def count(self, axis='major'):
"""
Expand Down
25 changes: 25 additions & 0 deletions pandas/tests/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,31 @@ def test_iloc_panel_issue(self):
self.assert_(p.iloc[1, :3, 1].shape == (3,))
self.assert_(p.iloc[:3, 1, 1].shape == (3,))

def test_panel_getitem(self):
# GH4016, date selection returns a frame when a partial string selection
ind = date_range(start="2000", freq="D", periods=1000)
df = DataFrame(np.random.randn(len(ind), 5), index=ind, columns=list('ABCDE'))
panel = Panel(dict([ ('frame_'+c,df) for c in list('ABC') ]))

test2 = panel.ix[:, "2002":"2002-12-31"]
test1 = panel.ix[:, "2002"]
tm.assert_panel_equal(test1,test2)

def test_panel_assignment(self):

# GH3777
wp = Panel(randn(2, 5, 4), items=['Item1', 'Item2'], major_axis=date_range('1/1/2000', periods=5), minor_axis=['A', 'B', 'C', 'D'])
wp2 = Panel(randn(2, 5, 4), items=['Item1', 'Item2'], major_axis=date_range('1/1/2000', periods=5), minor_axis=['A', 'B', 'C', 'D'])
expected = wp.loc[['Item1', 'Item2'], :, ['A', 'B']]

def f():
wp.loc[['Item1', 'Item2'], :, ['A', 'B']] = wp2.loc[['Item1', 'Item2'], :, ['A', 'B']]
self.assertRaises(NotImplementedError, f)

#wp.loc[['Item1', 'Item2'], :, ['A', 'B']] = wp2.loc[['Item1', 'Item2'], :, ['A', 'B']]
#result = wp.loc[['Item1', 'Item2'], :, ['A', 'B']]
#tm.assert_panel_equal(result,expected)

def test_multi_assign(self):

# GH 3626, an assignement of a sub-df to a df
Expand Down
25 changes: 25 additions & 0 deletions pandas/tests/test_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,31 @@ def test_ix_frame_align(self):
out = p.ix[0, [0, 1, 3, 5], -2:]
assert_frame_equal(out, df.T.reindex([0, 1, 3, 5], p.minor_axis[-2:]))

# GH3830, panel assignent by values/frame
for dtype in ['float64','int64']:

panel = Panel(np.arange(40).reshape((2,4,5)), items=['a1','a2'], dtype=dtype)
df1 = panel.iloc[0]
df2 = panel.iloc[1]

tm.assert_frame_equal(panel.loc['a1'], df1)
tm.assert_frame_equal(panel.loc['a2'], df2)

# Assignment by Value Passes for 'a2'
panel.loc['a2'] = df1.values
tm.assert_frame_equal(panel.loc['a1'], df1)
tm.assert_frame_equal(panel.loc['a2'], df1)

# Assignment by DataFrame Ok w/o loc 'a2'
panel['a2'] = df2
tm.assert_frame_equal(panel.loc['a1'], df1)
tm.assert_frame_equal(panel.loc['a2'], df2)

# Assignment by DataFrame Fails for 'a2'
panel.loc['a2'] = df2
tm.assert_frame_equal(panel.loc['a1'], df1)
tm.assert_frame_equal(panel.loc['a2'], df2)

def _check_view(self, indexer, comp):
cp = self.panel.copy()
obj = cp.ix[indexer]
Expand Down