Pandasでは列データを行データへと変換するstack関数と逆に行データを列データへと変換するunstack関数が実装されています。 ちょっとした見た目を変更する時もかなり手軽に出来るようになるので使ってみることをオススメします。 MultiIndexとなっているDataFrameに対して分析したい対象が見やすくなるように整形することが容易になります。

本記事ではstack関数とunstack関数の使い方を解説します。

stack関数

まずはstack関数から解説します。stack関数を使うことで列から行への変換をすることができます。

APIドキュメント

pandas.DataFrame.stack(level=-1, dropna=True)

params:

パラメータ名 概要
level int,str
もしくはリスト
(省略可能)初期値-1
どの階層のカラムラベルをインデックスラベルに移動させるかを選択します。デフォルトでは最も内側(level=-1)のカラムラベルを移動させます。複数階層選択することも可能です。
dropna bool値 (省略可能)初期値True
変形した際に生じる欠損値しかない行データを削除するかどうかを指定します。

returns:

変形されたDataFrameまたはSeries

引数は合わせて2つほどあります。 1つは移動させるカラムラベルの階層を指定するlevel引数ともう1つは移動させた際に発生する欠損値を含む行を取り除くかどうかを指定するdropna引数です。

変形した結果1つの列データしか残らない場合、Seriesになります。 使いながら覚えた方が早いと思うので手を動かしていきます。

カラムラベルが単一列のとき

まずは以下のCSVファイルのデータを利用します。

,basic,basic,basic,score,score,score
,class,grade,state,math,English,science
Alice,A,2,NY,85,69,50
Bob,B,1,DC,92,93,65
Catharine,B,1,DC,85,55,60
David,B,2,PA,70,89,79
Ellen,A,3,NY,98,86,67
Frank,A,3,DC,84,61,50
George,B,1,OH,97,95,62
Helen,A,2,OR,87,92,67

このファイルをsample_stack.csvとして保存します。以下のリンクからもダウンロードできます。

smaple_stack.csv

まずはカラムラベルが単一列(マルチインデックスではない場合)の時を試したいので一番上の行は読み飛ばして読み込みます。

In [1]: import pandas as pd

In [5]: df_single = pd.read_csv("sample_stack.csv",skiprows=[0], index_col=0)

In [7]: df_single
Out[7]:
          class  grade state  math  English  science
Alice         A      2    NY    85       69        50
Bob           B      1    DC    92       93        65
Catharine     B      1    DC    85       55        60
David         B      2    PA    70       89        79
Ellen         A      3    NY    98       86        67
Frank         A      3    DC    84       61        50
George        B      1    OH    97       95        62
Helen         A      2    OR    87       92        67

ではこのDataFrameにstack関数を適用させてみます。カラムラベルが単層構造(MultiIndexではない)ので、返り値はSeriesになります。少々データが多めなので長めのデータとなります。

In [8]: df_single.stack()
Out[8]:
Alice      class        A
           grade        2
           state       NY
           math        85
           English     69
           science     50
Bob        class        B
           grade        1
           state       DC
           math        92
           English     93
           science     65
Catharine  class        B
           grade        1
           state       DC
           math        85
           English     55
           science     60
David      class        B
           grade        2
           state       PA
           math        70
           English     89
           science     79
Ellen      class        A
           grade        3
           state       NY
           math        98
           English     86
           science     67
Frank      class        A
           grade        3
           state       DC
           math        84
           English     61
           science     50
George     class        B
           grade        1
           state       OH
           math        97
           English     95
           science     62
Helen      class        A
           grade        2
           state       OR
           math        87
           English     92
           science     67
dtype: object

この時インデックスラベルはMultiIndexオブジェクトとなっており、名前ー情報の種類となっています。

複数列構造(MultiIndex)のとき

次は先ほどのデータのカラムラベルをMultiIndexオブジェクトとして読み込みます。

