
本文详解如何在 Pandas DataFrame 中基于 article_id 去重,并严格保留每个 article_id 时间上最新的那条记录——关键在于先按 created_at 升序排序再调用 drop_duplicates(keep='last')。
本文详解如何在 pandas dataframe 中基于 `article_id` 去重,并**严格保留每个 article_id 时间上最新的那条记录**——关键在于先按 `created_at` 升序排序再调用 `drop_duplicates(keep='last')`。
在日志类数据分析中(如内容审核、用户行为追踪),常遇到同一实体(如 article_id)被多次操作并记录多条时间戳不同的记录。此时,简单使用 drop_duplicates(subset=['article_id'], keep='last') 往往无法得到预期结果——因为 keep='last' 是按 原始行序 保留最后出现的行,而非按时间先后保留最新的一条。若数据未按时间排序,该操作等价于随机采样,极易导致逻辑错误(如示例中误删大量早期用户操作,仅剩第29行起的片段)。
✅ 正确做法分三步:
- 将 created_at 列解析为 datetime 类型(确保可排序);
- 按时间升序排序(使最新记录排在每组末尾);
- 对 article_id 去重,保留每组最后一行(即时间上最新的记录)。
以下是完整、健壮的实现代码:
import pandas as pd
# 读取数据(假设已加载为 df)
# df = pd.read_csv("log.csv")
# 步骤1:转换 created_at 为 datetime(自动处理 ISO 格式如 '2023-12-05T20:04:45.088Z')
df["created_at"] = pd.to_datetime(df["created_at"])
# 步骤2:按时间升序排序(关键!确保最新记录在每组末尾)
df = df.sort_values("created_at", ascending=True)
# 步骤3:按 article_id 去重,保留每组最后一条(即最新时间戳的记录)
df_latest = df.drop_duplicates(subset=["article_id"], keep="last").reset_index(drop=True)
# (可选)格式化时间输出为 YYYY-MM-DD(不推荐用于后续计算,仅展示用)
# df_latest["created_at"] = df_latest["created_at"].dt.strftime("%Y-%m-%d")⚠️ 注意事项:
立即学习“Python免费学习笔记(深入)”;
- 切勿跳过 sort_values:drop_duplicates 的 keep='last' 依赖物理行序,而非逻辑时间顺序;
- 时间列必须是 datetime64 类型:若为字符串,sort_values 将按字典序排序(如 "2023-12-05" < "2023-12-1"),导致错误;
- 避免链式赋值风险:建议显式赋值给新变量(如 df_latest)或使用 inplace=True(不推荐);
- 若需保留原始索引供溯源,可在排序前保存:df = df.sort_values("created_at").reset_index(drop=False),之后通过 index 列回溯原始位置。
? 进阶提示:
如需同时保留「每个 article_id 最新操作的时间」和「对应的操作详情」(如 value, user_email),上述方法已天然满足;若还需统计每篇文章的操作次数或时间跨度,可额外使用 groupby("article_id").agg(...) 实现。
最终,该方案可稳定处理万级日志数据(如原文中 17,349 行),精准压缩至唯一文章数(约 7,000+ 条),且每条均代表该文章的最终决策状态——这才是业务上真正需要的“权威快照”。










