Mar 24, 2019

Pandasのmelt/pivotとstack/unstackの違い

この間、Pandasを使った教材を流し読みしていると、DataFrameクラスのmeltというメソッドが出てきて、pivotの逆だと説明されていた。それを読んで、meltされたものをpivotして元に戻してみよう思って、すきま時間を集めてのべ1時間くらいがんばったが、できなかった。
ついでに、meltとstackの違いがわからなかった。

今月、1時間くらい連続してがんばったら成功して、meltとstackの違いも理解できたので、ここに控えておく。

以下、コード例はJupyter Notebookで書いたインタラクティブシェル向けの形式のものをそのまま貼り付けており、出力例はJupyter Notebookの出力を加工して作成している。

In [1]:
import pandas as pd
df = pd.DataFrame({'月': ['1月', '2月', '3月'],
                   '京都': [110, 115, 144],
                   '大阪': [263, 283, 309],
                   '奈良': [12, 13, 21],
                  })
df
Out [1]:
京都 大阪 奈良
0 1月 110 263 12
1 2月 115 283 13
2 3月 144 309 21

このようなDataFrameがあるとして、これをmeltして、pivotで元に戻してみる。

In [2]:
df_melted = df.melt(id_vars='月', var_name='地域', value_name='人数')
df_melted
Out [2]:
地域 人数
0 1月 京都 110
1 2月 京都 115
2 3月 京都 144
3 1月 大阪 263
4 2月 大阪 283
5 3月 大阪 309
6 1月 奈良 12
7 2月 奈良 13
8 3月 奈良 21
In [3]:
df_pivoted = df_melted.pivot(index='月', columns='地域', values='人数')
df_pivoted
Out [3]:
地域 京都 大阪 奈良
1月 110 263 12
2月 115 283 13
3月 144 309 21

meltしたものをpivotすると大体戻ったが、「月」がindexになっているのと、meltで加えた「地域」が残っているのが異なるので、修正する。
(ちなみに、reset_indexしただけでは「月」が「地域」に入っておかしなことになる)

In [4]:
df_pivoted = df_pivoted.reset_index()
df_pivoted.columns.name = None
df_pivoted
Out [4]:
京都 大阪 奈良
0 1月 110 263 12
1 2月 115 283 13
2 3月 144 309 21

次に、meltとstackの違いを見てみる。
上のdfをstackすると「月」と地域名が同列に処理されてしまうので、まず「月」をインデックスにしてからstackする。

In [5]:
df2 = df.set_index('月')
df2
Out [5]:
京都 大阪 奈良
1月 110 263 12
2月 115 283 13
3月 144 309 21

この場合はstackすると結果が1列なのでDataFrameでなくSeriesになるので、変数名の先頭をdf_でなくsr_にしている。

In [6]:
sr_stacked = df2.stack()
sr_stacked
Out [6]:
1月 京都 110
大阪 263
奈良 12
2月 京都 115
大阪 283
奈良 13
3月 京都 144
大阪 309
奈良 21

当然ながら、stackしたものをunstackすると元に戻る。

In [7]:
sr_stacked.unstack()
Out [7]:
京都 大阪 奈良
1月 110 263 12
2月 115 283 13
3月 144 309 21

meltでは元々column名だった列(「地域」列)がindexにならなかったが、stackではindexになっている。つまり、meltはcolumn名を新たに加えた通常の列に展開することによって表を変形し、stackはcolumn名をindexの新たな階層に展開することによって表を変形する。このことがmeltとstackの主な違いのようだ。
次のようにして、stackしたもののindexを通常の列に戻すと、meltした結果(Out [2])と同じになる。

In [8]:
df_stacked = sr_stacked.sort_index(level=1).reset_index()
df_stacked.columns = ['月', '地域', '人数']
df_stacked
Out [8]:
地域 人数
0 1月 京都 110
1 2月 京都 115
2 3月 京都 144
3 1月 大阪 263
4 2月 大阪 283
5 3月 大阪 309
6 1月 奈良 12
7 2月 奈良 13
8 3月 奈良 21

