@@ -513,9 +513,7 @@ def stack(frame, level=-1, dropna=True):
513513 "names are not unique." .format (level ))
514514 raise ValueError (msg )
515515
516- if isinstance (level , int ) and level < 0 :
517- level += frame .columns .nlevels
518-
516+ # Will also convert negative level numbers and check if out of bounds.
519517 level = frame .columns ._get_level_number (level )
520518
521519 if isinstance (frame .columns , MultiIndex ):
@@ -547,6 +545,45 @@ def stack(frame, level=-1, dropna=True):
547545 return Series (new_values , index = new_index )
548546
549547
548+ def stack_multiple (frame , level , dropna = True ):
549+ # If all passed levels match up to column names, no
550+ # ambiguity about what to do
551+ if all (lev in frame .columns .names for lev in level ):
552+ result = frame
553+ for lev in level :
554+ result = stack (result , lev , dropna = dropna )
555+
556+ # Otherwise, level numbers may change as each successive level is stacked
557+ elif all (isinstance (lev , int ) for lev in level ):
558+ # As each stack is done, the level numbers decrease, so we need
559+ # to account for that when level is a sequence of ints
560+ result = frame
561+ # _get_level_number() checks level numbers are in range and converts
562+ # negative numbers to positive
563+ level = [frame .columns ._get_level_number (lev ) for lev in level ]
564+
565+ # Can't iterate directly through level as we might need to change
566+ # values as we go
567+ for index in range (len (level )):
568+ lev = level [index ]
569+ result = stack (result , lev , dropna = dropna )
570+ # Decrement all level numbers greater than current, as these
571+ # have now shifted down by one
572+ updated_level = []
573+ for other in level :
574+ if other > lev :
575+ updated_level .append (other - 1 )
576+ else :
577+ updated_level .append (other )
578+ level = updated_level
579+
580+ else :
581+ raise ValueError ("level should contain all level names or all level numbers, "
582+ "not a mixture of the two." )
583+
584+ return result
585+
586+
550587def _stack_multi_columns (frame , level = - 1 , dropna = True ):
551588 this = frame .copy ()
552589
0 commit comments