グループごとの変換 rank()

「変換」ではグループごとに 個々のデータ に対して処理を行う点が特徴です。そのため、次のようにグループごとにデータの個数分の結果が出ます。

https://images.pyq.jp/repo/prod/pandas_groupby_transform_v2/transform.jpg

変換処理の方法

次のようにgroupby()の結果(DataFrameGroupByオブジェクト)と変換メソッドを組み合わせることで、グループごとのデータの変換ができます。

# 指定した列名でグループ化
grouped = df.groupby(列名)
# グループごとにデータを変換する
grouped.変換メソッド()

DataFrameGroupByには、いろいろな変換メソッドがあります。基本的に、DataFrameで使えたメソッドはDataFrameGroupByでも使えます。

変換メソッド説明集約メソッド説明
rank()グループ内の順位を計算cumcount()グループ内で0始まりの通し番号をふる(※)
cumsum()グループ内で累積和を計算cumprod()グループ内で累積積を計算
diff()グループ内で階差を計算pct_change()グループ内で変化率を計算

※ cumcount()はDataFrameGroupByだけで使えます(DataFrameでは使えません)。

具体的な例として、本問ではrank()の使い方を紹介します。

DataFrameGroupByのrank()

パート「データ加工」のクエスト「よくある計算をしてみよう」では、DataFrameのrank()を使って順位を計算する方法を学びました。DataFrameGroupByの同名のメソッドを使うと、これらの処理をグループごとに適用できます。

DataFrameGroupByオブジェクトのrank()は次のようにして使います。

# 指定した列名ごとにグループ化
grouped = df.groupby(列名)
# グループごとに順位を計算
grouped.rank()

DataFrameのrank()同様、DataFrameGroupByオブジェクトのrank()でも、引数ascendingや引数methodが使えます。


生徒ID
クラス点数学習時間(分)
ST0011-A60232
ST0021-A87345
ST0031-B66180
ST0041-A7222
ST0051-B74120
ST0061-B58215

クラスごとに、各列の順位を計算してみましょう。値が大きい方から1位、2位……と順位をつける場合、引数ascendingFalseを指定します。

# 列「クラス」でグループ化
grouped = df.groupby("クラス")
# グループごとに、大きい順で順位を計算
grouped.rank(ascending=False)

生徒ID
点数学習時間(分)
ST0013.02.0
ST0021.01.0
ST0032.02.0
ST0042.03.0
ST0051.03.0
ST0063.01.0

グループごとに、列点数と列学習時間(分)でそれぞれ大きい順に順位が出されていることがわかります。
たとえば列点数では、1-Aの生徒(ST001ST002ST004)の中では、1位がST002(87点)、2位がST004(72点)、3位がST001(60点)になっています。

https://images.pyq.jp/repo/prod/pandas_groupby_transform_v2/groupby_rank.jpg

演習

import pandas as pd