In [11]: df_multi = pd.read_csv("sample_stack.csv",index_col=0,header=[0,1])

In [12]: df_multi
Out[12]:
          basic             score                 
          class grade state  math English science
Alice         A     2    NY    85      69       50
Bob           B     1    DC    92      93       65
Catharine     B     1    DC    85      55       60
David         B     2    PA    70      89       79
Ellen         A     3    NY    98      86       67
Frank         A     3    DC    84      61       50
George        B     1    OH    97      95       62
Helen         A     2    OR    87      92       67

このまま適用してみます。 すると、下の階層のカラムラベルが移動することになります。

In [13]: df_multi.stack()
Out[13]:
                   basic  score
Alice     English    NaN   69.0
          class        A    NaN
          grade        2    NaN
          math       NaN   85.0
          science    NaN   50.0
          state       NY    NaN
Bob       English    NaN   93.0
          class        B    NaN
          grade        1    NaN
          math       NaN   92.0
          science    NaN   65.0
          state       DC    NaN
Catharine English    NaN   55.0
          class        B    NaN
          grade        1    NaN
          math       NaN   85.0
          science    NaN   60.0
          state       DC    NaN
David     English    NaN   89.0
          class        B    NaN
          grade        2    NaN
          math       NaN   70.0
          science    NaN   79.0
          state       PA    NaN
Ellen     English    NaN   86.0
          class        A    NaN
          grade        3    NaN
          math       NaN   98.0
          science    NaN   67.0
          state       NY    NaN
Frank     English    NaN   61.0
          class        A    NaN
          grade        3    NaN
          math       NaN   84.0
          science    NaN   50.0
          state       DC    NaN
George    English    NaN   95.0
          class        B    NaN
          grade        1    NaN
          math       NaN   97.0
          science    NaN   62.0
          state       OH    NaN
Helen     English    NaN   92.0
          class        A    NaN
          grade        2    NaN
          math       NaN   87.0
          science    NaN   67.0
          state       OR    NaN

stackする階層を指定する

次はlevel引数を使ってstackする階層を指定してみます。 デフォルトでは最も内側(カラムラベルでなら最も下とも表現できます)の層を移動させています。

level=0だと一番外側で、level=1だと1つ内側になります。

In [14]: df_multi.stack(level=0) # 一番外側の階層が移動する
Out[14]:
                 English class  grade  math  science  state
Alice     basic      NaN     A    2.0   NaN       NaN    NY
          score     69.0   NaN    NaN  85.0      50.0   NaN
Bob       basic      NaN     B    1.0   NaN       NaN    DC
          score     93.0   NaN    NaN  92.0      65.0   NaN
Catharine basic      NaN     B    1.0   NaN       NaN    DC
          score     55.0   NaN    NaN  85.0      60.0   NaN
David     basic      NaN     B    2.0   NaN       NaN    PA
          score     89.0   NaN    NaN  70.0      79.0   NaN
Ellen     basic      NaN     A    3.0   NaN       NaN    NY
          score     86.0   NaN    NaN  98.0      67.0   NaN
Frank     basic      NaN     A    3.0   NaN       NaN    DC
          score     61.0   NaN    NaN  84.0      50.0   NaN
George    basic      NaN     B    1.0   NaN       NaN    OH
          score     95.0   NaN    NaN  97.0      62.0   NaN
Helen     basic      NaN     A    2.0   NaN       NaN    OR
          score     92.0   NaN    NaN  87.0      67.0   NaN

In [15]: df_multi.stack(level=1) # 1つ内側の階層が移動する
Out[15]:
                   basic  score
Alice     English    NaN   69.0
          class        A    NaN
          grade        2    NaN
          math       NaN   85.0
          science    NaN   50.0
          state       NY    NaN
Bob       English    NaN   93.0
          class        B    NaN
          grade        1    NaN
          math       NaN   92.0
          science    NaN   65.0
          state       DC    NaN
