使用map、Lambda和切片查询字典时如何处理KeyError问题?
如何处理字典映射时的缺失键问题
操作背景
我从静态电子表格创建查找表/字典,用于后续输入数据的标注,并添加名为label的新字段:
import pandas as pd lookup_table_data = pd.read_csv(r'C:\Location\format1.csv', sep=',') lookup_table_data['label'] = 'apache'
随后生成字典:
my_format = lookup_table_data.set_index('server').T.to_dict('list') print(my_format) # 输出结果 {'ABC123': ['IBM', 1000, 'East Coast', 'apache'], 'ABC456': ['Dell', 800, 'West Coast', 'apache'], 'XYZ123': ['HP', 900, 'West Coast', 'apache']}
读取输入数据:
my_data = pd.read_csv(r'C:\Location\my_data.csv') print(my_data) # 输出结果 server busy datetime 0 ABC123 24% 6/1/2024 0:02 1 ABC456 45% 6/1/2024 4:01 2 GHI100 95% 6/1/2024 9:10
遇到的问题
当尝试通过切片方法标注字段时,因输入数据中存在不在字典内的server(GHI100),出现KeyError:
my_data['type'] = my_data['server'].map(lambda x: my_format[x][0]) my_data['cost'] = my_data['server'].map(lambda x: my_format[x][1]) my_data['location'] = my_data['server'].map(lambda x: my_format[x][2]) # 报错信息 KeyError: 'GHI100'
移除GHI100数据后代码可正常运行:
server %busy datetime type cost location 0 ABC123 24% 6/1/2024 0:02 IBM 1000 East Coast 1 ABC456 45% 6/1/2024 4:01 Dell 800 West Coast
尝试使用.get()方法设置默认值时,若直接索引会出现“Index out of list range”;若仅获取字典值,则会把整个列表存入字段,不符合需求:
my_data['type'] = my_data['server'].map(lambda x: my_format.get(x, None))
结果:
server %busy datetime type 0 ABC123 24% 6/1/2024 0:02 [IBM, 1000, East Coast] 1 ABC456 45% 6/1/2024 4:01 [Dell, 800, West Coast] 2 GHI100 95% 6/1/2024 9:10 None
目前只能通过内连接移除不在字典中的server数据,但希望找到更合适的解决方案。
解决方案
方法1:优化.get()方法,设置默认值列表
在.get()中返回一个与字典值长度一致的默认列表,避免索引越界,同时提取对应位置的元素:
# 为缺失的server返回包含None的默认列表,长度与字典值一致 my_data['type'] = my_data['server'].map(lambda x: my_format.get(x, [None, None, None, None])[0]) my_data['cost'] = my_data['server'].map(lambda x: my_format.get(x, [None, None, None, None])[1]) my_data['location'] = my_data['server'].map(lambda x: my_format.get(x, [None, None, None, None])[2]) my_data['label'] = my_data['server'].map(lambda x: my_format.get(x, [None, None, None, None])[3])
执行后结果:
server busy datetime type cost location label 0 ABC123 24% 6/1/2024 0:02 IBM 1000.0 East Coast apache 1 ABC456 45% 6/1/2024 4:01 Dell 800.0 West Coast apache 2 GHI100 95% 6/1/2024 9:10 None NaN None None
方法2:使用DataFrame左连接(推荐)
将查找字典转回DataFrame,通过左连接合并输入数据,自动保留所有行并填充缺失值,代码更简洁易维护:
# 将字典转回DataFrame,指定列名 lookup_df = pd.DataFrame.from_dict(my_format, orient='index', columns=['type', 'cost', 'location', 'label']) # 左连接,保留my_data的所有行 my_data = my_data.merge(lookup_df, left_on='server', right_index=True, how='left')
执行后结果与方法1一致,且无需重复编写map逻辑,后续字段变更时只需调整列名即可。
方法3:使用Series映射
将字典的每个字段单独转为Series,再进行映射,Series的map方法会自动将缺失键映射为NaN:
# 提取各字段的Series type_series = pd.Series({k: v[0] for k, v in my_format.items()}) cost_series = pd.Series({k: v[1] for k, v in my_format.items()}) location_series = pd.Series({k: v[2] for k, v in my_format.items()}) label_series = pd.Series({k: v[3] for k, v in my_format.items()}) # 映射到my_data my_data['type'] = my_data['server'].map(type_series) my_data['cost'] = my_data['server'].map(cost_series) my_data['location'] = my_data['server'].map(location_series) my_data['label'] = my_data['server'].map(label_series)
内容的提问来源于stack exchange,提问作者Chester
相关产品推荐
相关产品推荐

