fillna传字典时键必须严格匹配列名字符串,否则该列不填充且不报错;值需与列类型兼容,避免隐式转换;条件填充须用loc配合,不可直接对布尔索引结果调用fillna。

fillna传字典时键必须是列名,且只影响对应列
直接传{'age': 0, 'city': 'unknown'}给fillna(),pandas会自动按列名匹配并填充——不是按位置、也不是按数据类型,纯粹靠列名字符串完全一致。如果列名带空格或大小写不一致(比如DataFrame里是'City'但字典里写'city'),那一列就完全不会被填充,也不会报错,容易漏掉。
常见错误现象:df.fillna({'age': 0})执行后df['age']没变化,大概率是列名实际叫'Age'或'user_age';用list(df.columns)确认真实列名再写键名。
- 字典的键必须是字符串,不能是
int或None - 值可以是标量(
0、'N/A')、Series(需对齐索引)、或pd.NA等缺失标识 - 未在字典中出现的列保持原样,
fillna不会动它们
混合填充类型:数字列填0,字符列填空字符串或None
不同列语义不同,强行统一填0会让字符串列变成'0'(触发隐式类型转换),填None又可能让整数列转成float64(因NaN只能存于浮点)。稳妥做法是按列类型分组构造字典:
fill_dict = {
'score': 0,
'rank': -1,
'name': '',
'category': 'other',
'is_active': False
}
注意:False填入布尔列没问题,但填入整数列会变0;''填入数值列会报TypeError(无法将字符串转为float),所以务必确保字典值与目标列类型兼容。
立即学习“Python免费学习笔记(深入)”;
- 用
df.dtypes检查各列类型,避免跨类型硬塞 - 若某列含混合类型(如
object但混了数字和字符串),先清洗再填,否则fillna可能静默失败 - 想填
pd.NA(pandas原生缺失值)?确保列已转为支持pd.NA的扩展类型(如string、Int64),否则会被转成np.nan
只对部分行+部分列组合填充:先切片再fillna
fillna本身不支持条件过滤,想“只给2023年之后的数据中price列填-1”,不能靠字典参数实现。必须拆成两步:先用布尔索引定位子集,再对其调用fillna:
mask = df['date'] > '2023-01-01'
df.loc[mask, ['price', 'discount']] = df.loc[mask, ['price', 'discount']].fillna({'price': -1, 'discount': 0})
这里fillna传的仍是字典,但作用范围已被loc严格限定。漏掉.loc直接写df[mask].fillna(...)会导致SettingWithCopyWarning,甚至不生效——因为df[mask]可能是视图而非原地引用。
- 所有条件填充场景,优先考虑
loc+fillna组合,别试图用where或mask绕开 - 字典里只写你真正要填的列,多写的键会被忽略,但少写会导致对应列在子集内仍为空
- 如果条件复杂(如多层嵌套逻辑),把mask单独算出来,比堆在一行里更易读、易调试
性能提示:大数据量下避免重复构造字典
在循环或apply里反复调用fillna({'a': 0, 'b': 'x'}),字典对象每次都是新创建的,但内容不变。Python里小字典开销不大,但如果列非常多(上百列)、字典键值对也上百,且调用频次高(如每行都算),会有可测的GC压力。此时可提前定义好不可变字典(虽然Python没原生const dict,但可放模块顶层或作为类属性):
FILL_MAP = {'user_id': -1, 'amount': 0.0, 'status': 'pending'}
然后统一用df.fillna(FILL_MAP)。另外,如果填充逻辑需要动态计算(如每列填当前列均值),就别用字典,改用df.apply(lambda s: s.fillna(s.mean())),但注意这比字典慢一个数量级——字典是C层向量化实现,apply是Python层逐列循环。
最易被忽略的一点:字典键顺序不影响结果,但如果你依赖dict插入顺序做列处理(比如后续要遍历键做日志),从Python 3.7+起是稳定的,但别假设旧版本也如此。


















