如何用Lambda为DataFrame创建新列?现有代码报KeyError
问题:使用Lambda函数为Pandas DataFrame添加新列时出现KeyError
我想给Pandas DataFrame创建3个新列,写了下面这段代码:
columnList = { 'hasCampaign': ( lambda x: x[ 'CAMPAIGNID' ] != '' ), 'hasLeadType': ( lambda x: x[ 'LEADTYPE' ] != '' ), 'hasEvent': ( lambda x: x[ 'EVENT' ] != '' ) } for ( k, v ) in columnList.items(): df = df.assign( k = v )
原本以为能正常运行,结果却碰到了KeyError: 'CAMPAIGNID'的报错,而且我已经确认DataFrame里确实存在CAMPAIGNID列,报错的堆栈信息是:
KeyError Traceback (most recent call last) /usr/local/lib/python3.6/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance) 2524 try: -> 2525 return self._engine.get_loc(key) 2526 except KeyError: pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc() pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc() pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item() pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()
请问怎么正确用这三个Lambda函数计算并添加这三个新列?
问题分析
这个报错主要有两个核心原因:
- 在
df.assign(k = v)中,k会被当作**固定字符串"k"**来处理,而不是循环变量k对应的列名,完全偏离了你想要动态生成列名的需求; - 你的lambda函数里的参数
x默认会被传入单个Series(也就是某一列的数据),而不是整个DataFrame或行数据,所以它找不到CAMPAIGNID这个键——毕竟单个列里不存在这个列名。
正确解决方案
这里提供两种简单且高效的实现方式,任选其一即可:
方法一:批量传入字典到assign(推荐)
df.assign()原生支持直接传入字典,键是新列名,值是基于整个DataFrame的计算逻辑。这种方式不需要循环,代码更简洁高效:
column_dict = { 'hasCampaign': lambda df: df['CAMPAIGNID'] != '', 'hasLeadType': lambda df: df['LEADTYPE'] != '', 'hasEvent': lambda df: df['EVENT'] != '' } # 用**解包字典作为关键字参数,实现动态列名绑定 df = df.assign(**column_dict)
这里的lambda函数接收整个DataFrame作为参数,自然能正确访问到CAMPAIGNID等列,同时字典的键会被直接用作新列名,完美匹配你的需求。
方法二:修复循环中的assign写法
如果你一定要用循环实现,需要调整两个关键点:
- 用字典解包的方式传入动态列名,避免把
k当成固定字符串; - 确保lambda函数的参数是整个DataFrame(或按行处理时指定
axis=1)。
修改后的循环代码如下:
columnList = { 'hasCampaign': lambda df: df['CAMPAIGNID'] != '', 'hasLeadType': lambda df: df['LEADTYPE'] != '', 'hasEvent': lambda df: df['EVENT'] != '' } for col_name, func in columnList.items(): # 用字典解包传递动态列名,避免字面量字符串问题 df = df.assign(**{col_name: func(df)})
如果你的逻辑涉及多列的行级判断(比如同时用到多个列的值),也可以用apply按行处理:
columnList = { 'hasCampaign': lambda row: row['CAMPAIGNID'] != '', 'hasLeadType': lambda row: row['LEADTYPE'] != '', 'hasEvent': lambda row: row['EVENT'] != '' } for col_name, func in columnList.items(): df[col_name] = df.apply(func, axis=1)
不过要注意,apply(axis=1)的性能不如直接按列操作,如果你只是判断列是否为空字符串,优先选择前面的列操作方式。
额外小技巧
判断列是否为空字符串时,还可以用Pandas的ne()方法,写法更简洁:
lambda df: df['CAMPAIGNID'].ne('')
和df['CAMPAIGNID'] != ''的效果完全一致。
内容的提问来源于stack exchange,提问作者Fernando F
相关产品推荐
相关产品推荐

