概要
カテゴリカル変数と連続変数の関係の分析に特に有効で、Excelでもよく使うピボットテーブルの機能ですが、Pythonのpandasでもpivot_tableというメソッドを使うことが出来ます。本記事ではこのpivot_tableの全引数の効果を検証しました。なお、pandasのバージョンは0.23.4を使っています。
引数一覧
pandasのpivot_tableには以下の7引数があります。以降、それぞれについて説明していきます。
- values
- index
- columns
- aggfunc
- fill_value
- dropna
- margins_name
また、データとしてはirisを使用しました。
df = pd.read_csv("iris.csv", index_col=0) df.head()
データ分析の定番データセットですね。(全150件の最初の5件を表示しています)
SepalLength SepalWidth PetalLength PetalWidth Name 0 5.1 3.5 1.4 0.2 Iris-setosa 1 4.9 3.0 1.4 0.2 Iris-setosa 2 4.7 3.2 1.3 0.2 Iris-setosa 3 4.6 3.1 1.5 0.2 Iris-setosa 4 5.0 3.6 1.4 0.2 Iris-setosa
以降で引数を検討していきますが、何も指定しないとエラー(ValueError: No group keys passed!)が出るため、indexにNameを指定しておくことにします。
values
集計するカラムを指定するパラメータ。カラム名を指定するとその列のデータが集計されます。aggfuncの項で説明しますが、デフォルトではmeanが計算されます。
1変数を対象に計算する場合
1列を対象とする場合は、カラム名を直接渡すかリスト形式で渡す。
SepalLengthを対象に集計する場合。
df.pivot_table(index="Name", values='SepalLength')
Nameに含まれるカテゴリごとにSepalLengthの平均値が計算された。
SepalLength Name Iris-setosa 5.006 Iris-versicolor 5.936 Iris-virginica 6.588
リストで指定することも可能。
df.pivot_table(index="Name", values=['SepalLength'])
同様の計算結果が得られた。
SepalLength Name Iris-setosa 5.006 Iris-versicolor 5.936 Iris-virginica 6.588
2変数以上を対象に計算する場合
2列以上を対象とする場合は、対象のカラム名のリスト形式で渡す。
SepalLengthとSepalWidthを対象に集計する場合。
df.pivot_table(index="Name", values=['SepalLength', 'SepalWidth'])
Nameに含まれるカテゴリごとにSepalLengthとSepalWidthの平均値が計算された。
SepalLength SepalWidth Name Iris-setosa 5.006 3.418 Iris-versicolor 5.936 2.770 Iris-virginica 6.588 2.974
与えるカラム名の順番を変えても出力の並びが変わらないことには注意。
df.pivot_table(index="Name", values=['SepalWidth', 'SepalLength'])
SepalWidthを先にしても、出力はSepalLengthが先のまま。
SepalLength SepalWidth Name Iris-setosa 5.006 3.418 Iris-versicolor 5.936 2.770 Iris-virginica 6.588 2.974
全変数を対象に計算する場合
カラム名を指定するのが面倒な場合など、全変数を対象にしたい場合はNone(デフォルト値)を与えればよい。
df.pivot_table(index="Name", values=None)
Nameに含まれるカテゴリごとに、集計可能な全変数の平均値が計算された。
PetalLength PetalWidth SepalLength SepalWidth Name Iris-setosa 1.464 0.244 5.006 3.418 Iris-versicolor 4.260 1.326 5.936 2.770 Iris-virginica 5.552 2.026 6.588 2.974
index
グルーピングする変数(行方向)を指定する引数。指定した変数の中のユニーク値で集約されるため、カテゴリ変数を指定するのが基本といえます。デフォルトはNone(なし)ですが、最低でもindexかcolumnsのどちらか一方を指定しないと前述したエラーが出ます。valueと同様にカラム名やリストが指定できるほか、時系列を対象とした場合にはGrouperで月集約等を指定することができます。
1変数を対象にグルーピングする場合
上記までで実施していますので省略します。
2変数以上を対象にグルーピングする場合
リストで渡すことで、2変数以上の組み合わせでグルーピングすることができます。
SepalLength_cutというカテゴリ変数を作っておき、本変数とNameで集約してみます。
#SepalLength_cutというカテゴリ変数を作っておく。 df['SepalLength_cut'] = pd.cut(df['SepalLength'], 5) df.pivot_table(index=["Name", 'SepalLength_cut'], values="PetalLength")
NameとSepalLength_cutの組み合わせでグルーピングされた。
PetalLength Name SepalLength_cut Iris-setosa (4.296, 5.02] 1.428571 (5.02, 5.74] 1.523810 (5.74, 6.46] 1.200000 Iris-versicolor (4.296, 5.02] 3.366667 (5.02, 5.74] 4.005556 (5.74, 6.46] 4.435000 (6.46, 7.18] 4.677778 Iris-virginica (4.296, 5.02] 4.500000 (5.02, 5.74] 4.950000 (5.74, 6.46] 5.257143 (6.46, 7.18] 5.540000 (7.18, 7.9] 6.336364
NameとSepalLength_cutの順番を入れ替えて実行。
df.pivot_table(index=["Name", 'SepalLength_cut'], values="PetalLength")
SepalLength_cut、Nameの順番でグルーピングされた。(valuesと異なり順番が反映される。)
PetalLength SepalLength_cut Name (4.296, 5.02] Iris-setosa 1.428571 Iris-versicolor 3.366667 Iris-virginica 4.500000 (5.02, 5.74] Iris-setosa 1.523810 Iris-versicolor 4.005556 Iris-virginica 4.950000 (5.74, 6.46] Iris-setosa 1.200000 Iris-versicolor 4.435000 Iris-virginica 5.257143 (6.46, 7.18] Iris-versicolor 4.677778 Iris-virginica 5.540000 (7.18, 7.9] Iris-virginica 6.336364
Grouperを指定する場合
本データは時系列ではないので、仮の時系列データを追加して試してみました。
Reshaping and Pivot Tablesを参考にしています。
#Timeという時系列データを作っておく。 df['Time'] = pd.date_range(start='2018/1/1', freq='1d', periods=df.shape[0]) #TImeに対して、1か月ごとに集約 df.pivot_table(values='SepalLength', index=pd.Grouper(freq='M', key='Time'))
仮で作成したデータの月ごとに集約されました。resampleを使う手間を省くことができるかもしれませんね。
SepalLength Time 2018-01-31 5.019355 2018-02-28 5.375000 2018-03-31 5.954839 2018-04-30 6.253333 2018-05-31 6.606667
columns
グルーピングする変数(列方向)を指定する引数。indexと同様に、指定した変数の中のユニーク値で集約されるため、カテゴリ変数を指定するのが基本といえます。デフォルトはNone(なし)ですが、最低でもindexかcolumnsのどちらか一方を指定しないと前述したエラーが出ます。
1変数を対象にグルーピングする場合
indexと同様ですが、indexとcolumnsを両方指定した場合を実施しておきます。
df.pivot_table(values='SepalLength', index='SepalLength_cut', columns='Name')
SepalLengthで行方向を、Nameで列方向にグルーピングしました。まさにピボットテーブルという使い方ですね。
Name Iris-setosa Iris-versicolor Iris-virginica SepalLength_cut (4.296, 5.02] 4.764286 4.966667 4.900000 (5.02, 5.74] 5.290476 5.538889 5.650000 (5.74, 6.46] 5.800000 6.085000 6.176190 (6.46, 7.18] NaN 6.722222 6.726667 (7.18, 7.9] NaN NaN 7.509091
2変数以上を対象にグルーピングする場合
indexと同様のため省略。
Grouperを指定する場合
indexと同様のため省略。
aggfunc
集計する関数を指定するパラメータ。デフォルトはmean(平均値)。指定の方法により、単独の関数をしていたり、複数の関数を指定したり、変数ごとに関数を指定したり、といったことができます。関数はデータ以外の引数を取るものはそのまま使うことはできませんが、自作の関数でラッピングすることで使うことが可能です。
1つの関数を指定する場合
関数を直接、又はリストで与えることで動作します。
例えば標準偏差を計算する場合、関数にnp.std又は[np.std]を指定します。
df.pivot_table(index='Name', aggfunc=np.std)
各説明変数の標準偏差が計算できました。
PetalLength PetalWidth SepalLength SepalWidth Name Iris-setosa 0.173511 0.107210 0.352490 0.381024 Iris-versicolor 0.469911 0.197753 0.516171 0.313798 Iris-virginica 0.551895 0.274650 0.635880 0.322497
2つ以上の関数を指定する場合
関数をリストで与えることで動作します。
例えば、中央値とデータ個数で指定する場合、関数に[np.median, len]を指定します。
df.pivot_table(index='Name', aggfunc=[np.median, len])
各説明変数の中央値とデータ個数が計算できました。
median ... len PetalLength PetalWidth ... SepalLength SepalWidth Name ... Iris-setosa 1.50 0.2 ... 50.0 50.0 Iris-versicolor 4.35 1.3 ... 50.0 50.0 Iris-virginica 5.55 2.0 ... 50.0 50.0
変数ごとに適用する関数を変える場合
変数名と関数を辞書の形で渡すことで動作します。
例えば、PetalLengthは平均値、PetalWidthは中央値と標準偏差という場合は、以下のように指定します。
df.pivot_table(index='Name', aggfunc={"PetalLength":np.mean, "PetalWidth":[np.median, np.std]})
それぞれの変数ごとに関数が適用されました。データの加工方法が決まっている時には使える方法かもしれないですね。
PetalLength PetalWidth mean median std Name Iris-setosa 1.464 0.2 0.107210 Iris-versicolor 4.260 1.3 0.197753 Iris-virginica 5.552 2.0 0.274650
引数を取る関数を指定する場合
np.quantileのような、データ以外に引数を取る関数を使う場合、そのまま指定してもエラーが出てしまいます。
df.pivot_table(index='Name', aggfunc=np.quantile)
q(何パーセントの値を取るのか)が指定されていないというエラー。
TypeError: quantile() missing 1 required positional argument: 'q'
苦し紛れに変数を渡すこともできません。
df.pivot_table(index='Name', aggfunc=np.quantile(q=0.1))
エラーが出てしまいます。
TypeError: quantile() missing 1 required positional argument: 'a'
このような場合は関数を自作してやる必要があります。
10パーセント値を計算する関数にラッピングする。
def quantile_10p(x): return np.quantile(x, q=0.1) df.pivot_table(index='Name', aggfunc=quantile_10p)
エラーが出ずに計算できた。
PetalLength PetalWidth SepalLength SepalWidth Name Iris-setosa 1.30 0.10 4.59 3.00 Iris-versicolor 3.59 1.00 5.38 2.30 Iris-virginica 4.90 1.79 5.80 2.59
fill_value
集計結果に欠損がある場合に埋める値を指定するパラメータ。デフォルトはNone(埋めない)。pivot_tableの処理後にfillna(x)をするのと同様です。
columnsの項で検討した例で、fill_valueに1を指定してみます。
df.pivot_table(values='SepalLength', index='SepalLength_cut', columns='Name', fill_value=1)
NaNになっていた部分が1で埋められました。
Name Iris-setosa Iris-versicolor Iris-virginica SepalLength_cut (4.296, 5.02] 4.764286 4.966667 4.900000 (5.02, 5.74] 5.290476 5.538889 5.650000 (5.74, 6.46] 5.800000 6.085000 6.176190 (6.46, 7.18] 1.000000 6.722222 6.726667 (7.18, 7.9] 1.000000 1.000000 7.509091
margins
グルーピング変数での集計結果の他に全体の集計結果を追加したいときに使うパラメータ。デフォルトはFalse(全体の集計はしない)。
Trueを指定すると全体の集計をしてくれます。地味に知らないと不便な引数ですね。
df.pivot_table(index='Name', values='SepalLength', margins=True)
Allという列が追加され、全体の平均が計算されている。
SepalLength Name Iris-setosa 5.006000 Iris-versicolor 5.936000 Iris-virginica 6.588000 All 5.843333
dropna
全ての値がNaNのカラムがある場合に、計算対象から外すかを指定するパラメータ。デフォルトはTrue(計算しない)。
あまり用途はなさそうですが一応検証します。
#SepalLengthを全てNaNにする。 df.iloc['SepalLength'] = np.NaN df.pivot_table(index='Name', dropna=False)
全てがNaNのSepalLengthも計算された。
PetalLength PetalWidth SepalLength SepalWidth Name Iris-setosa 1.464 0.244 NaN 3.418 Iris-versicolor 4.260 1.326 NaN 2.770 Iris-virginica 5.552 2.026 NaN 2.974
margins_name
marginがTrueの場合の集計要素の名称を指定するパラメータ。デフォルトはAll。
例えばTestに設定。
df.pivot_table(index='Name', values='SepalLength', margins=True, margins_name='Test')
Testという名称で集計された。
SepalLength Name Iris-setosa 5.006000 Iris-versicolor 5.936000 Iris-virginica 6.588000 Test 5.843333
まとめ
ピボットテーブルの計算をするときに使うpivot_tableの全引数を検証しました。基本はindex, columns, values, marginsを、応用的にはaggfuncを使いこなせるとかなり便利なのではないかと思います。是非活用してみてください。