如何在pandas的map()中获取值索引,同步实现数据清洗与错误值统计
问题场景
从CSV读取的DataFrame存在数据错位问题:部分值位于错误列(如'900'出现在ZIPCODE列、'RQ'出现在语言列而非国籍列),部分错误值需转为NaN,部分整行列偏移需单独处理。当前采用逐列map函数做数据清洗(例如用正则校验ZIPCODE列),希望在清洗的同时统计每行的错误值数量,但map函数无法访问对应值的索引;虽可分开统计错误值,但希望合并清洗与统计流程。
现有清洗代码示例:
import re import numpy as np import pandas as pd def is_zipcode(value: str, regx): if regx.match(value): return value else: return np.nan regx = re.compile(r"^[0-9]{5}(?:-[0-9]{4})?$") df['ZIPCODE'] = df['ZIPCODE'].map(lambda x: is_zipcode(x, regx), na_action='ignore')
分开统计错误值的代码示例:
def is_zipcode_count(value: str, regx): if regx.match(value): return 0 else: return 1 regx = re.compile(r"^[0-9]{5}(?:-[0-9]{4})?$") df['badValues'] = df['ZIPCODE'].map(lambda x: is_zipcode_count(x, regx), na_action='ignore') df['badValues'] = df['badValues'] + df['Nationalities'].map(is_nationality, na_action='ignore')
期望的合并逻辑:在清洗函数中直接更新对应行的错误计数。
可行解决方案
方案1:使用df.apply逐行处理(推荐)
利用apply可访问整行数据的特性,一次遍历完成列清洗与错误计数,适合需要复杂行级逻辑的场景(比如整行列偏移的特殊处理)。
代码示例:
import re import numpy as np import pandas as pd # 预定义各列校验规则 zipcode_regx = re.compile(r"^[0-9]{5}(?:-[0-9]{4})?$") valid_nationalities = {"CN", "US", "JP", "EU"} def process_row(row): # 初始化错误计数 if pd.isna(row['badValues']): row['badValues'] = 0 # 处理ZIPCODE列 if not pd.isna(row['ZIPCODE']): if not zipcode_regx.match(str(row['ZIPCODE'])): row['ZIPCODE'] = np.nan row['badValues'] += 1 # 处理Nationalities列 if not pd.isna(row['Nationalities']): if row['Nationalities'] not in valid_nationalities: row['Nationalities'] = np.nan row['badValues'] += 1 # 其他列的清洗逻辑可在此添加 return row # 初始化错误计数列 df['badValues'] = 0 # 逐行处理数据 df = df.apply(process_row, axis=1)
方案2:结合mask批量处理(性能最优)
适合规则统一的批量校验场景,性能远高于逐行遍历,代码简洁易维护。
代码示例:
import re import numpy as np import pandas as pd zipcode_regx = re.compile(r"^[0-9]{5}(?:-[0-9]{4})?$") valid_nationalities = {"CN", "US", "JP", "EU"} # 初始化错误计数列 df['badValues'] = 0 # 处理ZIPCODE列:标记错误值为NaN,同时累加错误数 zipcode_valid = df['ZIPCODE'].astype(str).str.match(zipcode_regx) df['ZIPCODE'] = df['ZIPCODE'].mask(~zipcode_valid, np.nan) df['badValues'] += (~zipcode_valid).astype(int) # 处理Nationalities列 nationality_valid = df['Nationalities'].isin(valid_nationalities) df['Nationalities'] = df['Nationalities'].mask(~nationality_valid, np.nan) df['badValues'] += (~nationality_valid).astype(int)
方案3:iterrows逐行遍历(直观但性能差)
如果需要直接操作索引,可采用此方式,但大数据量下不推荐使用。
代码示例:
import re import numpy as np import pandas as pd zipcode_regx = re.compile(r"^[0-9]{5}(?:-[0-9]{4})?$") df['badValues'] = 0 for idx, row in df.iterrows(): # 处理ZIPCODE列 if not pd.isna(row['ZIPCODE']): if not zipcode_regx.match(str(row['ZIPCODE'])): df.at[idx, 'ZIPCODE'] = np.nan df.at[idx, 'badValues'] += 1 # 其他列的清洗逻辑可在此添加
方案对比
- 方案1:灵活性最高,支持复杂行级定制逻辑,小数据量首选;
- 方案2:性能最优,适合规则统一的批量处理场景,大数据量首选;
- 方案3:逻辑直观但性能最差,仅用于小型数据集或调试场景。
内容的提问来源于stack exchange,提问作者студент001
相关产品推荐
相关产品推荐

