Pandas & NumPy Academy · レッスン

GroupByのTransformとFilter

transform()でグループ単位の統計量を列として追加し、filter()で条件を満たすグループだけを残します。

レッスン 4/413 ステップ

「GroupByのTransformとFilter」はCoddyKit上の無料Pandas & NumPy Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPandas & NumPy Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

集約の先へ

agg()を使いこなしたら、次のような疑問が自然に生まれます。元のすべての行を残したまま、グループ単位の統計量を追加するにはどうすればよいでしょうか。また、条件を満たすグループだけを残すにはどうすればよいでしょうか。ここでtransform()とfilter()が役立ちます。この2つのメソッドにより、GroupByは単純な集計を超えて、特徴量エンジニアリングやデータ選択にも活用できます。

transform()を理解する

transform()は各グループに関数を適用し、元のDataFrameと同じ形状の結果、つまり元の各行に1つずつ対応する値を返します。グループごとの結果は、そのグループに属する各行へブロードキャストして戻されます。そのため、行数を変えずにグループ単位の統計量を新しい列として追加するのに適しています。

import pandas as pd

df = pd.DataFrame({
    'dept':   ['Eng', 'HR', 'Eng', 'HR', 'Eng'],
    'salary': [90000, 60000, 95000, 62000, 88000]
})

# Add a column with each employee's department average salary
df['dept_avg'] = df.groupby('dept')['salary'].transform('mean')
print(df)
#    dept  salary    dept_avg
# 0   Eng   90000  91000.000
# 1    HR   60000  61000.000
# 2   Eng   95000  91000.000
# 3    HR   62000  61000.000
# 4   Eng   88000  91000.000

transform()の一般的な用途

transform()の一般的な用途には、値を正規化するためのグループ平均の追加、全体に占める各行の割合を計算するためのグループ合計の追加、グループ内で各要素を比較するためのグループ順位の追加などがあります。いずれも元の形状とインデックスを維持するため、元の列と組み合わせてすぐに利用できます。

# Percentage of each employee's salary within their department total
df['pct_of_dept'] = (
    df['salary'] / df.groupby('dept')['salary'].transform('sum') * 100
).round(1)
print(df[['dept', 'salary', 'pct_of_dept']])
# dept  salary  pct_of_dept
# Eng   90000        33.1
# HR    60000        49.2
# Eng   95000        34.9

transform()で独自関数を使う

agg()と同様に、transform()でも文字列の名前だけでなく任意の呼び出し可能オブジェクトを指定できます。関数は1つのグループの値を含むSeriesを受け取り、同じ長さのSeries、またはスカラーを返す必要があります。スカラーを返すと、その値がブロードキャストされます。スカラーを返す方法が最も一般的で、異なる長さのSeriesを返すとエラーになります。

# Z-score normalisation within each department
def zscore(s):
    return (s - s.mean()) / s.std()

df['salary_zscore'] = df.groupby('dept')['salary'].transform(zscore)
print(df[['dept', 'salary', 'salary_zscore']].round(2))
# dept  salary  salary_zscore
# Eng   90000          -0.51
# HR    60000          -0.71
# Eng   95000           1.03

agg()とtransform()の比較

重要な違いは、agg()が行数を減らす(グループごとに1行)のに対し、transform()は行数を維持する(元の各行に1行)ことです。集計表を作成するにはagg()を使います。元のDataFrameにグループ単位の情報を新しい特徴量列として追加するにはtransform()を使います。

g = df.groupby('dept')['salary']

# agg: 2 rows (one per unique dept)
print(g.agg('mean'))
# dept
# Eng    91000.0
# HR     61000.0

# transform: 5 rows (one per original row)
print(g.transform('mean'))
# 0    91000.0
# 1    61000.0
# 2    91000.0
# 3    61000.0
# 4    91000.0

filter()を理解する

filter()は、真偽値を返す関数に基づいてグループ全体を残すか破棄します。1つのグループのサブDataFrameを受け取り、True(グループを残す)またはFalse(グループを削除する)を返す関数を渡します。結果は元のDataFrameの部分集合となり、テストに合格したグループの行だけが含まれます。

df2 = pd.DataFrame({
    'dept':   ['Eng', 'HR', 'Eng', 'HR', 'Eng', 'Legal'],
    'salary': [90000, 60000, 95000, 62000, 88000, 70000]
})

# Keep only departments with at least 2 employees
big_depts = df2.groupby('dept').filter(lambda g: len(g) >= 2)
print(big_depts)
# dept, salary rows: Eng(3) and HR(2) remain; Legal(1) dropped

グループの集約値で絞り込む

filter()の非常に一般的な用途は、集約値がしきい値を満たすグループを残すことです。たとえば、平均給与が目標値を超える部署だけを残したり、総売上が最低基準を上回る商品カテゴリだけを残したりできます。これにより、後続の分析を行う前に取引量の少ないグループを取り除けます。

