pandas如何用.loc实现单行值覆盖多行?对比iloc赋值差异
pandas使用.loc赋值单行值到多行时出现NaN的解决方法
问题复现
需求为将DataFrame中某一行的多个列值,批量赋值给其他若干行。使用.iloc按位置赋值可正常实现,但使用带筛选条件的.loc赋值时,目标位置全部返回NaN,复现代码如下:
- 构造测试数据集
import pandas as pd df = pd.DataFrame(dict(A = [1,2,0,0],B=[0,0,0,10],C=[3,4,5,6])) df.index = ['a','b','c','d']
- 使用
.loc执行赋值操作(两种写法均失败)
# 条件筛选赋值 df.loc[df['A']>0, ['B','C']] = df.loc['d',['B','C']] # 指定索引名赋值 df.loc[['a','b'], ['B','C']] = df.loc['d',['B','C']]
运行后a、b行的B、C列全部为NaN,结果如下:
A B C a 1.0 NaN NaN b 2.0 NaN NaN c 0.0 0.0 5.0 d 0.0 10.0 6.0
- 使用
.iloc按位置赋值可得到预期结果
df.iloc[0:2,1:3] = df.iloc[3,1:3]
运行结果符合预期:
A B C a 1 10 6 b 2 10 6 c 0 0 5 d 0 10 6
根本原因
问题出在pandas默认的索引对齐机制:
- 右侧
df.loc['d',['B','C']]返回的是一个Series,它的行标签是d,列方向索引是['B','C'];当把它赋值给左侧行标签为a、b的切片时,pandas会先按行标签做匹配,a、b和d完全匹配不上,匹配不到的位置就会自动填充NaN。 .iloc是纯按位置操作,完全忽略索引标签,所以赋值时直接按位置把值填进去,不会出现对齐失败的问题。
解决方案
不需要把布尔筛选条件转换成行号改用iloc,只要把右侧待赋值的序列转成不带索引的数组/列表,跳过索引对齐逻辑,就可以直接用.loc实现需求:
# 方法1:使用.values获取numpy数组,是最常用的写法 df.loc[df['A']>0, ['B','C']] = df.loc['d',['B','C']].values # 方法2:转成原生list,效果一致 df.loc[['a','b'], ['B','C']] = df.loc['d',['B','C']].tolist()
注意:如果赋值的右侧是多行多列的DataFrame切片,只要保证左右两侧切片的形状完全一致,加
.values转成数组后同样可以正常赋值,不会出现NaN问题。
内容的提问来源于stack exchange,提问作者Peter Lustig
相关产品推荐
相关产品推荐

