【Pandas】ピボットテーブルで値を集約する【Python】
Excelなどでもデータ分析の時によく使うピボットテーブルをPythonのPandasでも使える。
データを色々な軸から分析したい時によく使う。
Excelなどでピボットテーブルを作成したことがあるなら、大体同じような感覚で作成できる。
ピボットテーブルの作成方法
ピボットテーブルを作成するための関数
PythonのPandasでピボットテーブルを作成する処理はpivot()
とpivot_table()
の2種類がある。
どちらもインデックス、列、値を指定できる。
さらにpivot_tableの方は集約方法(合計や平均など)も指定できる。
pivot()とpivot_table()の主な違い
pivot()は集約ができないが、pivot_table()は集約処理をできる。これが大きな違い。
インデックスと列を指定した際に、インデックスに重複が生じる場合、pivot()だとエラーになる。pivot_table()だと重複した列は集約される(デフォルトでは平均が計算される)
基本的にはpivot_table()を使う方が良い気がするが、指定したインデックスと列で重複が発生しないことを前提としたい場合などはpivot()を使い、エラーが発生したことで想定外のDataFrameを扱っていることに気づけるようにするなどの使い方もある。
インデックスに重複のないデータのピボットテーブル
ここで言う「インデックスに重複がない」とはピボットテーブル作成のためにインデックスと列を指定した際に、インデックスに重複がないことを意味する。
当然、同じデータであってもインデックスと列が変わればピボットテーブルの内容は変わる。
データ準備
1import pandas as pd
2
3df = pd.DataFrame({
4 'foo': ['one', 'one', 'one', 'two', 'two', 'two'],
5 'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
6 'baz': [1, 2, 3, 4, 5, 6],
7 'zoo': ['x', 'y', 'z', 'q', 'w', 'e']
8})
9# foo bar baz zoo
10# 0 one A 1 x
11# 1 one B 2 y
12# 2 one C 3 z
13# 3 two A 4 q
14# 4 two B 5 w
15# 5 two C 6 e
ピボットテーブル作成(pivot関数)
DataFrame.pivot()に引数としてindex, columns, valuesを指定する。
1# ピボットテーブルを作成。
2df_pivot = df.pivot(index='foo', columns='bar', values='baz')
3print(df_pivot)
4# bar A B C
5# foo
6# one 1 2 3
7# two 4 5 6
8
上記はインデックスにfoo列を、列にbar列を、値にbaz列を指定した場合の例。
値を2個指定した場合の例
valuesにbazとzooを指定した場合の例。
1# ピボットテーブルを作成。valuesにbazをzooの2個を指定したい場合の例
2df_pivot = df.pivot(index='foo', columns='bar', values=['baz', 'zoo'])
3# 列がマルチインデックスとなり、valuesで指定した値が最初のレベルにくる。
4print(df_pivot)
5# baz zoo
6# bar A B C A B C
7# foo
8# one 1 2 3 x y z
9# two 4 5 6 q w e
ピボットテーブルの列がMultiIndexになる。
インデックスを2個指定した場合の例
indexにfooとbarを指定した場合の例。
1# indexにfooとbarを指定した場合の例
2df_pivot = df.pivot(index=['foo', 'bar'], columns='baz', values='zoo')
3# インデックスがMultiIndexになる。
4print(df_pivot)
5# baz 1 2 3 4 5 6
6# foo bar
7# one A x NaN NaN NaN NaN NaN
8# B NaN y NaN NaN NaN NaN
9# C NaN NaN z NaN NaN NaN
10# two A NaN NaN NaN q NaN NaN
11# B NaN NaN NaN NaN w NaN
12# C NaN NaN NaN NaN NaN e
ピボットテーブルのインデックスがMultiIndexになる。
インデックスに重複のあるデータのピボットテーブル
ここで言う「インデックスに重複がある」とはピボットテーブル作成のためにインデックスと列を指定した際に、インデックスに重複があることを意味する。
集約関数を指定して集約する必要がある。
集約が行われるので数値以外のデータについては特に扱いに気を付ける必要がある。
データ準備
インデックスに重複が出るようにデータを1行追加。
1import pandas as pd
2
3df = pd.DataFrame({
4 'foo': ['one', 'one', 'one', 'two', 'two', 'two', 'two'],
5 'bar': ['A', 'B', 'C', 'A', 'B', 'C', 'A'],
6 'baz': [1, 2, 3, 4, 5, 6, 5],
7 'zoo': ['x', 'y', 'z', 'q', 'w', 'e', 't']
8})
9# foo bar baz zoo
10# 0 one A 1 x
11# 1 one B 2 y
12# 2 one C 3 z
13# 3 two A 4 q
14# 4 two B 5 w
15# 5 two C 6 e
16# 6 two A 5 t
重複がある場合にpivot関数を使うとエラーとなる
1# indexに重複があるのでエラーとなる
2df.pivot(index='foo', columns='bar', values='baz')
3# ValueError: Index contains duplicate entries, cannot reshape
4# indexに指定しているfooはoneとtwoの2種類の値がある。columnsに指定しているbarはA、B、Cの3種類の値がある。
5# このうち、fooがtwoでbarがAとなるデータは2行ある。一意でないのでValueErrorとなる。
6# pivot_tableでaggfuncで集約する必要がある。
DataFrame.pivot()でindex、columnsを指定した際に、indexに重複ができると、ValueErrorとなる。
indexに重複するデータを含んでいるため、整形できない旨のエラーメッセージが出力される。
pivot_table関数でピボットテーブルを作成する
DataFrame.pivot_table()は引数にaggfuncを指定できるため、indexに重複がある場合、aggfuncで集約される。
1# pivot_tableでaggfuncを指定して重複する行を集約してピボットテーブルを作成する。
2# aggfuncにはsumを指定して値を合計する。
3df_pivot = df.pivot_table(index="foo", columns="bar", values="baz", aggfunc="sum")
4print(df_pivot)
5# bar A B C
6# foo
7# one 1 2 3
8# two 9 5 6
上記はaggfuncにsumを指定した場合の例。aggfuncはデフォルトではmeanとなっている。
文字列に対してmeanで集約した場合の動作
valuesにzoo(値が全て文字列)の列を指定し、aggfuncにmeanを指定すると、文字列に対しては平均を計算できないので、空DataFrameが返ってくる。
1# 文字列の列など、平均値を計算できない列をvaluesに指定すると空のDataFrameが返ってくる。
2# なお将来のバージョンではエラーとなる予定。
3df_pivot = df.pivot_table(index="foo", columns="bar", values="zoo", aggfunc="mean")
4print(df_pivot)
5# Empty DataFrame
6# Columns: []
7# Index: [one, two]
8# FutureWarning: pivot_table dropped a column because it failed to aggregate.
9# This behavior is deprecated and will raise in a future version of pandas.
10# Select only the columns that can be aggregated.
なお、このパターンは将来のPandasのバージョンではエラーになるとのワーニングも出る。
文字列に対してsumで集約した場合の動作
文字列のvaluesに対して、aggfuncにsumを指定すると、文字列が結合される。
1# 文字列でも加算はできるため、aggfuncにsumを設定した場合はエラーにならずに文字列が連結される。
2df_pivot = df.pivot_table(index="foo", columns="bar", values="zoo", aggfunc="sum")
3print(df_pivot)
4# bar A B C
5# foo
6# one x y z
7# two qt w e
文字列同士の加算は文字列の結合になるためである。
色々なaggfunc
aggfuncには色々指定できる。
aggfuncを文字列で指定
- ‘sum’:値の合計
- ‘mean’:平均値
- ‘median’:中央値
- ‘min’:最小値
- ‘max’:最大値
- ‘count’:値のカウント
- ‘nunique’:ユニークな値のカウント
- ‘var’:分散
- ‘std’:標準偏差
aggfuncにmedian(中央値)を指定する例
1# median(中央値)の例
2df_pivot = df.pivot_table(index="foo", columns="bar", values="baz", aggfunc="median")
3print(df_pivot)
4# インデックス=two、A列の値について4.5となっているのは、中央値の計算の特性によるもの。
5# データ数が偶数の場合、中央の値が1個に決まらない。そのため中央の2個の線形補間した値が中央値として計算される。
6# bar A B C
7# foo
8# one 1.0 2.0 3.0
9# two 4.5 5.0 6.0
データ数が偶数個の場合、真ん中の2個の値に対して線形補間を行った値が中央値となる。
そのため、two行のA列は値が4.5となっている。
aggfuncにmin(最小値)を指定する例
1# min(最小値)の例
2df_pivot = df.pivot_table(index="foo", columns="bar", values="baz", aggfunc="min")
3print(df_pivot)
4# bar A B C
5# foo
6# one 1 2 3
7# two 4 5 6
aggfuncにmax(最大値)を指定する例
1# max(最大値)の例
2df_pivot = df.pivot_table(index="foo", columns="bar", values="baz", aggfunc="max")
3print(df_pivot)
4# bar A B C
5# foo
6# one 1 2 3
7# two 5 5 6
aggfuncにnumpyの関数を指定する例
numpyの関数も指定することができる。
np.sumやnp.averageなど。
1import numpy as np
2
3# NumPyの関数を使う
4df_pivot = df.pivot_table(index='foo', columns='bar', values='baz', aggfunc=np.sum)
5print(df_pivot)
6# bar A B C
7# foo
8# one 1 2 3
9# two 9 5 6
関数を実行するわけではないのでaggfunc=np.sum
のようにsumの後に()はつけないことに注意。
aggfuncに自分で作成した関数を指定する例
自分で作成した関数を指定することも可能。
1# 自分で定義した関数を使う
2def range_func(x):
3 return max(x) - min(x)
4
5df_pivot = df.pivot_table(index='foo', columns='bar', values='baz', aggfunc=range_func)
6print(df_pivot)
7# bar A B C
8# foo
9# one 0 0 0
10# two 1 0 0
上記の例は、range_funcで与えられたベクトルxに対して最大値と最小値の差を計算して返す。
numpyの関数を渡す時と同様に、aggfunc=range_func
と関数を渡す。
aggfuncは集約するための関数なので、リストのような値を受け取り、値を1個だけ返すような形になっていればいい。
numpyっぽい言い方だと、ベクトルを受け取ってスカラーを返すことを意識して書けばOK。
まとめ
1# ピボットテーブルを作成。
2df_pivot = df.pivot(index='foo', columns='bar', values='baz')
3
4# pivot_tableでaggfuncを指定して重複する行を集約してピボットテーブルを作成する。
5# sumを指定して値を合計する例。
6df_pivot = df.pivot_table(index="foo", columns="bar", values="baz", aggfunc="sum")
7# median(中央値)の例
8df_pivot = df.pivot_table(index="foo", columns="bar", values="baz", aggfunc="median")
9# min(最小値)の例
10df_pivot = df.pivot_table(index="foo", columns="bar", values="baz", aggfunc="min")
11# max(最大値)の例
12df_pivot = df.pivot_table(index="foo", columns="bar", values="baz", aggfunc="max")
13
14# NumPyの関数を使う例
15df_pivot = df.pivot_table(index='foo', columns='bar', values='baz', aggfunc=np.sum)
16
17# 自分で定義した関数を使う例
18def range_func(x):
19 return max(x) - min(x)
20
21df_pivot = df.pivot_table(index='foo', columns='bar', values='baz', aggfunc=range_func)
22print(df_pivot)