使用python数据清洗代码实例

发布时间:2026/10/17 5:17:59
使用python数据清洗代码实例
前言数据清洗data cleaning指的是在分析之前把原始数据里的缺失值、重复行、格式混乱、单位不统一、异常值这些问题处理掉。业内常说数据分析八成的工夫花在清洗上这句话虽然夸张但它准确地点出了清洗的地位脏数据直接进分析流程出来的结论一定是错的。关于数据清洗最常见的误解是把它当成调一个库函数就完事。真实情况是用什么工具并不重要重要的是你必须先把每一列的语义搞清楚——哪一列是数字、哪一列是日期、缺失值该填还是该丢、1,234 这种带千分位的字符串要先转成数字才能比较。第三方库能帮你少写代码但不能替你做判断。本机没有 pandas 等第三方库本文只用纯标准库csv、re、statistics、collections演示一份可以逐行推演的清洗流程。涉及 pandas 的地方只讲思路具体 API 以 pandas 官方文档为准。一、清洗的典型环节一份结构化数据比如 CSV从原始到可用一般要过这几关环节要解决的问题标准库工具解析分隔符、引号、编码csv模块、open(encoding...)去空白单元格前后多余空格str.strip()类型转换字符串转成数字 / 日期float()、int()、datetime缺失值空串、N/A、NULL自定义哨兵值集合去重完全重复或按主键重复的行set记录已见主键异常值明显超出合理范围的数值分位数 / 标准差判定统一格式大小写、单位、全角半角str方法、re清洗的每一步都应该是可解释的为什么要删这一行、为什么把缺失值填成中位数而不是 0这些判断需要记录否则数据一出问题就无从追溯。二、读 CSV 与处理缺失值不要用str.split(,)拆 CSV。原因很实在CSV 里如果某个单元格本身就是逗号规范写法是用引号把它包起来Beijing, China这种值用split(,)会被拆成两列整张表错位。csv模块处理引号、换行、转义是标准行为。# 适用于 Python 3.8import csvrows []with open(data.csv, encodingutf-8, newline) as f:reader csv.DictReader(f)for row in reader:rows.append(row)DictReader把每一行读成dict键是表头行。注意open()要加newline这是csv官方文档明确建议的写法否则在部分平台上会把行内的换行符处理错。缺失值在 CSV 里往往不是空而是N/A、null、-、暂无这类占位字符串。所以第一步是统一识别# 适用于 Python 3.8MISSING {, n/a, na, null, none, -, 暂无, 无}def is_missing(value):return value is None or value.strip().lower() in MISSINGdef to_float(value):清洗并把字符串转成浮点数无法转换时返回 None。if is_missing(value):return Nonecleaned value.strip().replace(,, ).replace(, )try:return float(cleaned)except ValueError:return None这里的关键点float(1,234)会抛ValueError所以要先去掉千分位逗号float()也会抛所以先判空。用try/except ValueError而不是先str.isdigit()因为-3.5、1e5都不是纯数字isdigit()会误判。三、去重与格式统一去重分两种。完全重复的行可以直接用set加元组做键按业务主键去重则是保留同 id 的第一条。# 适用于 Python 3.8def dedup_by_key(rows, key):seen set()result []for row in rows:value row.get(key)if value in seen:continueseen.add(value)result.append(row)return result格式统一里最容易被忽略的是全角/半角和首尾空格。中文输入法下的全角逗号、全角空格 与半角字符看起来很像程序眼里却是完全不同的字符。这里要分清两件事全角空格是码点 U3000 的表意空格它在str.isspace()下返回True所以str.strip()能把它去掉而全角逗号属于标点不是空白strip()完全不动它必须显式replace。判断一个字符到底算不算空白用str.isspace()最稳妥不要凭肉眼。# 适用于 Python 3.8def normalize_text(value):text value.strip()text text.replace(, ,).replace(, :)text text.replace( , ) # 全角空格U3000text .join(text.split()) # 合并连续空白return text .join(text.split())是个常用小技巧str.split()不带参数时按任意空白切分并丢弃空串再用单个空格拼回去就得到了压缩多余空格的效果。四、异常值检测与列统计异常值outlier检测最基础的做法是看数值分布。用statistics算均值和中位数用标准差或四分位距IQR划一个合理区间。# 适用于 Python 3.8import statisticsdef summarize(values):values 是已清洗的 float 列表。if not values:return Nonereturn {count: len(values),mean: statistics.mean(values),median: statistics.median(values),stdev: statistics.stdev(values) if len(values) 1 else 0.0,min: min(values),max: max(values),}def outliers(values, k3.0):if len(values) 2:return []mu statistics.mean(values)sd statistics.stdev(values)if sd 0:return []return [v for v in values if abs(v - mu) k * sd]需要留意的两点statistics.stdev()是样本标准差分母是n-1pstdev()才是总体标准差分母是n当样本量只有 1 时stdev()会抛StatisticsError所以上面先做了长度判断。样本标准差和总体标准差的区别决定了同一批数据算出来的正常范围宽窄不同选哪个要看这批数据是整体还是抽样。一个完整的清洗流水线# 适用于 Python 3.8import csvimport statisticsMISSING {, n/a, null, -}def clean_rows(rows, numeric_col):cleaned []for row in rows:raw row.get(numeric_col, )if raw is None or raw.strip().lower() in MISSING:continuetry:row[numeric_col] float(raw.strip().replace(,, ))except ValueError:continuecleaned.append(row)values [r[numeric_col] for r in cleaned]if len(values) 1:mu statistics.mean(values)sd statistics.stdev(values)if sd 0:cleaned [r for r in cleaned if abs(r[numeric_col] - mu) 3 * sd]return cleaned推演一下函数先逐行把目标列转成float转换失败或缺缺失的行被丢弃然后用剩余数值的均值和样本标准差把偏离超过 3 个标准差的整行剔除。默认参数之外什么都没写死调用方需要自己决定列名。常见坑点用str.split(,)拆 CSV。单元格里含逗号被引号包着时会错位整张表列数对不上。❌cols line.split(,)✅csv.reader或csv.DictReader。open()时不写encoding。跨平台读中文 CSV 会乱码或抛UnicodeDecodeError。❌open(data.csv)✅open(data.csv, encodingutf-8, newline)。直接float()而不处理缺失值和千分位。空串、N/A、1,234都会抛ValueError整个脚本中断。❌row[price] float(row[price])✅ 先判缺失、去掉千分位逗号再用try/except ValueError兜底。混淆stdev()与pstdev()。前者是样本标准差除以 n-1后者是总体标准差除以 n选错会让正常区间宽窄不同。❌ 把pstdev()当成stdev()用 ✅ 抽样数据用stdev()全量数据用pstdev()。在只有 1 个数值时调用stdev()。会抛StatisticsError因为样本标准差至少需要两个数据点。❌statistics.stdev([5])✅ 先判断len(values) 1再算或用pstdev()。遍历列表的同时删除元素。用for row in rows: rows.remove(row)会跳元素索引错乱漏删。❌for row in rows: if bad(row): rows.remove(row)✅rows [row for row in rows if not bad(row)]或用for row in list(rows):遍历副本。以为strip()能解决一切空白问题。它只处理首尾去不掉字符串中间的全角逗号、全角冒号那些必须显式replace。❌ 只靠value.strip()就认为数据干净了 ✅ 补上全角转半角、连续空白压缩等替换步骤。把缺失值一律填成 0。对金额身长温度这类字段0 是合法值用它当填充会污染均值让统计结果失真。❌df[price] df[price].fillna(0)一把梭 ✅ 先想清楚缺失是真为零还是没记录后者用中位数或直接丢弃更合理。总结任务标准库做法易错点读 CSVcsv.DictReadernewline别用split(,)编码open(..., encodingutf-8)不写就跨平台乱码缺失值哨兵集合 is_missing()缺失不一定是空串类型转换try/except ValueError千分位、空串会让float()抛错去重set记录主键不要边遍历边删原列表统计statistics.mean/median/stdevstdev与pstdev不是一回事异常值均值 ± k 倍标准差或 IQR标准差为 0 时要单独处理数据清洗的核心不是用了什么库而是每一步的语义判断是否有依据。把缺失、重复、格式、异常这四类问题按顺序处理并且每一步都留下可解释的理由清洗后的数据才值得拿去做分析。