最直接方法是 df.count(axis=1),它统计每行非空值数量,跳过 NaN、None、pd.NaT,返回整数型 Series;但不识别字符串'nan'、空字符串''或0,需清洗后再用。

用 count(axis=1) 直接统计每行非空值数量
这是最直接的方法:df.count(axis=1) 会跳过 NaN、None 和 pd.NaT,对每行有效值计数,返回一个 Series,索引与原 DataFrame 行索引一致。
注意它只识别 pandas 认为的“缺失值”,不处理字符串 'nan'、空字符串 '' 或数值 0 —— 这些全算作有效值。
- 如果列里混有字符串
'NULL'或'missing',得先用replace()或布尔掩码清洗 -
axis=1是关键,写成axis=0就变成按列统计了 - 结果是整数型
Series,可直接赋给新列:df['non_null_count'] = df.count(axis=1)
想把空字符串 '' 也当作“空”来统计?用 apply + 自定义逻辑
当业务上认为空字符串、仅空白符(如 ' ')、或特定占位符(如 'N/A')也该被排除时,count() 不够用,得手动定义“什么是空”。
推荐用 df.apply(..., axis=1) 配合 pd.isna() 和额外判断:
创建 Flask + Vue.js 全栈项目,采用单端口架构、uv 依赖管理、结构化日志和生产级前端设计。当用户要求以下操作时触发:创建新的 Flask+Vue 项目、搭建 Web 应用、构建全栈 Python+Vue 应用、初始化 Flask Web 项目、设置单端口 Flask 前后端项目。触发词:新建项目、创建项目、搭建项目、Flask项目、Vue项目。
立即学习“Python免费学习笔记(深入)”;
df['non_null_count'] = df.apply(
lambda row: sum(
not pd.isna(val) and
not (isinstance(val, str) and val.strip() == '')
for val in row
),
axis=1
)-
val.strip() == ''覆盖了纯空格、制表符等;但注意对非字符串类型调用.strip()会报错,所以必须加isinstance(val, str)判断 - 如果还要排除
'N/A'、'null'等,继续在条件里用val.strip().lower() not in ['n/a', 'null', 'none'] - 性能比向量化操作慢,万行以内没问题,超十万行建议先用
df.replace()统一转为NaN再走count()
为什么不用 notna().sum(axis=1)?它其实和 count(axis=1) 等价
df.notna() 返回布尔 DataFrame,.sum(axis=1) 对每行 True 求和 —— 数值上确实和 count(axis=1) 一致。但二者底层行为略有差异:
-
count()是专为缺失值设计的聚合方法,语义更清晰 -
notna().sum()在遇到扩展类型(如stringdtype 或 nullable integer)时更稳定;而旧版 pandas 中count()对某些扩展类型支持不一致 - 如果你已用
notna()做其他逻辑(比如同时生成掩码),复用它更连贯
常见错误:用 len(df.columns) 减去 isna().sum(axis=1) 得到非空数?小心类型不匹配
看起来合理:len(df.columns) - df.isna().sum(axis=1),但容易出问题:
- 如果某列是
category类型且含未使用的类别,isna()可能误判(尤其 pandas - 当 DataFrame 有重复列名时,
len(df.columns)是总数,但isna().sum()按实际列计算,结果错位 - 更隐蔽的是:若某列 dtype 为
object且含混合类型(如部分list、部分int),isna()可能返回False即使值是None—— 这时必须用pd.isna()才可靠
真正安全的做法始终是基于值本身判断,而不是靠“总数减缺失数”这种间接推导。

