# 試験結果のデータを読み込み
df = pd.read_csv("dataset/score_study_time.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("クラス")

各グループ内での順位はrank()を使って求められます。DataFrameのrank()同様、DataFrameGroupByのrank()も引数ascendingで順位付けを昇順でするか降順でするか指定できます。今回は大きい順に順位付けをしたいので、False(降順)を指定します。In [3]:

# クラス内での順位を計算する(降順で順位付け)
rank_df = grouped.rank(ascending=False)
# 結果の先頭5行を確認
rank_df.head()
点数学習時間(分)
生徒ID
ST0018.08.0
ST0029.010.0
ST0032.03.0
ST004NaN9.0
ST0056.06.0

順位付けの結果を、新しい列として元のdfに追加しましょう。

# 変換の結果を新しい列として追加
df[["クラス内順位_点数", "クラス内順位_学習時間"]] = rank_df
# 結果の先頭5行を確認
df.head()
クラス点数学習時間(分)クラス内順位_点数クラス内順位_学習時間
生徒ID
ST0011-A48.02268.08.0
ST0021-A0.0249.010.0
ST0031-B80.02712.03.0
ST0041-ANaN45NaN9.0
ST0051-A68.02716.06.0

確認のため、クラスが1-Aのデータの結果を見てみましょう。わかりやすいように、sort_values()を使って列点数を並び替えて確認します。列クラス内順位_点数を見ると、クラス1-A内で最も点数が高い生徒ST011が1位、次に点数が高い生徒ST020が2位になっていることがわかります。

# 1-Aのデータを列「点数」でソート
df[df["クラス"] == "1-A"].sort_values("点数")

特定の分類を出す方法

クラス点数学習時間(分)クラス内順位_点数クラス内順位_学習時間
生徒ID
ST0021-A0.0249.010.0
ST0011-A48.02268.08.0
ST0071-A49.02367.07.0
ST0051-A68.02716.06.0
ST0191-A78.03265.03.0
ST0171-A81.03554.02.0
ST0121-A84.02863.05.0
ST0201-A90.03012.04.0
ST0111-A98.03811.01.0
ST0041-ANaN45NaN9.0

同様に、クラスが1-Bのデータも確認してみましょう。クラス1-Bの中で最も点数が高い生徒ST014が1位、次に点数が高い生徒ST003が2位になっています。このように、DataFrameGroupByオブジェクトのrank()を使うと、グループごとに順位を計算できます。

# 1-Bのデータを列「点数」でソート
df[df["クラス"] == "1-B"].sort_values("点数")
クラス点数学習時間(分)クラス内順位_点数クラス内順位_学習時間
生徒ID
ST0061-B58.02157.59.0
ST0131-B58.01927.510.0
ST0151-B62.02206.08.0
ST0161-B69.02225.07.0
ST0091-B75.02564.04.0
ST0081-B79.03343.01.0
ST0031-B80.02712.03.0
ST0141-B83.02861.02.0
ST0101-BNaN229NaN5.0
ST0181-BNaN224NaN6.0

なお、rank()はSeriesGroupByオブジェクトでも使えます。特定の列の順位だけを使いたい場合は、こちらを使うとよいでしょう。

# SeriesGroupByオブジェクトのrank()を使う
rank_sr = grouped["点数"].rank()
rank_sr

Out[7]:

生徒ID
ST001    2.0
ST002    1.0
ST003    7.0
ST004    NaN
ST005    4.0
ST006    1.5
ST007    3.0
ST008    6.0
ST009    5.0
ST010    NaN
ST011    9.0
ST012    7.0
ST013    1.5
ST014    8.0
ST015    3.0
ST016    4.0
ST017    6.0
ST018    NaN
ST019    5.0
ST020    8.0
Name: 点数, dtype: float64

rank()の実行結果では元の列名が使われるため、元のデータと混同して分かりにくい場合があります。

たとえば今回の写経のrank()の実行結果では、列名が元データと同じ点数学習時間(分)になっています。

# クラス内での順位を計算する(降順で順位付け)
rank_df = grouped.rank(ascending=False)
# 結果の先頭5行を確認
rank_df.head()

生徒ID
点数学習時間(分)
ST00188
ST002910
ST00323
ST004NaN9
ST00566

このようなときは、add_suffix()が便利です。DataFrameで次のように使うことで、指定した文字列を各列名の末尾に付加 できます。

# 各列名の末尾に、指定した文字列を付加
df.add_suffix(文字列)

たとえば今回の写経のデータの場合、次のように書くと列点数点数_rankに、列学習時間(分)学習時間(分)_rankに一括で変更できます。

# rank()の結果の列名の末尾に"_rank"という文字列を付与する
grouped.rank(ascending=False).add_suffix("_rank")

生徒ID
点数_rank学習時間(分)_rank
ST0018.08.0
ST0029.010.0
ST0032.03.0
ST004NaN9.0

コメント

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