pd.NA在二元运算中具有“传染性”,只要参与运算的一方是pd.NA,结果即为pd.NA;而np.nan在算术和比较中返回np.nan,且np.nan==np.nan恒为False,pd.NA==pd.NA返回pd.NA。
pd.NA 和 np.nan 混用时,运算结果会变成 pd.NA
直接说结论:
在几乎所有二元运算(
、
、
、
、
、
等)中都会“传染”——只要参与运算的一方是
,结果就返回
,而不是
。而
虽然也传播,但只在算术和比较中返回
,且
永远为
,而
返回的是
(三值逻辑),不是
也不是
。
;
(但注意:布尔上下文中
会被当作
,引发
隐式转换
错误)
;
;
(这是关键差异:后者返回布尔值,前者返回缺失)
混合列运算(如
)会自动将结果转为
(nullable float),
保持为
,
会被统一转为
(前提是 pandas ≥ 1.2.0)
为什么不能直接用 isna() 判断混合缺失?
对
和
都返回
,看起来没问题——但陷阱在于:当列类型是
时,
可能误判
或字符串
;更严重的是,
或
在含混合缺失的 nullable 列(如
)中行为一致,但在普通
列里遇到
会直接报错:
。
✅ 安全做法:先统一缺失表示再操作 —— 用
(pandas ≥ 2.0)或显式
转为 nullable 类型
❌ 危险操作:对
列调用
—— 若含
,会静默转成
;但若含
,可能抛
⚠️ 注意:
永远返回全
;而
返回全
—— 别用
去筛缺失值
如何安全地做缺失值填充或条件过滤?
核心原则:别让
和
共存于同一列;一旦混合,优先升格为 nullable 类型并用
作为唯一缺失标记。
填充前先归一化:
布尔索引必须用
或
转成纯布尔才能用于
:
插值(
)不支持
—— 它只处理
;所以含
的列需先
再插值(但会丢失整数类型)
聚合函数
如
、
对
默认跳过(类似
),但
/
在全
时返回
,而全
时返回
升级与兼容性:哪些版本能稳住?
自 pandas 1.0.0 引入,但直到 1.5.x 才稳定三值逻辑;2.0+ 默认启用
,混合缺失行为更可预测。如果你还在用 1.3.x 或更低版本,
在某些聚合或 join 中可能意外退化为
或报
。
✅ 推荐最低版本:
(修复了
在
中的传播 bug)
❌ 1.0–1.4.x:
在
中可能导致右表 key 列类型崩成
? 检查当前行为:
应返回
;若返回
,说明底层仍走旧逻辑
真正麻烦的不是
本身,而是它和
在同一 DataFrame 里共存时,你根本不知道下一次
或
会触发哪条隐式转换路径。统一、尽早、强制转换,比事后 debug 快十倍。
pd.NA&|==!=+>pd.NApd.NAnp.nannp.nannp.nannp.nan == np.nanFalsepd.NA == pd.NApd.NATrueFalseTrue & pd.NA → pd.NATrue & np.nan → np.nannp.nanTrue1 + pd.NA → pd.NA1 + np.nan → np.nanpd.NA == "x" → pd.NAnp.nan == "x" → Falsedf["a"].astype("Int64") + df["b"].astype("float64")Float64pd.NApd.NAnp.nanpd.NApd.isna()pd.NAnp.nanTrueobjectpd.isna()None"NA"fillna()dropna()"Int64"float64pd.NATypeError: cannot convert NA to numericdf = df.convert_dtypes(dtype_backend="pyarrow")astypefloat64.astype("Int64")np.nanpd.NANoneValueErrordf.eq(np.nan)Falsedf.eq(pd.NA)pd.NA==pd.NAnp.nanpd.NAdf.replace([np.nan, None, "N/A", ""], pd.NA).convert_dtypes().fillna(False).fillna(True).locdf.loc[df["flag"].fillna(False)]interpolate()pd.NAnp.nanpd.NAdf["col"].replace(pd.NA, np.nan)sum()mean()pd.NAskipna=Truemin()max()pd.NApd.NAnp.nannp.nanpd.NAdtype_backend="numpy_nullable"pd.NAobjectNotImplementedErrorpandas>=2.0.3pd.NAgroupby().agg()pd.NAmerge()objectpd.array([1, pd.NA, 3], dtype="Int64").sum()4np.nanpd.NAnp.nan.dtypes.apply()