Catharine English    NaN   55.0
          class        B    NaN
          grade        1    NaN
          math       NaN   85.0
          science    NaN   60.0
          state       DC    NaN
David     English    NaN   89.0
          class        B    NaN
          grade        2    NaN
          math       NaN   70.0
          science    NaN   79.0
          state       PA    NaN
Ellen     English    NaN   86.0
          class        A    NaN
          grade        3    NaN
          math       NaN   98.0
          science    NaN   67.0
          state       NY    NaN
Frank     English    NaN   61.0
          class        A    NaN
          grade        3    NaN
          math       NaN   84.0
          science    NaN   50.0
          state       DC    NaN
George    English    NaN   95.0
          class        B    NaN
          grade        1    NaN
          math       NaN   97.0
          science    NaN   62.0
          state       OH    NaN
Helen     English    NaN   92.0
          class        A    NaN
          grade        2    NaN
          math       NaN   87.0
          science    NaN   67.0
          state       OR    NaN

リストで両方指定することも可能です。

In [21]: df_multi.stack(level=[0,1])
Out[21]:
Alice      basic  class        A
                  grade        2
                  state       NY
           score  math        85
                  science     50
Bob        basic  class        B
                  grade        1
                  state       DC
           score  math        92
                  science     65
Catharine  basic  class        B
                  grade        1
                  state       DC
           score  math        85
                  science     60
David      basic  class        B
                  grade        2
                  state       PA
           score  math        70
                  science     79
Ellen      basic  class        A
                  grade        3
                  state       NY
           score  math        98
                  science     67
Frank      basic  class        A
                  grade        3
                  state       DC
           score  math        84
                  science     50
George     basic  class        B
                  grade        1
                  state       OH
           score  math        97
                  science     62
Helen      basic  class        A
                  grade        2
                  state       OR
           score  math        87
                  science     67
dtype: object

欠損値も表示させる

dropna=Falseにすると移動後に新たに生じる組み合わせ(例えばbasicとEnglish)は基本NaN値があてがわれます。この結果両方とも欠損値となった場合、省略するかどうかを指定します。

一旦英語の成績を全部NaNに置き換えます。

In [16]: import numpy as np

In [17]: df_multi["score","English"] = np.nan

In [18]: df_multi
Out[18]:
          basic             score                 
          class grade state  math English science
Alice         A     2    NY    85     NaN       50
Bob           B     1    DC    92     NaN       65
Catharine     B     1    DC    85     NaN       60
David         B     2    PA    70     NaN       79
Ellen         A     3    NY    98     NaN       67
Frank         A     3    DC    84     NaN       50
George        B     1    OH    97     NaN       62
Helen         A     2    OR    87     NaN       67

次にこのDataFrameに対してstack関数を適用してみます。

In [19]: df_multi.stack() # デフォルトだとEnglishの部分は全部削除される
Out[19]:
                   basic  score
Alice     class        A    NaN
          grade        2    NaN
          math       NaN   85.0
          science    NaN   50.0
          state       NY    NaN
Bob       class        B    NaN
          grade        1    NaN
          math       NaN   92.0
          science    NaN   65.0
          state       DC    NaN
Catharine class        B    NaN
          grade        1    NaN
          math       NaN   85.0
          science    NaN   60.0
          state       DC    NaN
David     class        B    NaN
          grade        2    NaN
          math       NaN   70.0
          science    NaN   79.0
          state       PA    NaN
Ellen     class        A    NaN
          grade        3    NaN
          math       NaN   98.0
          science    NaN   67.0
          state       NY    NaN
Frank     class        A    NaN
          grade        3    NaN
          math       NaN   84.0
          science    NaN   50.0
          state       DC    NaN
George    class        B    NaN
          grade        1    NaN
          math       NaN   97.0
          science    NaN   62.0
          state       OH    NaN
