離散変数のあるデータセットにおいて、グループ分けをしてデータの傾向を見たいとき、groupby()やpivot_table()を使います。
離散変数が1つならgroupby()、離散変数が2つならpivot_table()を使います。
使用するデータ
説明のために、次のようなデータを作ります。
ドラクエⅢのルイーダの酒場にいる仲間リストを空想で作ったものです。「職業」と「性別」が離散変数、「人気度」が連続変数になります。
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
data = [
['戦士', '男', 7],
['僧侶', '男', 2],
['魔法使い', '男', 6],
['商人', '男', 4],
['遊び人', '男', 0],
['戦士', '女', 4],
['僧侶', '女', 8],
['僧侶', '女', 9],
['魔法使い', '女', 4],
['商人', '女', 1],
['遊び人', '女', 4],
['戦士', '男', 9],
['僧侶', '男', 3],
['戦士', '女', 2]
]
df = pd.DataFrame(data,
columns = ['職業', '性別', '人気度'])
df
groupby()は離散変数が1つのときに使う
1つの離散変数に基づいてグルーピングするときは、groupby()を使います。
例えば、「職業」に基づいてgroupbyをしてみます。
df.groupby('職業').mean()
職業ごとの人気度の平均値を抽出できました。
「性別」に基づいたgroupbyもやってみます。
df.groupby('性別').mean()
mean()の代わりにmax()やmin()を使うことで、平均ではなく最大値、最小値を抽出することができます。また、describe()を使うことで、さまざまな量を一括で表示させることも可能です。
df.groupby('性別').describe()
groupbyの結果を可視化する場合は、棒グラフがよく使われます。
df1 = df.groupby('性別').mean()
sns.barplot(x = df1.index, y = '人気度', data = df1)
このように、1つの離散変数に対して、連続変数の情報(平均値など)をグループ分けして表示させるのがgroupby()の機能です。
pivot_table()は離散変数が2つのときに使う
2つの離散変数に基づいてグルーピングするときは、pivot_table()を使います。
「職業」と「性別」に基づいてpivot_tableをしてみます。
行(index)、列(columns)、表に入れるデータ(values)、表に入れるデータの詳細(aggfunc)を指定します。
pd.pivot_table(
data = df,
index = '性別',
columns = '職業',
values = '人気度',
aggfunc= 'mean'
)
女性の僧侶、男性の戦士に人気が集まっていることがわかります。
pivot_tableの結果を可視化する場合は、ヒートマップがよく使われます。
df2= pd.pivot_table(
data = df,
index = '性別',
columns = '職業',
values = '人気度',
aggfunc= 'mean'
)
sns.heatmap(df2, annot=True, cmap='Purples', fmt="1.1f")
ちなみに、annotとはヒートマップ上に数値を記載するかどうか、cmapはカラーマップの略で「色合い」を、fmtはフォーマットの略で「数値の表記方法」を指定します。
詳しくは、こちらをご参照ください。
ビニング処理をして連続変数を離散変数に変えてから、グルーピングする
連続変数を離散変数に変えてからグルーピングする方法もよく行われます。
離散変数に変えることをビニング(binning)といいます。binとは棒グラフの棒のことです。連続変数を棒グラフの棒(離散変数)にしましょうということです。
次のように年齢情報を追加したデータでビニングをやってみます。
df['年齢'] = [34, 23, 65, 54, 38, 36, 24, 19, 32, 57, 21, 35, 27, 48]
df
連続変数である「年齢」を離散変数に変えるにはcutやqcutが使われます。
等間隔で分割するのがpd.cut()です。
pd.cut(df['年齢'], 3)
18.954〜34.333歳、34.333〜49.667歳、49.667〜 65.0歳の3カテゴリーに分類してくれました。
分割するしきい値を指定することもできます。
pd.cut(df['年齢'], [0, 30, 50, 70])
また、ラベルをつけることも可能です。
pd.cut(df['年齢'],
[0, 30, 50, 70], labels=['若手', '中堅', 'ベテラン'])
qcutを使うと、分割後のカテゴリーに含まれるデータ数が等しくなるようにしてくれます。
pd.qcut(df['年齢'], 3)
最後に、「年齢」を離散化したものに上書きして、groupbyをやってみましょう。
df['年齢'] = pd.cut(df['年齢'],
[0, 30, 50, 70], labels=['若手', '中堅', 'ベテラン'])
df.groupby('年代').mean()
うまくできました。