Skip to content

Commit 425924e

Browse files
committed
ENH: DataFrame.pivot now returns MultiIndex columns with multiple fields. Return view when indexing homogeneous hierarhical columns. various related unit tests
address GH #124
1 parent fd97457 commit 425924e

File tree

4 files changed

+36
-19
lines changed

4 files changed

+36
-19
lines changed

pandas/core/frame.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -795,10 +795,10 @@ def _getitem_multilevel(self, key):
795795
loc = self.columns.get_loc(key)
796796
if isinstance(loc, slice):
797797
new_columns = self.columns[loc]
798-
result = self.reindex(columns=new_columns)
799-
800-
# HACK: need a more general way of addressing this problem
801-
result.columns = _maybe_droplevels(new_columns, key)
798+
new_columns = _maybe_droplevels(new_columns, key)
799+
new_values = self.values[:, loc]
800+
result = DataFrame(new_values, index=self.index,
801+
columns=new_columns)
802802
return result
803803
else:
804804
return self._getitem_single(key)
@@ -1631,11 +1631,9 @@ def pivot(self, index=None, columns=None, values=None):
16311631
pivoted : DataFrame (value column specified) or WidePanel (no value
16321632
column specified)
16331633
"""
1634-
from pandas.core.panel import _make_long_index, LongPanel
1635-
16361634
index_vals = self[index]
16371635
column_vals = self[columns]
1638-
long_index = _make_long_index(index_vals, column_vals)
1636+
mindex = MultiIndex.from_arrays([index_vals, column_vals])
16391637

16401638
if values is None:
16411639
items = self.columns - [index, columns]
@@ -1644,14 +1642,15 @@ def pivot(self, index=None, columns=None, values=None):
16441642
items = [values]
16451643
mat = np.atleast_2d(self[values].values).T
16461644

1647-
lp = LongPanel(mat, index=long_index, columns=items)
1648-
lp = lp.sortlevel(level=0)
1645+
stacked = DataFrame(mat, index=mindex, columns=items)
16491646

1650-
wp = lp.to_wide()
1647+
if not mindex.is_lexsorted:
1648+
stacked = stacked.sortlevel(level=0)
1649+
1650+
unstacked = stacked.unstack()
16511651
if values is not None:
1652-
return wp[values]
1653-
else:
1654-
return wp
1652+
unstacked.columns = unstacked.columns.droplevel(0)
1653+
return unstacked
16551654

16561655
def stack(self):
16571656
"""

pandas/core/index.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -652,18 +652,20 @@ def levshape(self):
652652
def __reduce__(self):
653653
"""Necessary for making this object picklable"""
654654
object_state = list(np.ndarray.__reduce__(self))
655-
subclass_state = (self.levels, self.labels)
655+
subclass_state = (self.levels, self.labels, self.sortorder, self.names)
656656
object_state[2] = (object_state[2], subclass_state)
657657
return tuple(object_state)
658658

659659
def __setstate__(self, state):
660660
"""Necessary for making this object picklable"""
661661
nd_state, own_state = state
662662
np.ndarray.__setstate__(self, nd_state)
663-
levels, labels, = own_state
663+
levels, labels, sortorder, names = own_state
664664

665665
self.levels = [Index(x) for x in levels]
666666
self.labels = labels
667+
self.names = names
668+
self.sortorder = sortorder
667669

668670
def __getitem__(self, key):
669671
arr_idx = self.view(np.ndarray)
@@ -959,12 +961,12 @@ def get_loc(self, key):
959961
raise KeyError(key)
960962
return result
961963
else:
962-
# assert(self.sortorder == 0)
963-
# slice level 0
964964
level = self.levels[0]
965965
labels = self.labels[0]
966-
967966
loc = level.get_loc(key)
967+
968+
assert(self.lexsort_depth >= 1)
969+
968970
i = labels.searchsorted(loc, side='left')
969971
j = labels.searchsorted(loc, side='right')
970972
return slice(i, j)

pandas/tests/test_frame.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,7 @@ def test_pivot(self):
17211721
wp = tm.makeWidePanel()
17221722
lp = wp.to_long()
17231723
df = DataFrame.from_records(lp.toRecords())
1724-
tm.assert_panel_equal(df.pivot('major', 'minor'), wp)
1724+
assert_frame_equal(df.pivot('major', 'minor'), lp.unstack())
17251725

17261726
def test_reindex(self):
17271727
newFrame = self.frame.reindex(self.ts1.index)

pandas/tests/test_multilevel.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,22 @@ def test_is_lexsorted(self):
262262
self.assert_(not index.is_lexsorted())
263263
self.assert_(index.lexsort_depth == 0)
264264

265+
def test_frame_getitem_view(self):
266+
df = self.frame.T
267+
df['foo'].values[:] = 0
268+
self.assert_((df['foo'].values == 0).all())
269+
270+
# but not if it's mixed-type
271+
df['foo', 'four'] = 'foo'
272+
df = df.sortlevel(0, axis=1)
273+
df['foo']['one'] = 2
274+
self.assert_((df['foo', 'one'] == 0).all())
275+
276+
def test_frame_getitem_not_sorted(self):
277+
df = self.frame.T
278+
df['foo', 'four'] = 'foo'
279+
self.assertRaises(Exception, df.__getitem__, 'foo')
280+
265281
if __name__ == '__main__':
266282

267283
# unittest.main()

0 commit comments

Comments
 (0)