Helen     class        A    NaN
          grade        2    NaN
          math       NaN   87.0
          science    NaN   67.0
          state       OR    NaN

In [20]: df_multi.stack(dropna=False) # FalseにするとEnglishの部分は削除されない
Out[20]:
                   basic  score
Alice     English    NaN    NaN
          class        A    NaN
          grade        2    NaN
          math       NaN   85.0
          science    NaN   50.0
          state       NY    NaN
Bob       English    NaN    NaN
          class        B    NaN
          grade        1    NaN
          math       NaN   92.0
          science    NaN   65.0
          state       DC    NaN
Catharine English    NaN    NaN
          class        B    NaN
          grade        1    NaN
          math       NaN   85.0
          science    NaN   60.0
          state       DC    NaN
David     English    NaN    NaN
          class        B    NaN
          grade        2    NaN
          math       NaN   70.0
          science    NaN   79.0
          state       PA    NaN
Ellen     English    NaN    NaN
          class        A    NaN
          grade        3    NaN
          math       NaN   98.0
          science    NaN   67.0
          state       NY    NaN
Frank     English    NaN    NaN
          class        A    NaN
          grade        3    NaN
          math       NaN   84.0
          science    NaN   50.0
          state       DC    NaN
George    English    NaN    NaN
          class        B    NaN
          grade        1    NaN
          math       NaN   97.0
          science    NaN   62.0
          state       OH    NaN
Helen     English    NaN    NaN
          class        A    NaN
          grade        2    NaN
          math       NaN   87.0
          science    NaN   67.0
          state       OR    NaN

unstack関数

次にstack関数と逆の操作。つまり、インデックスラベルをカラムラベルに移動させるunstack関数の使い方について解説します。

行うこと自体が逆になるだけなので、基本的な考え方はstack関数とほぼ同一です。

APIドキュメント

pandas.DataFrame.stack(level=-1, fill_values=None)

params:

パラメータ名 概要
level int,str
もしくはリスト
(省略可能)初期値-1
どの階層のカラムラベルをインデックスラベルに移動させるかを選択します。デフォルトでは最も内側(level=-1)のカラムラベルを移動させます。複数階層選択することも可能です。
fill_value (省略可能)初期値None
変形した際に欠損値が発生するとき、どんな値で埋め合わせをするかを指定します。

returns:

変形されたDataFrameまたはSeries

引数をみてみると先ほどあったdropnaの代わりにfill_valueが追加されています。 なぜこうしたのかは今ひとつ不明ですがfill_valueがあることでunsatck関数では新たに生成された欠損値に対する補完を行う値を指定ができます。

インデックスラベルが単層のとき

先ほどと同じデータを使います。NaN値もそのまま残しておきます。

In [22]: df_multi
Out[22]:
          basic             score                 
          class grade state  math English science
Alice         A     2    NY    85     NaN       50
Bob           B     1    DC    92     NaN       65
Catharine     B     1    DC    85     NaN       60
David         B     2    PA    70     NaN       79
Ellen         A     3    NY    98     NaN       67
Frank         A     3    DC    84     NaN       50
George        B     1    OH    97     NaN       62
Helen         A     2    OR    87     NaN       67

unstack関数を適用させます。 行→列へとデータが変換されます。

ただ、今回インデックスラベルが単層なので変形後のデータはSeriesとなるので横長ではなく縦長の見た目のデータとなります。

In [23]: df_multi.unstack()
Out[23]:
basic  class     Alice          A
                 Bob            B
                 Catharine      B
                 David          B
                 Ellen          A
                 Frank          A
                 George         B
                 Helen          A
       grade     Alice          2
                 Bob            1
                 Catharine      1
                 David          2
                 Ellen          3
                 Frank          3
                 George         1
                 Helen          2
       state     Alice         NY
                 Bob           DC
                 Catharine     DC
                 David         PA
                 Ellen         NY
                 Frank         DC
                 George        OH
                 Helen         OR
