如何在Pandas中结合groupby与sort实现按name取最新非空数据
用Pandas实现按分组取最新非空数据(对应R的group_by逻辑)
完全懂你的困惑!R里那套group_by()+arrange()+slice()的组合用惯了,刚转Python确实得适应下pandas的逻辑。我给你拆解下怎么实现,保证和你R里的思路对齐~
核心逻辑和你在R里的思路完全一致:
- 按
name分组 - 每个组内按
timestamp降序排序(最新数据排最前) - 对每一列,取该组内第一个非空的有效值(因为最新时间在前,第一个非空就是最新的有效数据)
先构造示例数据(方便你测试)
先模拟一个和你需求匹配的数据集,包含空值和多组数据:
import pandas as pd import numpy as np df = pd.DataFrame({ 'name': ['A', 'A', 'A', 'B', 'B'], 'timestamp': ['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-01', '2023-01-02'], 'col1': [1, np.nan, 3, np.nan, 5], 'col2': [np.nan, 'x', 'y', 'z', np.nan], 'col3': ['foo', 'bar', np.nan, np.nan, 'baz'] }) # 一定要把timestamp转成datetime类型,不然排序会出错! df['timestamp'] = pd.to_datetime(df['timestamp'])
方法一:groupby() + apply()(最贴近R的思维,易理解)
这个方法完全复刻你在R里的操作逻辑,写起来直观,适合小数据集:
def get_latest_non_null(group): # 第一步:组内按timestamp降序排序,最新数据放最前面 sorted_group = group.sort_values('timestamp', ascending=False) # 第二步:对每一列,去掉空值后取第一个;如果整列都是空就留NaN return sorted_group.apply(lambda x: x.dropna().iloc[0] if not x.dropna().empty else np.nan) # 把函数应用到每个分组,最后用reset_index把name变回普通列 result = df.groupby('name').apply(get_latest_non_null).reset_index()
运行后你会得到每个name的最新非空数据:
| name | timestamp | col1 | col2 | col3 |
|---|---|---|---|---|
| A | 2023-01-03 | 3 | y | bar |
| B | 2023-01-02 | 5 | z | baz |
方法二:先排序再groupby()+agg()(更高效,适合大数据集)
如果你的数据量很大,apply()的速度会变慢,这时候可以先整体排序,再用agg()批量处理,速度快很多:
# 第一步:先按name升序、timestamp降序整体排序,确保每个组的最新数据在组的最顶端 df_sorted = df.sort_values(['name', 'timestamp'], ascending=[True, False]) # 第二步:按name分组,对每列取第一个非空值 result = df_sorted.groupby('name').agg( lambda x: x.dropna().iloc[0] if not x.dropna().empty else np.nan ).reset_index()
这个方法和方法一的结果完全一致,但性能更优,处理十万级以上数据时差距会很明显。
几个关键注意点
- timestamp必须转成datetime类型:如果是字符串格式,排序可能会出现逻辑错误(比如"2023-10-01"会排在"2023-09-01"前面,但字符串排序可能出问题)。
- 空值处理:如果某个组的某一列全是空值,结果会保留
NaN,符合你的需求。 - 自定义逻辑:如果需要调整规则(比如取最新的3条非空值的平均值),只需要修改
apply或agg里的lambda函数即可。
内容的提问来源于stack exchange,提问作者Rilcon42
相关产品推荐
相关产品推荐

