集計の方法

次のように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
クラス点数学習時間(分)部活動
ST0011-A60232合唱部
ST0021-A87345科学部
ST0031-B66180美術部
ST0041-A7222合唱部
ST0051-B74120美術部

mean()を使うと、数値の列である列点数と列学習時間(分)はクラスごとの平均値が計算されます。
しかし、文字列である列部活動は平均を計算できないため、無視されます。

# クラスごとに平均値を計算
# 結果には列「部活動」は含まれない
df.groupby("クラス").mean()

クラス
点数学習時間(分)
1-A73.0199.666667
1-B70.0150.000000

これに対し、ユニーク数は文字列でも計算できるため、nunique()では列部活動も含めて結果が取得できます。

# クラスごとにユニーク数を計算
# 結果にはすべての列が含まれる
df.groupby("クラス").nunique()

クラス
点数学習時間(分)部活動
1-A332
1-B221

また、各グループの行数を計算する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
ST0011-A48.0226合唱部
ST0021-A0.024科学部
ST0031-B80.0271科学部
ST0041-ANaN45合唱部
ST0051-A68.0271美術部

今回はクラスごとに平均点や生徒数を求めるので、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-A91010
1-B81010

また、今回のように特定の列の結果だけを知りたい場合は、次のようにSeriesGroupByオブジェクトを使うと指定した列の集約結果だけが得られます。

# SeriesGroupByオブジェクトを使って
# 列「点数」の集約結果を確認
grouped["点数"].count()
クラス
1-A    9
1-B    8
Name: 点数, dtype: int64

結果が正しいことを確認するために、点数NaNだった行を調べてみましょう。df[df[列名].isna()]のように書くと、指定した列が欠損値である行を抽出できます。

次のコードを実行すると、1-Aでは1人の生徒(ST004)が、1-Bでは2人の生徒(ST010ST018)の列点数NaNであることがわかります。各クラス全体の生徒数は10人なので、1-Aは10-1=9、1-Bは10-2=8で、それぞれcount()の結果と一致しますね。

# 列「点数」がNaNの行を確認
df[df["点数"].isna()]
クラス点数学習時間(分)部活動
生徒ID
ST0041-ANaN45合唱部
ST0101-BNaN229科学部
ST0181-BNaN224手芸部

(3)各クラスの列点数と列学習時間(分)の中央値

次に、各クラスの点数と学習時間の中央値を計算してみましょう。

集約メソッドのmedian()を使うと、グループごとに各列の中央値を計算できます。なお、文字列が格納されている列部活動は中央値を計算できないので、median()の結果には含まれません。

# 各クラスの中央値
median_df = grouped.median()
median_df
点数学習時間(分)
クラス
1-A78.0278.5
1-B72.0226.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
ST0011-A48.0226合唱部
ST0021-A0.024科学部
ST0041-ANaN45合唱部
ST0051-A68.0271美術部
ST0071-A49.0236ラグビー部
ST0111-A98.0381科学部
ST0121-A84.0286ラグビー部
ST0171-A81.0355科学部
ST0191-A78.0326サッカー部
ST0201-A90.0301サッカー部

コメント

タイトルとURLをコピーしました