score  math      Alice         85
                 Bob           92
                 Catharine     85
                 David         70
                 Ellen         98
                 Frank         84
                 George        97
                 Helen         87
       English   Alice        NaN
                 Bob          NaN
                 Catharine    NaN
                 David        NaN
                 Ellen        NaN
                 Frank        NaN
                 George       NaN
                 Helen        NaN
       science   Alice         50
                 Bob           65
                 Catharine     60
                 David         79
                 Ellen         67
                 Frank         50
                 George        62
                 Helen         67
dtype: object

多層構造(MultiIndex)のとき

もう一度csvファイルから読み込みます。今度は名前とクラスをインデックスラベルに設定します。

In [39]: df_multi_index = pd.read_csv("sample_stack.csv",index_col=[0,1],header=[0,1])

In [40]: df_multi_index
Out[40]:
            basic       score                 
            grade state  math English science
Alice     A     2    NY    85      69       50
Bob       B     1    DC    92      93       65
Catharine B     1    DC    85      55       60
David     B     2    PA    70      89       79
Ellen     A     3    NY    98      86       67
Frank     A     3    DC    84      61       50
George    B     1    OH    97      95       62
Helen     A     2    OR    87      92       67
In [41]: df_multi_index.unstack() # クラス部分がunstackされる
Out[41]:
          basic                 score                                   
          grade      state       math       English       science       
              A    B     A    B     A     B       A     B        A     B
Alice       2.0  NaN    NY  NaN  85.0   NaN    69.0   NaN     50.0   NaN
Bob         NaN  1.0   NaN   DC   NaN  92.0     NaN  93.0      NaN  65.0
Catharine   NaN  1.0   NaN   DC   NaN  85.0     NaN  55.0      NaN  60.0
David       NaN  2.0   NaN   PA   NaN  70.0     NaN  89.0      NaN  79.0
Ellen       3.0  NaN    NY  NaN  98.0   NaN    86.0   NaN     67.0   NaN
Frank       3.0  NaN    DC  NaN  84.0   NaN    61.0   NaN     50.0   NaN
George      NaN  1.0   NaN   OH   NaN  97.0     NaN  95.0      NaN  62.0
Helen       2.0  NaN    OR  NaN  87.0   NaN    92.0   NaN     67.0   NaN

In [42]: df_multi_index.unstack(level=0) # 名前がunstackされる
Out[42]:
  basic                             ...     score                         
  grade                             ...  science                          
  Alice  Bob Catharine David Ellen  ...     David Ellen Frank George Helen
A   2.0  NaN       NaN   NaN   3.0  ...       NaN  67.0  50.0    NaN  67.0
B   NaN  1.0       1.0   2.0   NaN  ...      79.0   NaN   NaN   62.0   NaN

[2 rows x 40 columns]

fill_valueで欠損値の補完

fill_value引数で欠損値の補完を行うことができます。 今回は"missing"で穴埋めしてみます。

In [43]: df_multi_index.unstack(fill_value="missing")
Out[43]:
             basic                     ...       score                  
             grade             state   ...     English science          
                 A        B        A   ...           B        A        B
Alice            2  missing       NY   ...     missing       50  missing
Bob        missing        1  missing   ...          93  missing       65
Catharine  missing        1  missing   ...          55  missing       60
David      missing        2  missing   ...          89  missing       79
Ellen            3  missing       NY   ...     missing       67  missing
Frank            3  missing       DC   ...     missing       50  missing
George     missing        1  missing   ...          95  missing       62
Helen            2  missing       OR   ...     missing       67  missing

[8 rows x 10 columns]

まとめ

今回はラベルを移動させるstackunstack関数の使い方についてまとめました。

  • stackは列→行
  • unstackは行→列

となります。

MultiIndexと一緒に使いこなせるようになるとデータの見た目を簡単に変えることができます。

参考