
本文介绍如何使用 pandas 对 dataframe 按某一列(如产品类别)分组,并在每组中保留日期列最大(即最新)的那条记录,适用于清洗含重复id但需保留最新快照的业务数据。
本文介绍如何使用 pandas 对 dataframe 按某一列(如产品类别)分组,并在每组中保留日期列最大(即最新)的那条记录,适用于清洗含重复id但需保留最新快照的业务数据。
在实际数据分析中,我们常遇到这样的场景:同一标识(如商品名称、用户ID、设备编号)存在多条历史记录,而目标仅是提取每组中“时间最晚”的那一条——例如最新销售日期、最近一次登录、最新版本配置等。Pandas 提供了简洁高效的解决方案,无需复杂嵌套或手动遍历。
✅ 核心思路:groupby + idxmax() + loc
虽然 df.groupby('B')['A'].max() 能返回每组的最大日期值,但它只输出聚合结果(Series),无法直接获取原始行数据(含列 A、B、E 的完整记录)。因此,正确做法是:
- 将日期列转为 datetime 类型(关键前提!否则字符串比较会导致逻辑错误,如 '26/12/2023' > '01/01/2024' 可能为 True);
- 使用 groupby('B').A.idxmax() 获取每组中列 A 值最大的原始索引位置;
- 用 .loc[] 按这些索引筛选原 DataFrame,保留完整行。
? 完整可运行代码示例:
import pandas as pd
# 构造示例数据(注意:日期为字符串格式)
df = pd.DataFrame({
'A': ['26/12/2023', '26/12/2022', '26/12/2023', '26/12/2022',
'26/12/2023', '26/12/2022', '26/12/2023'],
'B': ['apple', 'apple', 'pear', 'orange', 'wildberry', 'wildberry', 'grapes'],
'E': ['7,9', '8,3', '28,6', '33,3', '24,7', '29,1', '17,1']
})
# ✅ 步骤1:将列 A 转为 datetime(自动识别 dayfirst=True 更安全)
df['A'] = pd.to_datetime(df['A'], dayfirst=True)
# ✅ 步骤2:按 B 分组,取每组中 A 最大值对应的原始索引
idx = df.groupby('B')['A'].idxmax()
# ✅ 步骤3:用 loc 提取完整行,并重置索引(可选)
result = df.loc[idx].sort_values('A').reset_index(drop=True)
# 若需恢复日期为原始字符串格式(如 '26/12/2023'),可选:
# result['A'] = result['A'].dt.strftime('%d/%m/%Y')
print(result)输出结果:
A B E 0 2023-12-26 apple 7,9 1 2023-12-26 pear 28,6 2 2022-12-26 orange 33,3 3 2023-12-26 wildberry 24,7 4 2023-12-26 grapes 17,1
? 提示:.idxmax() 返回的是原始索引(保留原顺序),而 .max() 仅返回值;若后续需按日期排序,建议使用 sort_values('A') 显式控制。
⚠️ 注意事项与常见误区
- 日期类型必须转换:若跳过 pd.to_datetime(),Pandas 会按字符串字典序比较(如 '26/12/2022' > '01/01/2024' 成立),导致结果完全错误;
- 处理空值(NaN):idxmax() 默认跳过 NaN;如需保留含 NaN 的组,添加参数 skipna=False 并配合异常处理;
- 多列同时取最新:若还需按另一列(如 C)二次排序,可用 sort_values(['B', 'A']).drop_duplicates('B', keep='last');
- 性能提示:对超大数据集,groupby().idxmax() 比 sort_values().drop_duplicates() 更高效,因无需全量排序。
✅ 总结
一句话掌握:先转日期 → 再 groupby().idxmax() 找索引 → 最后 loc[] 提取行。这是 Pandas 中处理“每组取最新记录”任务的标准范式,简洁、健壮且易于扩展。作为初学者,理解 idxmax 与 max 的区别,是跨越 loc 困境的关键一步。