See more ...

Posted at 09:38 in PC一般 | WriteBacks (0)
WriteBacks

Mar 09, 2019

ピボットテーブルを理解した

15年くらい前にExcelのピボットテーブルを使ってみて以来ずっと、ピボットテーブルとは何なのかが理解できなかった。2年くらい前にも、ピボットテーブルを初心者向けに詳しく解説したITProの記事を読んで、今度こそ理解しようと意気込んで、何も見ずに演習問題を解けるようにもなったが、結局「ピボットテーブル」の意味を理解できず、ただ演習問題の解き方を丸暗記したような感じで終わってしまった。
筆者にとってピボットテーブルは、わかりそうなのにわからない、何故分からないのかがわからない、まるで眼の盲点のように、筆者の脳はそれが理解できない作りになっているかのように思えるものだった。

最近、久々にPandasを勉強する機会があって、ピボットテーブルが出てきて、また理解できなかった。しかし、何回かgroupbyの練習をした後、ふと、ピボットテーブルってgroupbyと似てるなと思い、Webで調べたらピボットテーブルはGroupByを2次元にしたようなものというような説明が見つかって、遂に理解に成功した。

GroupByの動作は、split-apply-combineと言われるように、データをキーによって分割し、分割した単位で何らかの処理を適用し、結合して1つのデータにするものである。
次の図は、データをkey列の値によって分割し、分割された組毎に合計値を計算して1つの値に「集約(aggregate)」し、1つのデータにまとめる、というGroupByの処理の例を表している。

●コード例
import pandas as pd
df = pd.DataFrame({
    'key': ['A', 'B', 'C'] * 3,
    'val': range(1, 10)})
print(df)
print(df.groupby('key').aggregate(sum))
●実行結果
  key  val
0   A    1
1   B    2
2   C    3
3   A    4
4   B    5
5   C    6
6   A    7
7   B    8
8   C    9
     val
key     
A     12
B     15
C     18

Applyは集約に限らず、単に表を変形したり、グループ毎の値を使って値を変換することなども含まれるが、グループ毎に集約するのがGroupByやピボットテーブルの醍醐味だと思うので、この記事の例では集約にしている。

ピボットテーブルは、split-apply-combineを行方向と列方向に同時に行うものと捉えることができる。
次の図は、データをkey1列の値によって行方向に、key2列の値によって列方向に分割し、同様に分割毎に合計値に集約し、1つのデータに結合する、というピボットテーブルの処理の例を表している。

●コード例(pivot_tableメソッド使用)
import pandas as pd
df = pd.DataFrame({
    'key1': ['A', 'B'] * 6,
    'key2': ['①', '②', '③'] * 4,
    'val': range(1, 13)})
print(df)
print(df.pivot_table(index='key1', columns='key2', values='val', aggfunc='sum'))
●実行結果
   key1 key2  val
0     A    ①    1
1     B    ②    2
2     A    ③    3
3     B    ①    4
4     A    ②    5
5     B    ③    6
6     A    ①    7
7     B    ②    8
8     A    ③    9
9     B    ①   10
10    A    ②   11
11    B    ③   12
key2   ①   ②   ③
key1            
A      8  16  12
B     14  10  18

ピボットテーブルはGroupByを2次元にしたようなもの、ということを確認する為に、pivot_tableの代わりにgroupbyを2つ使って同じ結果にしてみる。

●コード例(groupbyメソッドを2つ使用)
print(df.groupby('key1').apply(lambda x: x.groupby('key2')['val'].apply(sum)))
●実行結果
key2   ①   ②   ③
key1            
A      8  16  12
B     14  10  18

See more ...

Posted at 20:26 in PC一般 | WriteBacks (0)
WriteBacks