Skip to content

Commit e7ee418

Browse files
authored
BUG: Multiple unstack using row index level labels and multi level columns DataFrame (#32990)
* BUG: Fix bug for unstack with a lot of indices (#32624)
1 parent c81d90f commit e7ee418

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ Reshaping
417417
- Bug in :meth:`DataFrame.pivot_table` losing timezone information when creating a :class:`MultiIndex` level from a column with timezone-aware dtype (:issue:`32558`)
418418
- Bug in :meth:`concat` where when passing a non-dict mapping as ``objs`` would raise a ``TypeError`` (:issue:`32863`)
419419
- :meth:`DataFrame.agg` now provides more descriptive ``SpecificationError`` message when attempting to aggregating non-existant column (:issue:`32755`)
420+
- Bug in :meth:`DataFrame.unstack` when MultiIndexed columns and MultiIndexed rows were used (:issue:`32624`, :issue:`24729` and :issue:`28306`)
420421

421422

422423
Sparse

pandas/core/reshape/reshape.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ def _unstack_multiple(data, clocs, fill_value=None):
338338
comp_ids, obs_ids = compress_group_index(group_index, sort=False)
339339
recons_codes = decons_obs_group_ids(comp_ids, obs_ids, shape, ccodes, xnull=False)
340340

341-
if rlocs == []:
341+
if not rlocs:
342342
# Everything is in clocs, so the dummy df has a regular index
343343
dummy_index = Index(obs_ids, name="__placeholder__")
344344
else:
@@ -363,7 +363,7 @@ def _unstack_multiple(data, clocs, fill_value=None):
363363
for i in range(len(clocs)):
364364
val = clocs[i]
365365
result = result.unstack(val, fill_value=fill_value)
366-
clocs = [v if i > v else v - 1 for v in clocs]
366+
clocs = [v if v < val else v - 1 for v in clocs]
367367

368368
return result
369369

pandas/tests/frame/test_reshape.py

+54
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,60 @@ def test_unstack_unused_level(self, cols):
765765
expected.index = expected.index.droplevel("C")
766766
tm.assert_frame_equal(result, expected)
767767

768+
def test_unstack_long_index(self):
769+
# PH 32624: Error when using a lot of indices to unstack.
770+
# The error occurred only, if a lot of indices are used.
771+
df = pd.DataFrame(
772+
[[1]],
773+
columns=pd.MultiIndex.from_tuples([[0]], names=["c1"]),
774+
index=pd.MultiIndex.from_tuples(
775+
[[0, 0, 1, 0, 0, 0, 1]],
776+
names=["i1", "i2", "i3", "i4", "i5", "i6", "i7"],
777+
),
778+
)
779+
result = df.unstack(["i2", "i3", "i4", "i5", "i6", "i7"])
780+
expected = pd.DataFrame(
781+
[[1]],
782+
columns=pd.MultiIndex.from_tuples(
783+
[[0, 0, 1, 0, 0, 0, 1]],
784+
names=["c1", "i2", "i3", "i4", "i5", "i6", "i7"],
785+
),
786+
index=pd.Index([0], name="i1"),
787+
)
788+
tm.assert_frame_equal(result, expected)
789+
790+
def test_unstack_multi_level_cols(self):
791+
# PH 24729: Unstack a df with multi level columns
792+
df = pd.DataFrame(
793+
[[0.0, 0.0], [0.0, 0.0]],
794+
columns=pd.MultiIndex.from_tuples(
795+
[["B", "C"], ["B", "D"]], names=["c1", "c2"]
796+
),
797+
index=pd.MultiIndex.from_tuples(
798+
[[10, 20, 30], [10, 20, 40]], names=["i1", "i2", "i3"],
799+
),
800+
)
801+
assert df.unstack(["i2", "i1"]).columns.names[-2:] == ["i2", "i1"]
802+
803+
def test_unstack_multi_level_rows_and_cols(self):
804+
# PH 28306: Unstack df with multi level cols and rows
805+
df = pd.DataFrame(
806+
[[1, 2], [3, 4], [-1, -2], [-3, -4]],
807+
columns=pd.MultiIndex.from_tuples([["a", "b", "c"], ["d", "e", "f"]]),
808+
index=pd.MultiIndex.from_tuples(
809+
[
810+
["m1", "P3", 222],
811+
["m1", "A5", 111],
812+
["m2", "P3", 222],
813+
["m2", "A5", 111],
814+
],
815+
names=["i1", "i2", "i3"],
816+
),
817+
)
818+
result = df.unstack(["i3", "i2"])
819+
expected = df.unstack(["i3"]).unstack(["i2"])
820+
tm.assert_frame_equal(result, expected)
821+
768822
def test_unstack_nan_index(self): # GH7466
769823
def cast(val):
770824
val_str = "" if val != val else val

0 commit comments

Comments
 (0)