【pandas】ピボットテーブルの使い方【pivot・pivot_table】
Excelなどでよく使われるピボットテーブル。
それをpandasのデータフレームで使う場合には
の2種類があります。
今回はピボットテーブルの使い方と2つの関数の違いをご紹介します。
他に似たような機能としてはDataFrame.groupby()
があります
pivot()とpivot_table()の違いは?
pivot()
が簡易版、pivot_table()
のほうが沢山の引数を持ち、色々なことができます。
基本的にpivot_table()
を使うと考えてよいでしょう。
使い方を見ていきます。
pivot()
DataFrame.pivot(index=None, columns=None, values=None)
まずはデータの準備をします。
flights = sns.load_dataset("flights")
flights.head()
year | month | passengers | |
---|---|---|---|
0 | 1949 | Jan | 112 |
1 | 1949 | Feb | 118 |
2 | 1949 | Mar | 132 |
3 | 1949 | Apr | 129 |
4 | 1949 | May | 121 |
基本形
インデックス、カラムを指定します。
flights_p = flights.pivot(index="year", columns="month", values=["passengers"])
# もしくは
flights_p = flights.pivot("year", "month","passengers")
flights_p
month | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec |
---|---|---|---|---|---|---|---|---|---|---|---|---|
year | ||||||||||||
1949 | 112 | 118 | 132 | 129 | 121 | 135 | 148 | 148 | 136 | 119 | 104 | 118 |
1950 | 115 | 126 | 141 | 135 | 125 | 149 | 170 | 170 | 158 | 133 | 114 | 140 |
1951 | 145 | 150 | 178 | 163 | 172 | 178 | 199 | 199 | 184 | 162 | 146 | 166 |
1952 | 171 | 180 | 193 | 181 | 183 | 218 | 230 | 242 | 209 | 191 | 172 | 194 |
1953 | 196 | 196 | 236 | 235 | 229 | 243 | 264 | 272 | 237 | 211 | 180 | 201 |
1954 | 204 | 188 | 235 | 227 | 234 | 264 | 302 | 293 | 259 | 229 | 203 | 229 |
1955 | 242 | 233 | 267 | 269 | 270 | 315 | 364 | 347 | 312 | 274 | 237 | 278 |
1956 | 284 | 277 | 317 | 313 | 318 | 374 | 413 | 405 | 355 | 306 | 271 | 306 |
1957 | 315 | 301 | 356 | 348 | 355 | 422 | 465 | 467 | 404 | 347 | 305 | 336 |
1958 | 340 | 318 | 362 | 348 | 363 | 435 | 491 | 505 | 404 | 359 | 310 | 337 |
1959 | 360 | 342 | 406 | 396 | 420 | 472 | 548 | 559 | 463 | 407 | 362 | 405 |
1960 | 417 | 391 | 419 | 461 | 472 | 535 | 622 | 606 | 508 | 461 | 390 | 432 |
そのままヒートマップにすることもできます。
sns.heatmap(flights_p)
複数項目の表示
より複雑なデータフレームを扱ってみます。
tips = sns.load_dataset("tips")
tips.head()
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
0 | 16.99 | 1.01 | Female | No | Sun | Dinner | 2 |
1 | 10.34 | 1.66 | Male | No | Sun | Dinner | 3 |
2 | 21.01 | 3.50 | Male | No | Sun | Dinner | 3 |
3 | 23.68 | 3.31 | Male | No | Sun | Dinner | 2 |
4 | 24.59 | 3.61 | Female | No | Sun | Dinner | 4 |
重複するラベルのエラー
重複する項目があるとエラーが発生します。
tips = tips.pivot("day", "time")
# 重複するラベルがあるとエラーが発生する
なぜ表示できないかというと、重複する項目があると表示できないからです。
このエラーはpivot_table()では表示されません。
pivot_table()
は重複した場合、平均値をとります。
しかし、pivot()
の場合は値をそのまま表示するからです。
groupbyとの組み合わせで表示
pivot_table()
を使えば表示できますが、groupby
との組み合わせで実現できます。
tips_g = tips.groupby(["day", "time"], as_index=False).sum()
tips_g
day | time | total_bill | tip | size | |
---|---|---|---|---|---|
0 | Thur | Lunch | 1077.55 | 168.83 | 150.0 |
1 | Thur | Dinner | 18.78 | 3.00 | 2.0 |
2 | Fri | Lunch | 89.92 | 16.68 | 14.0 |
3 | Fri | Dinner | 235.96 | 35.28 | 26.0 |
4 | Sat | Lunch | NaN | NaN | NaN |
5 | Sat | Dinner | 1778.40 | 260.40 | 219.0 |
6 | Sun | Lunch | NaN | NaN | NaN |
7 | Sun | Dinner | 1627.16 | 247.39 | 216.0 |
groupby()
でデータをまとめた後にピボットテーブルで表示してみます。
tips_p = tips_g.pivot("day","time")
tips_p
total_bill | tip | size | ||||
---|---|---|---|---|---|---|
time | Lunch | Dinner | Lunch | Dinner | Lunch | Dinner |
day | ||||||
Thur | 1077.55 | 18.78 | 168.83 | 3.00 | 150.0 | 2.0 |
Fri | 89.92 | 235.96 | 16.68 | 35.28 | 14.0 | 26.0 |
Sat | NaN | 1778.40 | NaN | 260.40 | NaN | 219.0 |
Sun | NaN | 1627.16 | NaN | 247.39 | NaN | 216.0 |
やっと表示できました。おとなしくpivot_table()
使ったほうがいいですね・・・。
pivot_table()
DataFrame.pivot_table
(values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All', observed=False)
先ほどのflightsデータをもう一度扱ってみましょう。
flights.head()
year | month | passengers | |
---|---|---|---|
0 | 1949 | Jan | 112 |
1 | 1949 | Feb | 118 |
2 | 1949 | Mar | 132 |
3 | 1949 | Apr | 129 |
4 | 1949 | May | 121 |
基本形
flight_pt = flights.pivot_table(index="year", columns="month", values="passengers")
flight_pt
month | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec |
---|---|---|---|---|---|---|---|---|---|---|---|---|
year | ||||||||||||
1949 | 112 | 118 | 132 | 129 | 121 | 135 | 148 | 148 | 136 | 119 | 104 | 118 |
1950 | 115 | 126 | 141 | 135 | 125 | 149 | 170 | 170 | 158 | 133 | 114 | 140 |
1951 | 145 | 150 | 178 | 163 | 172 | 178 | 199 | 199 | 184 | 162 | 146 | 166 |
1952 | 171 | 180 | 193 | 181 | 183 | 218 | 230 | 242 | 209 | 191 | 172 | 194 |
1953 | 196 | 196 | 236 | 235 | 229 | 243 | 264 | 272 | 237 | 211 | 180 | 201 |
1954 | 204 | 188 | 235 | 227 | 234 | 264 | 302 | 293 | 259 | 229 | 203 | 229 |
1955 | 242 | 233 | 267 | 269 | 270 | 315 | 364 | 347 | 312 | 274 | 237 | 278 |
1956 | 284 | 277 | 317 | 313 | 318 | 374 | 413 | 405 | 355 | 306 | 271 | 306 |
1957 | 315 | 301 | 356 | 348 | 355 | 422 | 465 | 467 | 404 | 347 | 305 | 336 |
1958 | 340 | 318 | 362 | 348 | 363 | 435 | 491 | 505 | 404 | 359 | 310 | 337 |
1959 | 360 | 342 | 406 | 396 | 420 | 472 | 548 | 559 | 463 | 407 | 362 | 405 |
1960 | 417 | 391 | 419 | 461 | 472 | 535 | 622 | 606 | 508 | 461 | 390 | 432 |
pivot()
と変わらないです。
複数リストの指定
次はtips
データ見てみます。
tips.head()
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
0 | 16.99 | 1.01 | Female | No | Sun | Dinner | 2 |
1 | 10.34 | 1.66 | Male | No | Sun | Dinner | 3 |
2 | 21.01 | 3.50 | Male | No | Sun | Dinner | 3 |
3 | 23.68 | 3.31 | Male | No | Sun | Dinner | 2 |
4 | 24.59 | 3.61 | Female | No | Sun | Dinner | 4 |
indexやcolumns、valuesはリストで値を渡せます。
tips_pt = tips.pivot_table(index=["time", "day"],
columns=["sex","smoker"],
values="total_bill")
tips_pt
sex | Male | Female | |||
---|---|---|---|---|---|
smoker | Yes | No | Yes | No | |
time | day | ||||
Lunch | Thur | 19.171000 | 18.486500 | 19.218571 | 15.899167 |
Fri | 11.386667 | NaN | 13.260000 | 15.980000 | |
Dinner | Thur | NaN | NaN | NaN | 18.780000 |
Fri | 25.892000 | 17.475000 | 12.200000 | 22.750000 | |
Sat | 21.837778 | 19.929063 | 20.266667 | 19.003846 | |
Sun | 26.141333 | 20.403256 | 16.540000 | 20.824286 |
欠損値の穴埋め
欠損値の穴埋めは引数にfill_value=""
とします。
tips_pt2 = tips.pivot_table(fill_value=0,
index=["time", "day"],
columns=["sex","smoker"],
values="total_bill")
tips_pt2
sex | Male | Female | |||
---|---|---|---|---|---|
smoker | Yes | No | Yes | No | |
time | day | ||||
Lunch | Thur | 19.171000 | 18.486500 | 19.218571 | 15.899167 |
Fri | 11.386667 | 0.000000 | 13.260000 | 15.980000 | |
Dinner | Thur | 0.000000 | 0.000000 | 0.000000 | 18.780000 |
Fri | 25.892000 | 17.475000 | 12.200000 | 22.750000 | |
Sat | 21.837778 | 19.929063 | 20.266667 | 19.003846 | |
Sun | 26.141333 | 20.403256 | 16.540000 | 20.824286 |
カテゴリの集計値
各カテゴリの集計値を表示する場合にはmargins=True
とします。
tips_pt = tips.pivot_table(index=["time", "day"],
columns=["sex"],
values=["total_bill","size"],
fill_value=0,
margins=True)
tips_pt
size | total_bill | ||||||
---|---|---|---|---|---|---|---|
sex | Male | Female | All | Male | Female | All | |
time | day | ||||||
Lunch | Thur | 2.433333 | 2.483871 | 2.459016 | 18.714667 | 16.648710 | 17.664754 |
Fri | 1.666667 | 2.250000 | 2.000000 | 11.386667 | 13.940000 | 12.845714 | |
Dinner | Thur | 0.000000 | 2.000000 | 2.000000 | 0.000000 | 18.780000 | 18.780000 |
Fri | 2.285714 | 2.000000 | 2.166667 | 23.487143 | 14.310000 | 19.663333 | |
Sat | 2.644068 | 2.250000 | 2.517241 | 20.802542 | 19.680357 | 20.441379 | |
Sun | 2.810345 | 2.944444 | 2.842105 | 21.887241 | 19.872222 | 21.410000 | |
All | 2.630573 | 2.459770 | 2.569672 | 20.744076 | 18.056897 | 19.785943 |
集計値は引数にaggfunc
を指定しない場合、平均値になります。
ALLにも平均値になっていますね。
集計方法の指定
集計方法は引数:aggfunc=""
にて指定できます。()は不要です。
合計値を計算してみます。
tips_pt = tips.pivot_table(index=["time", "day"],
columns=["sex"],
values=["total_bill","size"],
fill_value=0,
margins=True,
aggfunc=sum)
tips_pt
size | total_bill | ||||||
---|---|---|---|---|---|---|---|
sex | Male | Female | All | Male | Female | All | |
time | day | ||||||
Lunch | Thur | 73 | 77 | 150 | 561.44 | 516.11 | 1077.55 |
Fri | 5 | 9 | 14 | 34.16 | 55.76 | 89.92 | |
Dinner | Thur | 0 | 2 | 2 | 0.00 | 18.78 | 18.78 |
Fri | 16 | 10 | 26 | 164.41 | 71.55 | 235.96 | |
Sat | 156 | 63 | 219 | 1227.35 | 551.05 | 1778.40 | |
Sun | 163 | 53 | 216 | 1269.46 | 357.70 | 1627.16 | |
All | 413 | 214 | 627 | 3256.82 | 1570.95 | 4827.77 |
これは便利ですねー。
複数の集計項目
aggfunc
にはリストで複数の集計方法を渡せます。
tips_pt = tips.pivot_table(index=["time", "day"],
columns=["sex"],
values=["total_bill"],
fill_value=0,
margins=True,
aggfunc=[sum, max])
tips_pt
sum | max | ||||||
---|---|---|---|---|---|---|---|
total_bill | total_bill | ||||||
sex | Male | Female | All | Male | Female | All | |
time | day | ||||||
Lunch | Thur | 561.44 | 516.11 | 1077.55 | 41.19 | 43.11 | 43.11 |
Fri | 34.16 | 55.76 | 89.92 | 13.42 | 16.27 | 16.27 | |
Dinner | Thur | 0.00 | 18.78 | 18.78 | 0.00 | 18.78 | 18.78 |
Fri | 164.41 | 71.55 | 235.96 | 40.17 | 22.75 | 40.17 | |
Sat | 1227.35 | 551.05 | 1778.40 | 50.81 | 44.30 | 50.81 | |
Sun | 1269.46 | 357.70 | 1627.16 | 48.17 | 35.26 | 48.17 | |
All | 3256.82 | 1570.95 | 4827.77 | 50.81 | 44.30 | 50.81 |
機械学習・データ処理を学ぶのにおすすめの教材
じっくり書籍で学習するなら!
コメント