DataFrameGroupByのtransform()
次のようにtransform()
を使うことで、関数を使った変換処理ができます。そのため、指定する関数次第で柔軟な処理が行えます。
# 指定した列でグループ化
grouped = df.groupby(列名)
# 指定した関数の処理を使って変換
grouped.transform(関数)
変換方法として指定する関数には、「引数でグループごとに各列のSeriesを受け取り、戻り値で変換結果を返す」 ような処理を定義します。戻り値には、引数と同じ要素数のSeries か、スカラー(1つの値) を指定できます。
具体的な例を見てみましょう。次のような試験結果と学習時間のデータについて考えます。
生徒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 |
ST006 | 1-B | 58 | 215 |
クラスごとに、「各クラスの最大値と個々のデータの差」 を計算したいとします。
たとえば列点数
では、クラス1-A
のデータは「最大値87
との差」が、クラス1-B
のデータは「最大値74
との差」が計算されることになります。
また列学習時間(分)
では、クラス1-A
のデータは「最大値345
との差」が、クラス1-B
のデータは「最大値215
との差」が計算されることになります。
まずは、次のように各列のSeriesを受け取る関数を作成します。
def calc_max_diff(sr):
# グループの最大値と各データの差を計算する
# 引数srには各列のSeriesが渡される
return sr.max() - sr
上記の関数をtransform()
の引数に渡すと、各クラスの各列のSeriesにcalc_max_diff()
が適用されます。
# 列「クラス」でグループ化
grouped = df.groupby("クラス")
# グループごとに変換関数を適用
grouped.transform(calc_max_diff)
生徒ID | 点数 | 学習時間(分) |
---|---|---|
ST001 | 27 | 113 |
ST002 | 0 | 0 |
ST003 | 8 | 35 |
ST004 | 15 | 323 |
ST005 | 0 | 95 |
ST006 | 16 | 0 |
結果を確認してみましょう。列点数
に着目すると、下記のようになります。
1-A
に属する生徒ST001
(60点)は87 - 60 = 27
で27点1-A
に属する生徒ST002
(87点)は87 - 87 = 0
で0点1-B
に属する生徒ST003
(66点)は74 - 66 = 8
で8点
このように、グループごとに最高値との差が計算されていることがわかります。
SeriesGroupByのtransform()
SeriesGroupByでもtransform()
は使えます。
DataFrameGroupBy同様、各グループのSeriesを受け取り、同じサイズのSeriesまたはスカラーを返す関数を指定します。
# 指定した列でグループ化
grouped = df.groupby(列名)
# 指定した関数の処理を使って変換
grouped[列名].transform(関数)
演習
import pandas as pd # 試験結果のデータを読み込み df = pd.read_csv("dataset/score_study_time.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-A
では98、クラス1-B
では83です。そのため、クラス1-A
のデータは98との差分を、クラス1-B
のデータでは83との差分を計算します。列学習時間(分)
も同様に、1-A
のデータでは381、1-B
のデータでは334との差分を計算することになります。
# 各グループの最大値 grouped.max()
点数 | 学習時間(分) | |
---|---|---|
クラス | ||
1-A | 98.0 | 381 |
1-B | 83.0 | 334 |
transform()
を使うと、指定した関数をグループごとに適用できます。 次のように 「各列のSeriesを引数で受け取り、Seriesの最大値との個々のデータの差を返す関数」 を定義します。
# transformで指定する関数 def calc_max_diff(sr): # 各データとグループの最大値との差を計算する # 引数srには各列のSeriesが渡される return sr.max() - sr
定義した関数をtarnsform()
で指定して実行します。
# グループごとに変換関数を適用 max_diff_df = grouped.transform(calc_max_diff) # 結果の先頭5行を確認 max_diff_df.head()
点数 | 学習時間(分) | |
---|---|---|
生徒ID | ||
ST001 | 50.0 | 155 |
ST002 | 98.0 | 357 |
ST003 | 3.0 | 63 |
ST004 | NaN | 336 |
ST005 | 30.0 | 110 |
実行結果を新しい列として元のdf
に追加し、確認してみましょう。
たとえば、1-A
の列点数
の最大値は98点だったので、1-A
に所属する生徒ST001(48点)の列クラス内最大値との差_点数
は 98 - 48 = 50
となり、結果は50点になっています。これに対し、ST003(80点)は1-B
に所属しているので、1-B
の最大値である83点との差が計算され、83 - 3 = 3
で結果は3点になっています。
このように、グループごとに異なる処理が適用されていることがわかります。In [6]:
# 変換結果を新しい列として追加
df[["クラス内最大値との差_点数", "クラス内最大値との差_学習時間"]] = max_diff_df
# 結果の先頭5行を確認
df.head()
2つの値を入れる時は、df[[a,b]]とリストで代入するようにする。
クラス | 点数 | 学習時間(分) | クラス内最大値との差_点数 | クラス内最大値との差_学習時間 | |
---|---|---|---|---|---|
生徒ID | |||||
ST001 | 1-A | 48.0 | 226 | 50.0 | 155 |
ST002 | 1-A | 0.0 | 24 | 98.0 | 357 |
ST003 | 1-B | 80.0 | 271 | 3.0 | 63 |
ST004 | 1-A | NaN | 45 | NaN | 336 |
ST005 | 1-A | 68.0 | 271 | 30.0 | 110 |
transform()
で適用する関数でスカラーを返すと、同じグループでは同じ結果になります。
たとえば、次のコードでは各列のSeriesの最大値を返しています。結果を確認すると、1-A
に所属する生徒(ST001
、ST002
、ST004
、ST005
など)の列点数
は、すべて98.0
になっていることがわかります。
def calc_group_max(sr): # スカラーを返す関数の例 # 各グループの最大値を返す(同じグループでは同じ値になる) return sr.max() # スカラー(1つの値) # スカラーを返す関数を適用する max_df = grouped.transform(calc_group_max) max_df.head()
点数 | 学習時間(分) | |
---|---|---|
生徒ID | ||
ST001 | 98.0 | 381.0 |
ST002 | 98.0 | 381.0 |
ST003 | 83.0 | 334.0 |
ST004 | 98.0 | 381.0 |
ST005 | 98.0 | 381.0 |
(2)クラス内の偏差値
次に、列点数
の値を使って、クラス内の偏差値を出してみましょう。ここでの偏差値は、次の式で計算します。
((データの点数 - グループ内の平均) / グループ内の標準偏差) * 10 + 50
まずは、偏差値を計算する関数を定義しましょう。In [8]:
def calc_dev(sr): # 偏差値を計算する # 引数srには各列のSeriesが渡される # ((個人の点数 - クラスの平均)/ クラスの標準偏差) * 10 + 50 return (sr - sr.mean()) / sr.std() * 10 + 50
agg()
と違い、DataFrameGroupByのtransform()
では指定した関数がすべての列に適用されます。特定の列だけを指定することはできません。
そのため、今回は次のようにSeriesGroupByオブジェクトのtransform()
を使います。
# クラス内の偏差値を計算する # 列「点数」だけに適用 dev_sr = grouped["点数"].transform(calc_dev) # 結果の先頭5行を確認 dev_sr.head()
生徒ID ST001 43.953391 ST002 28.025736 ST003 59.328339 ST004 NaN ST005 50.589913 Name: 点数, dtype: float64
実行した結果を、新しい列として追加しましょう。In [10]:
# クラス内偏差値を新しい列として追加 df["クラス内偏差値"] = dev_sr # 結果の先頭5行を確認 df.head()
クラス | 点数 | 学習時間(分) | クラス内最大値との差_点数 | クラス内最大値との差_学習時間 | クラス内偏差値 | |
---|---|---|---|---|---|---|
生徒ID | ||||||
ST001 | 1-A | 48.0 | 226 | 50.0 | 155 | 43.953391 |
ST002 | 1-A | 0.0 | 24 | 98.0 | 357 | 28.025736 |
ST003 | 1-B | 80.0 | 271 | 3.0 | 63 | 59.328339 |
ST004 | 1-A | NaN | 45 | NaN | 336 | NaN |
ST005 | 1-A | 68.0 | 271 | 30.0 | 110 | 50.589913 |
では、引数を持つ関数を適用したい場合、どうすればよいでしょうか。
たとえば次の関数では、各データのグループの最大値との差を計算した後、引数x
を掛けています。
(なお、これはあくまで説明用の例であり、この処理自体に何か統計的に意味があるわけではありません)
def calc_max_diff_with_x(sr, x):
# 「各データとグループの最大値との差」のx倍を計算する
# 引数srには各列のSeriesが渡される
return (sr.max() - sr) * x
上記のx
のように追加の引数を指定したい場合は、下記のようにtansform()
呼び出し時にキーワード引数で指定します。前クエスト「グループ化して集約しよう」で学んだagg()
と同じ要領です。
import pandas as pd
# 試験結果のデータを読み込み
df = pd.read_csv("dataset/score_study_time.csv", index_col="生徒ID")
# 列「クラス」でグループ化
grouped = df.groupby("クラス")
# x=1倍に指定
grouped.transform(calc_max_diff_with_x, x=1)
生徒ID | 点数 | 学習時間(分) |
---|---|---|
ST001 | 50.0 | 155 |
ST002 | 98.0 | 357 |
ST003 | 3.0 | 63 |
ST004 | NaN | 336 |
… | … | … |
次のように、引数に渡す値を変えることで処理を変更できることがわかります。
# x=2倍に指定
grouped.transform(calc_max_diff_with_x, x=2)
生徒ID | 点数 | 学習時間(分) |
---|---|---|
ST001 | 100.0 | 310 |
ST002 | 196.0 | 714 |
ST003 | 6.0 | 126 |
ST004 | NaN | 672 |
… | … | … |
位置引数で指定したい場合は、次のように第2引数にタプルを使って指定します。
# DataFrameGroupByの場合
grouped.transform(関数, (第2引数に渡す値, 第3引数に渡す値, ... , 第N引数に渡す値))
# SeriesGroupByの場合
grouped[列名].transform(関数, (第2引数に渡す値, 第3引数に渡す値, ... , 第N引数に渡す値))
たとえば、先ほどのcalc_max_diff_with_x()
の第2引数(x
)に2
を指定する場合、次のようになります。
# 位置引数で指定する場合
grouped.transform(calc_max_diff_with_x, (2,))
コメント