Pandas & NumPy Academy · 课时

GroupBy 变换与筛选

使用 transform() 将组级统计量添加回列中,并使用 filter() 仅保留满足条件的组。

第 4 / 4 课13 个步骤

GroupBy 变换与筛选 是 CoddyKit 上的免费 Pandas & NumPy Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Pandas & NumPy Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Pandas & NumPy Academy 课程共包含 4 节课。

超越聚合

掌握 agg() 后,自然会产生这样的问题:如果您希望保留所有原始行,同时为其添加组级统计数据,该怎么办?或者,如果您只想保留满足某个条件的组呢?这正是 transform() 和 filter() 的用武之地。这两种方法让 GroupBy 超越简单的汇总,应用于特征工程和数据筛选。

理解 transform()

transform() 会对每个组应用一个函数,并返回与原始 DataFrame 形状相同的结果——原始每一行对应一个值。组级结果会广播回属于该组的每一行。这使它非常适合在不改变行数的情况下,将组级统计数据添加为新列。

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() 除了接受字符串名称外,也接受任意可调用对象。该函数会接收一个组的 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() 会减少行数(每组一行),而 transform() 会保留行数(每个原始行一行)。使用 agg() 构建摘要表;使用 transform() 将组级信息作为新的特征列添加到原始 DataFrame 中。

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() 根据布尔函数保留或丢弃整个组。您需要传入一个函数,该函数接收某个组的子 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() 对非数值函数同样有用。一种常见模式是在组内部使用该组的向前填充值或中位数填充缺失值,这比全局填充效果更好。传入一个 lambda,在组 Series 上调用 fillna();结果会与原始 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)

实践模式:相对表现

一个强大的业务应用场景是计算每一行相对于所属组的表现。通过将 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 必须为每个组调用一次该函数;当组数量很多时,这可能会很慢。对于大型数据集,为了获得最佳性能,请检查您的自定义逻辑是否可以改写为内置字符串函数。

# 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 — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
30
课程
120

常见问题解答

「GroupBy 变换与筛选」课时是免费的吗?

是的 — 「GroupBy 变换与筛选」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Pandas & NumPy Academy 课程的其余内容,请升级到 CoddyKit PRO。 Pandas & NumPy Academy 课程共包含 4 节课。

「GroupBy 变换与筛选」这节课中我会学到什么?

使用 transform() 将组级统计量添加回列中,并使用 filter() 仅保留满足条件的组。 你通过在浏览器中直接运行的动手代码来练习 Pandas & NumPy Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Pandas & NumPy Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Pandas & NumPy Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「GroupBy 变换与筛选」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Pandas & NumPy Academy 课中编写并运行代码吗?

能。每节 Pandas & NumPy Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 拆分—应用—合并模式
  2. 使用单个和多个键进行 GroupBy
  3. agg() 方法
  4. GroupBy 变换与筛选
← 返回 Pandas & NumPy Academy