Pandas GroupBy
GroupBy 是 Pandas 中非常強大的功能,可以將資料按照某個欄位分組,然後對每個群組進行聚合計算。這類似於 SQL 的 GROUP BY 語法。
GroupBy 的概念
GroupBy 操作包含三個步驟:
- Split(分割):根據某個條件將資料分成多個群組
- Apply(應用):對每個群組套用函數(如 sum、mean)
- Combine(合併):將結果合併成新的資料結構
基本用法
import pandas as pd
df = pd.DataFrame({
'city': ['Taipei', 'Tokyo', 'Taipei', 'Tokyo', 'Seoul'],
'year': [2023, 2023, 2024, 2024, 2024],
'sales': [100, 200, 150, 250, 180]
})
# 按 city 分組,計算 sales 總和
result = df.groupby('city')['sales'].sum()
print(result)
city
Seoul 180
Taipei 250
Tokyo 450
Name: sales, dtype: int64
多欄位分組
# 按 city 和 year 分組
result = df.groupby(['city', 'year'])['sales'].sum()
print(result)
city year
Seoul 2024 180
Taipei 2023 100
2024 150
Tokyo 2023 200
2024 250
Name: sales, dtype: int64
# 轉換成 DataFrame
result = df.groupby(['city', 'year'])['sales'].sum().reset_index()
常見聚合方法
grouped = df.groupby('city')['sales']
grouped.sum() # 總和
grouped.mean() # 平均
grouped.median() # 中位數
grouped.min() # 最小值
grouped.max() # 最大值
grouped.count() # 計數
grouped.std() # 標準差
grouped.var() # 變異數
grouped.first() # 第一個值
grouped.last() # 最後一個值
對多個欄位聚合
df = pd.DataFrame({
'city': ['Taipei', 'Tokyo', 'Taipei', 'Tokyo'],
'sales': [100, 200, 150, 250],
'profit': [10, 20, 15, 25]
})
# 對多個欄位計算
result = df.groupby('city')[['sales', 'profit']].sum()
print(result)
sales profit
city
Taipei 250 25
Tokyo 450 45
agg():多種聚合
使用 agg() 可以同時計算多種統計值:
# 對同一欄位計算多種統計
result = df.groupby('city')['sales'].agg(['sum', 'mean', 'max'])
print(result)
sum mean max
city
Taipei 250 125.0 150
Tokyo 450 225.0 250
對不同欄位用不同聚合
result = df.groupby('city').agg({
'sales': 'sum',
'profit': 'mean'
})
print(result)
自訂聚合名稱
result = df.groupby('city').agg(
total_sales=('sales', 'sum'),
avg_profit=('profit', 'mean'),
max_sales=('sales', 'max')
)
print(result)
自訂聚合函數
# 使用 lambda
result = df.groupby('city')['sales'].agg(lambda x: x.max() - x.min())
# 使用自訂函數
def range_func(x):
return x.max() - x.min()
result = df.groupby('city')['sales'].agg(range_func)
取得 GroupBy 物件
grouped = df.groupby('city')
# 查看群組
print(grouped.groups)
# {'Taipei': [0, 2], 'Tokyo': [1, 3]}
# 取得特定群組
print(grouped.get_group('Taipei'))
# 遍歷群組
for name, group in grouped:
print(f'Group: {name}')
print(group)
print()
群組數量
# 每個群組的筆數
result = df.groupby('city').size()
# 或使用 count(會排除 NaN)
result = df.groupby('city').count()
# 群組的數量
print(df.groupby('city').ngroups) # 2
transform()
transform() 會回傳與原始資料相同長度的結果:
df = pd.DataFrame({
'city': ['Taipei', 'Tokyo', 'Taipei', 'Tokyo'],
'sales': [100, 200, 150, 250]
})
# 計算每個城市的平均,並對應回每一列
df['city_avg'] = df.groupby('city')['sales'].transform('mean')
print(df)
city sales city_avg
0 Taipei 100 125.0
1 Tokyo 200 225.0
2 Taipei 150 125.0
3 Tokyo 250 225.0
transform 的應用
# 計算與群組平均的差距
df['diff_from_avg'] = df['sales'] - df.groupby('city')['sales'].transform('mean')
# 群組內的百分比
df['pct_of_city'] = df['sales'] / df.groupby('city')['sales'].transform('sum')
# 群組內的排名
df['rank_in_city'] = df.groupby('city')['sales'].transform('rank')
filter()
根據群組條件過濾:
df = pd.DataFrame({
'city': ['Taipei', 'Tokyo', 'Seoul', 'Tokyo', 'Taipei'],
'sales': [100, 200, 50, 150, 120]
})
# 只保留 sales 總和大於 200 的城市
result = df.groupby('city').filter(lambda x: x['sales'].sum() > 200)
print(result)
常見應用
計算佔比
df = pd.DataFrame({
'category': ['A', 'A', 'B', 'B'],
'value': [10, 20, 30, 40]
})
# 計算每個類別的佔比
df['category_total'] = df.groupby('category')['value'].transform('sum')
df['pct'] = df['value'] / df['category_total']
填補缺失值
# 用群組的平均值填補缺失
df['value'] = df.groupby('category')['value'].transform(lambda x: x.fillna(x.mean()))
取每組的前 N 名
df = pd.DataFrame({
'city': ['Taipei', 'Taipei', 'Taipei', 'Tokyo', 'Tokyo'],
'sales': [100, 200, 150, 300, 250]
})
# 每個城市 sales 最高的 2 筆
top2 = df.groupby('city').apply(lambda x: x.nlargest(2, 'sales')).reset_index(drop=True)