如何在Pandas的groupby().agg()后选取首个非NaN值?
问题描述
我编写了如下代码:
df.groupby(["id", "year"], as_index=False).agg({"brand":"first", "color":"first"})
但数据中存在部分NaN值,我希望选取分组后各字段的首个非NaN值。
示例DataFrame如下:
| id | year | brand | color |
|---|---|---|---|
| 001 | 2010 | NaN | Blue |
| 001 | 2010 | Audi | NaN |
| 001 | 2010 | Audo | Blue |
| 001 | 2011 | Bmw | NaN |
| 001 | 2011 | NaN | NaN |
| 001 | 2012 | BMW | Green |
| 002 | 2010 | Tesla | White |
我需要按id和year的唯一组合分组(即df.groupby(["id", "year"])),获取每个字段的首个有效数据。数据集庞大且杂乱,存在大量缺失值和拼写错误(示例已模拟),拼写错误将另行处理,当前只需获取分组后的首个非空值。
期望输出:
| id | year | brand | color |
|---|---|---|---|
| 001 | 2010 | Audi | Blue |
| 001 | 2011 | Bmw | NaN |
| 001 | 2012 | BMW | Green |
| 002 | 2010 | Tesla | White |
解决方案
原代码中使用的"first"聚合规则会直接返回分组内列的第一个元素,哪怕该元素是NaN,无法满足需求。可以通过以下几种方式实现“取首个非NaN值”:
方法1:自定义聚合函数(可读性高)
定义一个函数筛选非空值后取第一个,全为空则返回NaN:
import pandas as pd import numpy as np def first_non_null(series): non_null_values = series.dropna() return non_null_values.iloc[0] if not non_null_values.empty else np.nan # 应用分组聚合 result = df.groupby(["id", "year"], as_index=False).agg({ "brand": first_non_null, "color": first_non_null })
方法2:Lambda表达式(简洁快速)
无需单独定义函数,直接在agg中使用lambda逻辑:
result = df.groupby(["id", "year"], as_index=False).agg({ "brand": lambda x: x.dropna().iloc[0] if not x.dropna().empty else np.nan, "color": lambda x: x.dropna().iloc[0] if not x.dropna().empty else np.nan })
方法3:利用first_valid_index(更高效)
借助Pandas Series的first_valid_index()方法定位首个非空值的位置,再取值:
result = df.groupby(["id", "year"], as_index=False).agg({ "brand": lambda x: x[x.first_valid_index()] if x.first_valid_index() is not None else np.nan, "color": lambda x: x[x.first_valid_index()] if x.first_valid_index() is not None else np.nan })
以上三种方法均可实现需求,其中方法1适合需要重复使用的场景,方法2、3适合快速实现。
内容的提问来源于stack exchange,提问作者Xtiaan
相关产品推荐
相关产品推荐

