Pandas:对行子集的category列使用apply方法时出现异常的咨询
Pandas中Category类型列子集apply触发未存在类别KeyError的问题
问题重现
当你将DataFrame的列转为Category类型后,取行子集并使用apply通过字典映射值时,会触发子集中不存在的类别KeyError,即使子集中根本没有该类别值:
df = pd.DataFrame({'A':[11,22,33], 'B':['foo','baz', 'bar']}) df['B'] = df['B'].astype('category') dfs = df[df['A'] > 20].reset_index(drop=True) d = {'baz': 14, 'bar': 19} dfs['B'].apply(lambda x: d[x])
执行后会报错:KeyError: 'foo',但dfs中明明只有baz和bar两个值。而如果不将列转为Category类型,代码可以正常运行并得到预期结果:
df = pd.DataFrame({'A':[11,22,33], 'B':['foo','baz', 'bar']}) dfs = df[df['A'] > 20].reset_index(drop=True) d = {'baz': 14, 'bar': 19} dfs['B'].apply(lambda x: d[x])
输出:
0 14 1 19 Name: B, dtype: int64
原因分析
Category类型的列会保留原始DataFrame的所有类别元数据,即使生成子集后某些类别并没有出现在子集中。当使用apply方法时,Pandas内部会基于这些完整的类别集合进行操作(而非仅子集里的实际值),导致lambda函数被应用到所有原始类别,包括子集中不存在的foo,从而触发KeyError。
解决方案
方案1:移除子集中未使用的类别
在生成子集后,调用cat.remove_unused_categories()删除未被使用的类别,这样apply就只会处理子集中实际存在的类别:
df = pd.DataFrame({'A':[11,22,33], 'B':['foo','baz', 'bar']}) df['B'] = df['B'].astype('category') dfs = df[df['A'] > 20].reset_index(drop=True) # 清理未使用的类别 dfs['B'] = dfs['B'].cat.remove_unused_categories() d = {'baz': 14, 'bar': 19} dfs['B'].apply(lambda x: d[x])
方案2:改用map方法(推荐)
map方法专门用于元素级别的映射,只会处理子集中实际存在的值,不受Category列的完整类别元数据影响:
df = pd.DataFrame({'A':[11,22,33], 'B':['foo','baz', 'bar']}) df['B'] = df['B'].astype('category') dfs = df[df['A'] > 20].reset_index(drop=True) d = {'baz': 14, 'bar': 19} dfs['B'].map(d)
方案3:将Category列转回字符串类型
如果不需要保留Category类型,可以先将列转回普通字符串,再使用apply:
df = pd.DataFrame({'A':[11,22,33], 'B':['foo','baz', 'bar']}) df['B'] = df['B'].astype('category') dfs = df[df['A'] > 20].reset_index(drop=True) d = {'baz': 14, 'bar': 19} dfs['B'].astype(str).apply(lambda x: d[x])
内容的提问来源于stack exchange,提问作者javadr
相关产品推荐
相关产品推荐

