You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Pandas DataFrame每行提取首个非空值?求更优方案

高效实现DataFrame每行取首个非空列值的优化方案

嘿,你现在用嵌套np.where的方式虽然能实现需求,但当列数变多的时候会变得非常繁琐,而且代码可读性也差。这里给你几个更简洁高效的方案,适配你这种列名无固定规律的场景:

首先先还原你的示例DataFrame:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "ID": [1, 2, 3, 4],
    "c1": ["a", np.nan, np.nan, np.nan],
    "c2": ["b", "cc", np.nan, np.nan],
    "c3": [np.nan, "dd", "ee", np.nan],
    "c4": ["a", np.nan, "ff", "gg"],
})

方案1:用bfill实现一行搞定(最简洁)

这是我最推荐的方法,利用pandas的向后填充(bfill)功能,按行方向操作:

# 先筛选出需要检查的目标列(这里是c1到c4,你可以根据实际列名调整筛选逻辑)
target_cols = [col for col in df.columns if col.startswith('c')]
# 按行向后填充,取第一列就是每行的首个非空值
df['result'] = df[target_cols].bfill(axis=1).iloc[:, 0]

原理很简单:bfill(axis=1)会沿着每行从左到右,用后面的非空值填充前面的空值,所以填充后的第一列就正好是每行最左边的非空值。如果你的列是要从右往左找首个非空值,换成ffill(axis=1).iloc[:, -1]就行。

方案2:向量化的idxmax+lookup(高效适配大数据)

如果你的数据集很大,需要极致的效率,可以用这种纯向量化的操作,避免循环或apply:

non_null_mask = df[target_cols].notnull()
# 找到每行第一个非空值对应的列名
first_non_null_col = non_null_mask.idxmax(axis=1)
# 根据行索引和列名取值
df['result'] = df.lookup(df.index, first_non_null_col)

idxmax(axis=1)会返回每行第一个True值的列名,配合lookup就能快速取出对应的值,全程都是pandas的底层向量操作,性能拉满。

方案3:apply+first_valid_index(可读性拉满)

如果需要代码逻辑非常清晰,容易理解,可以用apply逐行处理:

def get_first_non_null(row):
    # 获取该行第一个非空值的列索引
    first_valid_col = row.first_valid_index()
    return row[first_valid_col] if first_valid_col is not None else np.nan

df['result'] = df[target_cols].apply(get_first_non_null, axis=1)

这个方法的好处是逻辑一目了然,但要注意:apply本质是逐行循环,在数据量很大的时候性能会比前两种向量化方法差一些,适合小数据集或者需要自定义额外逻辑的场景。

方案对比

方案优点缺点
bfill代码最简洁,性能优逻辑相对不直观(需要理解填充机制)
idxmax+lookup性能最优,纯向量操作代码比bfill稍长
apply可读性最强,易扩展大数据量下性能较差

内容的提问来源于stack exchange,提问作者qqqwww

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:29:17