在数据分析工作中,按分组保留最新日期对应的数据行是非常常见的需求。比如我们需要从用户订单表中,获取每个用户最近的一次下单记录,或者从设备日志表中,提取每台设备最新的运行状态数据。这类需求的核心逻辑是先按照指定的分组字段对数据进行分组,然后在每个分组内找到日期最大的那一行数据并保留。

方法一:使用sort_values结合groupby的head方法
这种方法的思路是先按照日期字段降序排序,让每个分组内最新的日期排在最前面,然后按分组字段分组后取每个分组的第一行数据。具体实现步骤如下:
- 首先将日期字段转换为datetime类型,避免排序时出现字符串排序的错误
- 按照日期字段降序排序整个数据集
- 按照目标分组字段进行分组,取每个分组的第一行数据
下面是完整的代码示例:
import pandas as pd
# 构造示例数据
data = {
'user_id': [1, 1, 2, 2, 3, 3],
'order_date': ['2024-03-01', '2024-03-05', '2024-02-20', '2024-03-10', '2024-01-15', '2024-03-08'],
'order_amount': [100, 200, 150, 300, 80, 120]
}
df = pd.DataFrame(data)
# 将order_date转换为datetime类型
df['order_date'] = pd.to_datetime(df['order_date'])
# 按日期降序排序,再按user_id分组取第一行
result = df.sort_values('order_date', ascending=False).groupby('user_id').head(1)
print(result)
这种方法的优点是逻辑直观,容易理解,适合数据量不大的场景。但如果数据量很大,先对整个数据集排序会消耗较多内存和时间。
方法二:使用groupby结合idxmax方法
idxmax方法可以返回每个分组中最大值对应的索引,我们可以利用这个特性先找到每个分组中最新日期的索引,再通过索引提取对应的数据行。具体实现如下:
import pandas as pd
# 构造示例数据
data = {
'user_id': [1, 1, 2, 2, 3, 3],
'order_date': ['2024-03-01', '2024-03-05', '2024-02-20', '2024-03-10', '2024-01-15', '2024-03-08'],
'order_amount': [100, 200, 150, 300, 80, 120]
}
df = pd.DataFrame(data)
# 转换日期类型
df['order_date'] = pd.to_datetime(df['order_date'])
# 获取每个user_id对应的最新日期的索引
idx = df.groupby('user_id')['order_date'].idxmax()
# 通过索引提取数据
result = df.loc[idx]
print(result)
这种方法的效率比第一种更高,因为它不需要对整个数据集排序,只需要在分组内计算最大值索引,适合处理大规模数据。
方法三:使用drop_duplicates方法
如果数据已经按照日期降序排序,或者我们只需要保留每个分组中第一次出现的记录,也可以使用drop_duplicates方法,指定分组字段为去重依据,保留第一条记录即可。示例代码如下:
import pandas as pd
# 构造示例数据
data = {
'user_id': [1, 1, 2, 2, 3, 3],
'order_date': ['2024-03-05', '2024-03-01', '2024-03-10', '2024-02-20', '2024-03-08', '2024-01-15'],
'order_amount': [200, 100, 300, 150, 120, 80]
}
df = pd.DataFrame(data)
# 转换日期类型
df['order_date'] = pd.to_datetime(df['order_date'])
# 先按日期降序排序,再去重保留每个user_id的第一条记录
result = df.sort_values('order_date', ascending=False).drop_duplicates(subset='user_id', keep='first')
print(result)
这种方法的逻辑也比较简单,适合已经对数据做过排序的场景,代码简洁度很高。
不同方法的对比
我们可以通过下面的表格对比三种方法的特性:
| 方法 | 实现逻辑 | 适用场景 | 性能表现 |
|---|---|---|---|
| sort_values+groupby head | 全局排序后取分组首行 | 小数据量,逻辑直观优先 | 较差,大数据量排序开销大 |
| groupby+idxmax | 分组内找最大日期索引 | 大数据量,性能优先 | 较好,无需全局排序 |
| drop_duplicates | 排序后去重保留首行 | 已排序数据,代码简洁优先 | 中等,依赖排序步骤 |
注意事项
在实际使用中需要注意以下几点:
- 一定要先将日期字段转换为
datetime类型,否则字符串排序可能会出现2024-1-1排在2024-12-1前面的情况 - 如果日期字段存在缺失值,需要先处理缺失值,否则
idxmax方法可能会返回错误结果 - 如果分组字段有多个,比如需要按用户和地区分组保留最新记录,只需要把
groupby的参数改为列表即可,例如groupby(['user_id', 'region'])
如果数据中存在同一个分组内多条记录日期完全相同的情况,上述方法默认会保留其中一条,如果需要保留所有日期最新的重复记录,可以在分组后使用apply方法结合条件筛选实现。