け日記

最近はPythonでいろいろやってます

Python: PandasのDataFrameを横持ち・縦持ちに変換する

PandasのDataFrameを縦持ちから横持ちにする方法とその逆(横持ちから縦持ちにする方法)についての備忘録です。

縦持ちと横持ち

縦持ちは、以下のように、カラム固定で1行に1つの値を持たせている表です。カラムをおいそれと変更できないDBのテーブルなどはこういった形かと思います。

customer_id product_id count
C1 P1 1
C1 P2 2
C2 P2 2
C2 P2 1
C3 P3 3

一方で、横持ちは、カラム数が可変で1行に複数の値をもたせている表です。行列はこういう形になるでしょう。

P1 P2 P3
C1 1 2 0
C2 0 3 0
C3 0 0 3

縦持ちから横持ちへ変換する

それでは縦持ちとなっている以下のデータを横持ちへ変換します。

import pandas as pd
import numpy as np

orders_df = pd.DataFrame({'customer_id': ['C1', 'C1', 'C2', 'C2', 'C3'], 'product_id': ['P1', 'P2', 'P2', 'P2', 'P3'], 'count': [1, 2, 2, 1, 3]})

f:id:ohke:20180721120245p:plain

横持ちにするときは、Pandasのpivot_tableメソッドを使います。 インデックスにcustomer_id、カラムにはproduct_idを指定することで、customer_id×product_idの横持ちテーブルとなります。

pivot_orders_df = orders_df.pivot_table(values=['count'], index=['customer_id'], columns=['product_id'], aggfunc='sum')

f:id:ohke:20180721131300p:plain

いくつかオプション引数があります。

  • aggfuncには、同じcustomer_idとproduct_idの値を集約するためのnumpyの関数を指定します
    • 例えば、customer_id='C2'とprodct_id='P2'の組み合わせが2つありますが、合計値3で埋められてます
    • デフォルトでは"mean"ですが、"sum"、"count"、"max"、"min"などが使えます
  • fill_valueで、欠測値を指定します (デフォルトではNaN)

横持ちから縦持ちへ変換する

上で横持ちにしたテーブルを縦持ちに戻します。

縦持ちにするときは、stackメソッドが使えます。

orders_df = pivot_orders_df.stack()

f:id:ohke:20180721133105p:plain

dropna=Falseとすると、NaNの行となります。

orders_df = pivot_orders_df.stack(dropna=False)

f:id:ohke:20180721133222p:plain

元のテーブルの通り、インデックスではなくカラムにする場合は、reset_indexメソッドでカラム化します。

orders_df = pivot_orders_df.stack().reset_index()

f:id:ohke:20180721133621p:plain