Pandas按Cost_centre匹配替换Pool_costs 未匹配保留原值实现问题
问题描述
给定如下DataFrame:
输入df:
| Cost_centre | Pool_costs |
|---|---|
| 90272 | A |
| 92705 | A |
| 98754 | A |
| 91350 | A |
实现需求:根据Cost_centre字段值,将对应行的Pool_costs字段值替换为'B';若Cost_centre值不在指定列表中,则保留Pool_costs的原有值。本次指定需要匹配替换的Cost_centre列表为[90272, 91350]。
预期输出df:
| Cost_centre | Pool_costs |
|---|---|
| 90272 | B |
| 92705 | A |
| 98754 | A |
| 91350 | B |
现有尝试的问题
- 第一种lambda+apply写法错误:else分支直接返回整个
df['Pool_costs']列,没有取当前行的原值,导致结果不符合预期,错误代码如下:
df = pd.DataFrame({'Cost_centre': [90272, 92705, 98754, 91350], 'Pool_costs': ['A', 'A', 'A', 'A']}) pool_cc = ([90272,91350]) pool_cc_set = set(pool_cc) df['Pool_costs'] = df['Cost_centre'].apply(lambda x: 'B' if x in pool_cc_set else df['Pool_costs']) print (df)
- 第二种多条件拼接写法问题:当需要匹配的
Cost_centre数量较多时,需要手动拼接大量等值判断,可读性差、维护成本高,且原代码存在语法错误(括号不匹配、数值与字符串类型不匹配无法正确比较),原代码如下:
df = pd.DataFrame({'Cost_centre': [90272, 92705, 98754, 91350], 'Pool_costs': ['A', 'A', 'A', 'A']}) filt = df['Cost_centre'] == '90272'|df['Cost_centre'] == '91350') df.loc[filt, 'Pool_costs'] = 'B'
推荐实现方案
优先使用pandas原生向量化操作,性能远高于apply循环,且写法简洁易维护:
方案1:isin() + loc 赋值(最推荐)
直接用isin()判断值是否在目标列表中生成过滤条件,再用loc定位赋值,无论目标列表有多少元素,只需要维护列表本身即可,代码可读性高、性能最优:
import pandas as pd df = pd.DataFrame({'Cost_centre': [90272, 92705, 98754, 91350], 'Pool_costs': ['A', 'A', 'A', 'A']}) # 仅需维护这个目标匹配列表,新增/删除匹配值直接修改列表即可 pool_cc = [90272, 91350] # 生成过滤条件:筛选Cost_centre在目标列表中的行 filt = df['Cost_centre'].isin(pool_cc) # 仅对符合条件的行赋值为'B',不符合条件的行自动保留原值 df.loc[filt, 'Pool_costs'] = 'B'
运行结果与预期完全一致。
方案2:numpy.where() 实现
如果偏好条件判断生成整列的写法,可以用numpy.where,逻辑直观:
import pandas as pd import numpy as np df = pd.DataFrame({'Cost_centre': [90272, 92705, 98754, 91350], 'Pool_costs': ['A', 'A', 'A', 'A']}) pool_cc = [90272, 91350] df['Pool_costs'] = np.where( df['Cost_centre'].isin(pool_cc), # 判断条件 'B', # 条件成立时的取值 df['Pool_costs'] # 条件不成立时保留原值 )
方案3:修正后的apply写法(不推荐,性能差)
如果一定要用apply实现,需要指定axis=1按行遍历,取当前行的Pool_costs值。该方式在数据量较大时性能远低于向量化操作,非必要不使用:
import pandas as pd df = pd.DataFrame({'Cost_centre': [90272, 92705, 98754, 91350], 'Pool_costs': ['A', 'A', 'A', 'A']}) pool_cc = set([90272, 91350]) df['Pool_costs'] = df.apply(lambda row: 'B' if row['Cost_centre'] in pool_cc else row['Pool_costs'], axis=1)
注意:使用时需要确认
Cost_centre字段的类型,如果字段是字符串类型,目标列表里的值也要对应写成字符串格式,避免类型不匹配导致匹配失败。
内容的提问来源于stack exchange,提问作者Chandler Shores
相关产品推荐
相关产品推荐

