给Pandas DataFrame新增多列触发KeyError:原因及解决方法
Pandas apply新增多列触发KeyError的原因与解决方法
先还原你遇到的场景:
这段代码能正常生成x和y列:
import pandas as pd df = pd.DataFrame(data = {'a': [1, 2, 3], 'b': [4, 5, 6]}) def myfun(a, b): return [a + b, a - b] df[['x', 'y']] = df.apply( lambda row: myfun(row.a, row.b), axis=1)
输出的DataFrame符合预期:
a b x y 0 1 4 5 -3 1 2 5 7 -3 2 3 6 9 -3
但当你尝试用几乎相同的代码新增xx和yy列时:
df[['xx','yy']] = df.apply(lambda row: myfun(row.a, row.b), axis=1)
却弹出了KeyError: "['xx' 'yy'] not in index"的错误,这到底是怎么回事?
错误原因
核心问题出在Pandas对多列赋值的逻辑差异上:
- 第一次赋值时,
df.apply(...)返回的是一个元素为列表的Series。此时Pandas会自动识别到列表的长度和你指定的列数(2列)匹配,于是悄悄把这个Series转换成了一个2列的DataFrame,完成赋值。 - 但第二次赋值时,因为
xx和yy这两列原本不存在于DataFrame中,Pandas的赋值逻辑会先尝试对齐索引——它会把apply返回的Series当成单个值来处理,而单个列表无法匹配两个新列的索引,所以直接抛出了KeyError。
简单说就是:Pandas对“给已有列赋值”和“新增多列”的处理逻辑不一样,前者会自动展开列表型Series,后者不会。
正确的实现方法
这里有几种靠谱的解决方式,按推荐程度排序:
1. 用result_type='expand'参数(最简洁)
直接给apply加上result_type='expand'参数,让它直接返回一个DataFrame,这样Pandas就能明确知道要把每一列对应到你指定的列名上:
df[['xx','yy']] = df.apply(lambda row: myfun(row.a, row.b), axis=1, result_type='expand')
这个参数的作用就是告诉Pandas:把每行返回的列表拆分成单独的列,完美解决新增多列的问题。
2. 显式将Series转为DataFrame
如果你的Pandas版本比较旧(低于0.23,这个版本才引入result_type参数),可以手动把apply返回的Series转换成DataFrame:
df[['xx','yy']] = df.apply(lambda row: myfun(row.a, row.b), axis=1).apply(pd.Series)
apply(pd.Series)会把每个列表元素拆分成DataFrame的一列,再赋值给目标列。
3. 用向量化操作(性能最优)
如果你的函数逻辑像这个例子一样简单(只是加减运算),完全没必要用apply——因为apply是逐行循环,效率很低。直接用Pandas的向量化运算更快:
# 一行搞定 df[['xx','yy']] = df['a'] + df['b'], df['a'] - df['b'] # 或者更清晰的分步写法 df['xx'] = df['a'] + df['b'] df['yy'] = df['a'] - df['b']
这种方式在处理大数据集时,速度会比apply快几个数量级。
4. 用assign链式赋值
如果你习惯用链式操作,不想直接修改原DataFrame,可以用assign方法:
df = df.assign( **df.apply(lambda row: myfun(row.a, row.b), axis=1, result_type='expand') .rename(columns={0: 'xx', 1: 'yy'}) )
这种方式会返回一个新的DataFrame,原数据不会被修改。
内容的提问来源于stack exchange,提问作者A Hansson
相关产品推荐
相关产品推荐

