次のようにgroupby()の結果(DataFrameGroupByオブジェクト)と集約メソッドを組み合わせることで、グループごとの集約ができます。
# 指定した列でグループ化する
grouped = df.groupby(列名)
# グループごとに集約する
grouped.集約メソッド()
DataFrameGroupByには、いろいろな方法の集約メソッドが用意されています。たとえば、次のようなメソッドです。
集約メソッド | 説明 | 集約メソッド | 説明 |
---|---|---|---|
max() | 最大値 | quantile() | 指定したパーセンタイルのデータ |
min() | 最小値 | describe() | 基本統計量(複数の集約値) |
mean() | 平均値 | size() | 各グループのデータ(DataFrame)の行数(※) |
median() | 中央値 | count() | データの個数(NaN を含まない) |
sum() | 合計 | nunique() | ユニーク数 |
std() | 標準偏差 | idxmax() | 最大値のデータのインデックス |
var() | 分散 | idxmin() | 最小値のデータのインデックス |
行数、データ数は、size()
固有数値の数は、unique()
データ数 欠損値は含まないは count()
集約のメソッドは、
def time_120_over(sr):
count=(sr["利用時間(分)"]>=120).sum()
return count>=2
条件を入れて使用することが出来る。
練習
集約メソッドによって、出力結果の形式が若干異なります。
たとえば、平均を求めるmean()
では数値の列は対象となりますが、文字列の列は無視されます。これに対し、ユニーク数を求めるnunique()
では、文字列の列も対象になります。
具体的な例を見てみましょう。次のような試験結果のデータについて考えます。列点数
と列学習時間(分)
は整数、列部活動
には文字列が格納されています。
生徒ID | クラス | 点数 | 学習時間(分) | 部活動 |
---|---|---|---|---|
ST001 | 1-A | 60 | 232 | 合唱部 |
ST002 | 1-A | 87 | 345 | 科学部 |
ST003 | 1-B | 66 | 180 | 美術部 |
ST004 | 1-A | 72 | 22 | 合唱部 |
ST005 | 1-B | 74 | 120 | 美術部 |
mean()
を使うと、数値の列である列点数
と列学習時間(分)
はクラスごとの平均値が計算されます。
しかし、文字列である列部活動
は平均を計算できないため、無視されます。
# クラスごとに平均値を計算
# 結果には列「部活動」は含まれない
df.groupby("クラス").mean()
クラス | 点数 | 学習時間(分) |
---|---|---|
1-A | 73.0 | 199.666667 |
1-B | 70.0 | 150.000000 |
これに対し、ユニーク数は文字列でも計算できるため、nunique()
では列部活動
も含めて結果が取得できます。
# クラスごとにユニーク数を計算
# 結果にはすべての列が含まれる
df.groupby("クラス").nunique()
クラス | 点数 | 学習時間(分) | 部活動 |
---|---|---|---|
1-A | 3 | 3 | 2 |
1-B | 2 | 2 | 1 |
また、各グループの行数を計算するsize()の結果は、次のようにSeriesになります。これは、同じグループであれば当然どの列も同じ行数になるため、列に関わらずグループごとに1つの値になるからです。
# クラスごとに行数を計算
# 結果はSeriesになる
df.groupby("クラス").size()
クラス
1-A 3
1-B 2
dtype: int64
演習
まずは、今回使うデータを読み込みましょう。1行あたり1生徒のデータで、所属するクラス・試験の点数、学習時間(分)、部活動を表す列があります。
import pandas as pd
# 試験結果のデータを読み込み
df = pd.read_csv("dataset/score_study_time_club.csv", index_col="生徒ID")
# 先頭5行を確認
df.head()
クラス | 点数 | 学習時間(分) | 部活動 | |
---|---|---|---|---|
生徒ID | ||||
ST001 | 1-A | 48.0 | 226 | 合唱部 |
ST002 | 1-A | 0.0 | 24 | 科学部 |
ST003 | 1-B | 80.0 | 271 | 科学部 |
ST004 | 1-A | NaN | 45 | 合唱部 |
ST005 | 1-A | 68.0 | 271 | 美術部 |
今回はクラスごとに平均点や生徒数を求めるので、groupby()
を使ってクラスごとにグループ化します。
# クラスでグループ化
grouped = df.groupby("クラス")
(1)各クラスの生徒数
まずは、各クラスの生徒数を計算してみましょう。今回のデータは1行あたり1生徒で、重複する生徒のデータはないため、各グループの行数がそのまま各クラスの生徒数になります。
各グループの行数はsize()
を使って求められます。実行すると、グループ1-A
もグループ1-B
も、生徒が10人ずつだとわかります。
# 各クラスの生徒数(= 各グループの行数)
size_sr = grouped.size()
size_sr
クラス
1-A 10
1-B 10
dtype: int64
(2)各クラスの試験を受けた生徒数
次に、試験を受けた生徒数をクラスごとに求めてみましょう。
今回のデータでは、試験を欠席した生徒は列点数
がNaN
になっています。そのため「NaN
ではないデータの個数」を数えれば、試験を受けた生徒数がわかります。
「NaN
ではないデータの個数」はcount()
を使って求められます。実行すると、各列の結果が表示されます。列点数
の結果を確認しましょう。グループ1-A
では9人、グループ1-B
では8人の生徒が試験を受けたことがわかります。
# NaN以外のデータの個数を確認
count_df = grouped.count()
count_df
点数 | 学習時間(分) | 部活動 | |
---|---|---|---|
クラス | |||
1-A | 9 | 10 | 10 |
1-B | 8 | 10 | 10 |
また、今回のように特定の列の結果だけを知りたい場合は、次のようにSeriesGroupByオブジェクトを使うと指定した列の集約結果だけが得られます。
# SeriesGroupByオブジェクトを使って
# 列「点数」の集約結果を確認
grouped["点数"].count()
クラス
1-A 9
1-B 8
Name: 点数, dtype: int64
結果が正しいことを確認するために、列点数
がNaN
だった行を調べてみましょう。df[df[列名].isna()]
のように書くと、指定した列が欠損値である行を抽出できます。
次のコードを実行すると、1-A
では1人の生徒(ST004
)が、1-B
では2人の生徒(ST010
とST018
)の列点数
がNaN
であることがわかります。各クラス全体の生徒数は10人なので、1-A
は10-1=9、1-B
は10-2=8で、それぞれcount()
の結果と一致しますね。
# 列「点数」がNaNの行を確認
df[df["点数"].isna()]
クラス | 点数 | 学習時間(分) | 部活動 | |
---|---|---|---|---|
生徒ID | ||||
ST004 | 1-A | NaN | 45 | 合唱部 |
ST010 | 1-B | NaN | 229 | 科学部 |
ST018 | 1-B | NaN | 224 | 手芸部 |
(3)各クラスの列点数
と列学習時間(分)
の中央値
次に、各クラスの点数と学習時間の中央値を計算してみましょう。
集約メソッドのmedian()
を使うと、グループごとに各列の中央値を計算できます。なお、文字列が格納されている列部活動
は中央値を計算できないので、median()
の結果には含まれません。
# 各クラスの中央値
median_df = grouped.median()
median_df
点数 | 学習時間(分) | |
---|---|---|
クラス | ||
1-A | 78.0 | 278.5 |
1-B | 72.0 | 226.5 |
(4)各クラスの生徒が所属している部活動の種類数
最後に、各クラスの生徒が所属している部活動が何種類あるのか計算してみましょう。
部活動の種類の数なので、列部活動
のユニーク数を求めればよいです。nunique()
を使うと、グループごとに各列のユニーク数を求められます。次のコードを実行すると、グループ1-A
の生徒が属する部活動は5種類、1-B
は7種類だとわかります。
# ユニーク数を確認
nunique_sr = grouped["部活動"].nunique()
nunique_sr
クラス 1-A 5 1-B 7 Name: 部活動, dtype: int64
実際にデータを確認してみましょう。前クエスト「グループ化でできることを知ろう」で学んだように、get_group()
でグループ名を指定すると、指定したグループのデータを取得できます。
get_group()
を使って1-A
のデータを確認すると、列部活動
は「合唱部」「科学部」「美術部」「ラグビー部」「サッカー部」の5種類あり、nunique()
で求めた結果と一致することがわかります。
# 1-Aのデータを確認
grouped.get_group("1-A")
クラス | 点数 | 学習時間(分) | 部活動 | |
---|---|---|---|---|
生徒ID | ||||
ST001 | 1-A | 48.0 | 226 | 合唱部 |
ST002 | 1-A | 0.0 | 24 | 科学部 |
ST004 | 1-A | NaN | 45 | 合唱部 |
ST005 | 1-A | 68.0 | 271 | 美術部 |
ST007 | 1-A | 49.0 | 236 | ラグビー部 |
ST011 | 1-A | 98.0 | 381 | 科学部 |
ST012 | 1-A | 84.0 | 286 | ラグビー部 |
ST017 | 1-A | 81.0 | 355 | 科学部 |
ST019 | 1-A | 78.0 | 326 | サッカー部 |
ST020 | 1-A | 90.0 | 301 | サッカー部 |
コメント