# Keep only departments where average salary > 80000
high_paying = df2.groupby('dept').filter(
    lambda g: g['salary'].mean() > 80000
)
print(high_paying)
#    dept  salary
# 0   Eng   90000
# 2   Eng   95000
# 4   Eng   88000
# (HR avg is 61000, filtered out)

transform()とfilter()の組み合わせ

transform()とfilter()は順番に適用して、データを拡張してから絞り込むことができます。まずfilter()で不要なグループを取り除き、次に絞り込み後の結果へtransform()を適用してグループ単位の特徴量を追加します。この組み合わせにより、モデリングやレポートにすぐ使える、整理された情報量の多い部分集合を作成できます。

# Step 1: keep only large departments
filtered = df2.groupby('dept').filter(lambda g: len(g) >= 2)

# Step 2: add group mean salary to the filtered result
filtered = filtered.copy()
filtered['dept_avg'] = filtered.groupby('dept')['salary'].transform('mean')
print(filtered)

グループ内の前方埋めにtransform()を使う

transform()は数値以外の関数と組み合わせても便利です。よく使われるパターンは、グローバルな埋め値を使うのではなく、グループ内で前方埋めやグループの中央値によって欠損値を埋める方法です。グループのSeriesに対してfillna()を呼び出すlambdaを渡すと、結果は元のDataFrameと同じインデックスを持ちます。

import numpy as np

df3 = pd.DataFrame({
    'dept':   ['Eng', 'Eng', 'HR', 'HR', 'Eng'],
    'salary': [90000, np.nan, 60000, np.nan, 88000]
})

# Fill NaN with the group mean
df3['salary_filled'] = df3.groupby('dept')['salary'].transform(
    lambda s: s.fillna(s.mean())
)
print(df3)

実践パターン: グループ内での相対的な位置

ビジネスで役立つ強力な用途の1つは、各行のグループ内での相対的な位置を計算することです。transform()と算術演算を組み合わせると、グループ平均からの給与の差(偏差)、グループ合計に占める給与の割合、その従業員の給与がグループ中央値を上回っているかどうかを示す真偽値フラグなどの列を追加できます。これらの特徴量は、ダッシュボードや機械学習モデルで非常に役立ちます。

df['dept_total'] = df.groupby('dept')['salary'].transform('sum')
df['pct_of_total'] = (df['salary'] / df['dept_total'] * 100).round(1)
df['above_avg'] = df['salary'] > df.groupby('dept')['salary'].transform('mean')
print(df[['dept', 'salary', 'pct_of_total', 'above_avg']])

パフォーマンスに関する考慮事項

組み込みの文字列関数を使うtransform()とfilter()は、最適化されたコードパスを使用するため高速です。ただし、lambdaや独自のPython関数を渡すと、Pandasはグループごとにその関数を1回呼び出す必要があり、グループ数が多いデータでは遅くなる可能性があります。大規模なデータセットで最大限のパフォーマンスを得るには、独自のロジックを組み込みの文字列関数で表現できないか確認してください。

# Slower: custom lambda (called once per group)
df['dept_mean_slow'] = df.groupby('dept')['salary'].transform(lambda s: s.mean())

# Faster: built-in string shortcut (vectorised C path)
df['dept_mean_fast'] = df.groupby('dept')['salary'].transform('mean')

# Both give identical results, but the built-in is significantly faster

理解度チェック

このレッスンで学んだGroupByのtransform()とfilter()の理解度を確認しましょう。

レッスンのまとめ

このレッスンでは、transform()がグループの統計量を元の行数にブロードキャストして戻すため、グループ単位の特徴量の追加に適していること、filter()が真偽条件に基づいてグループ全体を残すか削除すること、そして両方を組み合わせることで後続の分析に適した、情報量の多い絞り込み済みデータセットを作成できることを学びました。次は、pd.concatを使ってDataFrameを結合する方法を学びます。

無料で開始

AI チューターと学ぶ Python — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
30
レッスン
120

よくある質問

「GroupByのTransformとFilter」レッスンは無料ですか?

はい。「GroupByのTransformとFilter」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Pandas & NumPy Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

「GroupByのTransformとFilter」で何を学びますか?

transform()でグループ単位の統計量を列として追加し、filter()で条件を満たすグループだけを残します。 ブラウザで直接実行するハンズオンコードでPandas & NumPy Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Pandas & NumPy Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPandas & NumPy Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「GroupByのTransformとFilter」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPandas & NumPy Academyレッスンでコードを書いて実行できますか?

はい。すべてのPandas & NumPy Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Split-Apply-Combineパターン
  2. 単一キーと複数キーによるGroupBy
  3. agg()メソッド
  4. GroupByのTransformとFilter
← Pandas & NumPy Academyに戻る