
本文介绍在 pandas 中实现“先按某一列(如 `y`)分组,再依据每组中指定列(如 `x`)的最后一个值作为新分组键”的高效方法,适用于未知末行值场景,避免硬编码过滤。
在数据分析中,常需对 DataFrame 进行嵌套式逻辑分组:例如,先按业务维度(如 'y' 列)划分自然组,再根据每组内某关键字段(如 'x')的最终状态(即最后一行的值)进行二次聚合或拆分。该需求不同于普通 groupby,核心难点在于:末行值未知且需动态提取,无法预先枚举。
以下以实际数据为例,展示专业、可扩展的解决方案:
import pandas as pd
df = pd.DataFrame({
'x': ['a', 'b', 'c', 'c', 'e', 'f', 'd', 'a', 'b', 'c', 'c', 'e', 'f', 'd'],
'y': ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'f', 'f', 'f', 'f', 'g', 'g', 'g']
})目标是将 df 按 'y' 分组后,提取每组 'x' 的最后一个值(即该组“终态”),再以此终态值为键重新分组——最终得到两组:终态为 'c' 的组(含 y='a' 和 y='f')、终态为 'd' 的组(含 y='b' 和 y='g')。
✅ 推荐方案:transform('last') 构建动态分组键
最清晰、可读性最强的方式是使用 groupby().transform('last') 提取每组 'x' 的末值,并生成与原 DataFrame 等长的分组映射序列:
# 步骤1:按 'y' 分组,提取每组 'x' 的最后一个值,广播至全组
last_x_per_y = df.groupby('y')['x'].transform('last')
# 步骤2:用该序列作为新分组键,执行二次分组
for last_val, group in df.groupby(last_x_per_y):
print(f"\n【终态为 '{last_val}' 的组】")
print(group)输出结果完全匹配预期:
【终态为 'c' 的组】
x y
0 a a
1 b a
2 c a
3 c a
7 a f
8 b f
9 c f
10 c f
【终态为 'd' 的组】
x y
4 e b
5 f b
6 d b
11 e g
12 f g
13 d g? 原理说明:transform('last') 会为每个 'y' 组计算 'x' 的末值(如 y='a' 组末行为 'c'),并将该值填充到该组所有行对应位置,从而生成长度为 len(df) 的 Series last_x_per_y,可直接用于 groupby()。
⚡ 高性能替代方案(适合大数据)
若 'y' 列天然构成连续唯一块(即相同 'y' 值总是相邻出现),可绕过 groupby,用向量化操作加速:
# 方案A:通过 drop_duplicates + map(推荐,语义清晰)
mapper = df.drop_duplicates('y', keep='last').set_index('y')['x']
last_x_fast = df['y'].map(mapper)
# 方案B:利用 duplicated + bfill(内存友好)
last_x_fast = df['x'].mask(df['y'].duplicated(keep='last')).bfill()
# 后续分组逻辑一致
for k, g in df.groupby(last_x_fast):
print(f"\nGroup by last x = '{k}':\n{g}")? 通用化:支持任意行选择逻辑
若需取“倒数第二行”、“最大值对应行”等非固定位置,可将 transform 与 lambda 结合:
# 取每组 'x' 的倒数第二行(需确保组长度 ≥ 2)
last_x_custom = df.groupby('y')['x'].transform(lambda s: s.iloc[-2] if len(s) >= 2 else s.iloc[0])
# 或取每组 'x' 中字典序最大值
last_x_max = df.groupby('y')['x'].transform('max')? 输出结构化结果
如需将分组结果存为字典(键为终态值,值为子 DataFrame),一行即可完成:
grouped_dict = dict(list(df.groupby(last_x_per_y))) # grouped_dict['c'] → 包含所有终态为 'c' 的行 # grouped_dict['d'] → 包含所有终态为 'd' 的行
⚠️ 注意事项
- y 列连续性假设:文中 drop_duplicates(..., keep='last') 方案要求 'y' 相同值在原始数据中物理连续;若 'y' 呈离散分布(如 ['a','b','a','b'] 应视为4个独立组),请改用 df.groupby(df['y'].ne(df['y'].shift()).cumsum()) 构建自然段落组。
- 空组处理:transform 在空组上返回 NaN,建议提前检查 df['y'].nunique() 是否为 0。
- 性能对比:transform('last') 在中等数据量下简洁可靠;超大数据(千万级+)优先选用 map + drop_duplicates 方案,减少分组开销。
掌握这一模式,即可灵活应对“按维度分组 → 提取组内特征 → 依特征再分组”的典型分析链路,大幅提升 Pandas 流水线表达力与鲁棒性。







