Pandas批量转换多列数据异常,单独转换却正常?
Pandas批量转换ASCII列时丢失列和行的问题
问题背景
现有一个包含三类数据的DataFrame:
- 13列ASCII码形式的序列号数据,列名存在列表
serialname_list中; - 所有行都存在的核心数据列;
- 仅80%行存在、含NaN值的附加数据列。
DataFrame结构示例:
sn1 sn2 ... sn13 data1 data2 add1 add2 0 74 79 76 66 5.0 5.0 NaN NaN 1 81 66 89 65 1.0 1.0 NaN NaN 2 82 81 78 66 2.0 2.0 79 80
需求是将13列ASCII码转为对应字符,合并为单个字段后删除原列,同时删除含无效浮点值的序列号行。但使用df[serialname_list] = df[serialname_list].apply(lambda x: x.map(chr))批量转换时,会丢失所有附加数据列,且删除含NaN的附加数据行;仅逐个列调用apply才能正常运行。
问题原因
核心是混淆了Pandas中apply和applymap的适用场景:
apply默认按列/行处理,传入的是整个Series(列/行对象),而非单个元素。若直接对多列DataFrame调用apply,要么因传入对象不匹配触发隐式异常,要么因临时结果的索引对齐问题导致原DF的列/行意外丢失。- 逐个列调用
apply时,是对单个Series的每个元素执行函数,因此能正常工作。
正确批量处理方式
方法1:用applymap实现元素级批量转换
applymap专门用于对DataFrame的每个元素执行函数,完全匹配批量转换ASCII码的需求:
def convertserial(df): def chr_convert(x): return chr(x) # 过滤无效ASCII行(值小于1的非有效ASCII) msk = df[serialname_list].lt(1).any(axis=1) df = df.drop(df.index[msk]).reset_index(drop=True) # 批量转换ASCII码为字符 df[serialname_list] = df[serialname_list].astype(int).applymap(chr_convert) # 也可直接简化为:df[serialname_list] = df[serialname_list].astype(int).applymap(lambda x: chr(x)) # 生成合并后的serial字段 df.insert(0, "serial", df.apply( lambda x: f"{x['sn1']}{x['sn2']}{x['sn3']}{x['sn4']+'-'+x['sn5']+x['sn6']+x['sn7']+x['sn8']+'-'+x['sn9']+x['sn10']+x['sn11'] if x['sn4'] == 'A' else '-'+x['sn4']+x['sn5']+x['sn6']+x['sn7']+'-'+x['sn8']+x['sn9']+x['sn10']}", axis=1 )) # 生成position字段 df.insert(1, "position", df.apply( lambda x: f"{x['sn12']+x['sn13'] if x['sn4'] == 'A' else x['sn11']+x['sn12']}", axis=1 )) # 删除原序列号列 df.drop(columns=serialname_list, inplace=True) return df
方法2:循环遍历列批量处理
通过循环遍历serialname_list中的列,本质和逐个列调用逻辑一致,但避免了重复代码:
def convertserial(df): def chr_convert(x): return chr(x) # 过滤无效ASCII行 msk = df[serialname_list].lt(1).any(axis=1) df = df.drop(df.index[msk]).reset_index(drop=True) df[serialname_list] = df[serialname_list].astype(int) # 循环处理每一列 for col in serialname_list: df[col] = df[col].apply(chr_convert) # 后续生成合并字段、删除原列的代码与方法1一致 df.insert(0, "serial", df.apply( lambda x: f"{x['sn1']}{x['sn2']}{x['sn3']}{x['sn4']+'-'+x['sn5']+x['sn6']+x['sn7']+x['sn8']+'-'+x['sn9']+x['sn10']+x['sn11'] if x['sn4'] == 'A' else '-'+x['sn4']+x['sn5']+x['sn6']+x['sn7']+'-'+x['sn8']+x['sn9']+x['sn10']}", axis=1 )) df.insert(1, "position", df.apply( lambda x: f"{x['sn12']+x['sn13'] if x['sn4'] == 'A' else x['sn11']+x['sn12']}", axis=1 )) df.drop(columns=serialname_list, inplace=True) return df
关键注意事项
- 绝对避免将
df[serialname_list].apply(...)的结果直接赋值给整个df,这会导致仅保留序列号列,丢失核心数据和附加数据列。 - 元素级批量处理优先用
applymap,列/行级复杂逻辑才用apply,两者不要混用。
内容的提问来源于stack exchange,提问作者user2059972
相关产品推荐
相关产品推荐

