为何DataFrame中.dropna().apply()结果赋值给原列可正常运行?
df["foo"] = df["foo"].dropna().apply(lambda x: x+1)能成功执行? 这背后的核心原因是pandas的索引对齐机制——这是pandas处理数据赋值、合并等操作的核心特性,和你之前“赋值两边长度必须匹配”的认知并不冲突,只是你之前接触的大多是无索引序列(比如普通列表)的赋值场景。
让我一步步拆解这个操作:
右边的结果是带索引的Series
当你执行df["foo"].dropna().apply(lambda x: x+1)时,返回的不是一个简单的3元素列表,而是一个保留了原DataFrame行索引的Series。以你的示例df为例:import pandas as pd import numpy as np df = pd.DataFrame({'foo': [1, np.nan, 1, 1]}) print(df["foo"].dropna().apply(lambda x: x+1))输出是:
0 2.0 2 2.0 3 2.0 Name: foo, dtype: float64可以看到,这个Series只包含原df中
foo非NaN的行(索引0、2、3),并且每个值都加了1。赋值时按索引匹配,而非位置匹配
当你把这个Series赋值给df["foo"]时,pandas不会直接按“第一个值给第一行,第二个给第二行”的位置逻辑塞值,而是会根据索引一一对应:- 索引0:右边Series有对应的值2.0,所以替换原df中索引0的1;
- 索引1:右边Series没有这个索引,所以保留原df的NaN;
- 索引2:右边Series有对应的值2.0,替换原1;
- 索引3:右边Series有对应的值2.0,替换原1;
和你之前写法的本质联系
你之前用loc或apply加pd.notna()的写法,本质上也是在定位非NaN的索引位置并修改值;而这个dropna()后赋值的方式,是利用pandas自动忽略不匹配索引的特性,让那些不在右边Series里的索引位置(也就是原NaN的位置)保持不变,最终效果和你之前的写法是一致的。补充:什么时候需要长度匹配?
只有当你赋值的是无索引的序列(比如普通列表、numpy数组)时,pandas才会要求长度和目标列一致,因为这时候没有索引可以用来对齐,只能按位置顺序匹配。比如你如果写df["foo"] = [2,2,2],就会报错,因为长度3≠4;但如果是带索引的Series,哪怕长度不同,只要索引能匹配,就能正常赋值。
内容的提问来源于stack exchange,提问作者Stphn

