数据清洗:缺失值、重复值、异常值——处理脏数据的正确姿势

📅 2026/7/4 4:22:28
数据清洗:缺失值、重复值、异常值——处理脏数据的正确姿势
数据清洗缺失值、重复值、异常值——处理脏数据的正确姿势真实世界的数据从来不干净。缺失值、重复行、乱码、类型错误、异常数据——每一项都能让你的分析结果差十万八千里。数据清洗通常占数据分析 70%-80% 的时间。今天把常见的清洗套路讲透。制造一份脏数据importpandasaspdimportnumpyasnp dfpd.DataFrame({name:[张三,李四,None,赵六,孙七,李四],age:[25,30,22,999,28,30],city:[北京,,广州,深圳,北京,上海],salary:[15000,20000,None,25000,18000,20000],score:[85.5,np.nan,78.0,95.2,60.0,np.nan],})print(df)查看数据质量# 每列缺失值数量print(df.isnull().sum())# 缺失值比例print(df.isnull().sum()/len(df)*100)# 每列的数据类型print(df.dtypes)# 重复行print(f重复行数{df.duplicated().sum()})处理缺失值# 删除有缺失的行df_cleandf.dropna()# 任何列有缺失就删df_cleandf.dropna(subset[salary])# 只关心某些列df_cleandf.dropna(thresh4)# 至少有4个非空值的行才保留# 填充缺失值df[name].fillna(未知,inplaceTrue)# 固定值df[salary].fillna(df[salary].mean(),inplaceTrue)# 均值df[salary].fillna(df[salary].median(),inplaceTrue)# 中位数df[city].fillna(methodffill,inplaceTrue)# 前向填充df[score].fillna(methodbfill,inplaceTrue)# 后向填充# 分组填充按城市填均薪df[salary]df.groupby(city)[salary].transform(lambdax:x.fillna(x.mean()))哪种策略看情况缺失比例策略 5%直接删行5%-30%均值/中位数/众数填充 30%考虑删列或单独分析处理重复值# 查看重复print(df[df.duplicated()])print(df[df.duplicated(subset[name])])# 只看名字重复# 删除重复df.drop_duplicates(inplaceTrue)# 完全重复的行df.drop_duplicates(subset[name],keepfirst,inplaceTrue)# 保留第一个df.drop_duplicates(subset[name],keeplast,inplaceTrue)# 保留最后一个处理异常值# IQR 方法最常用Q1df[age].quantile(0.25)Q3df[age].quantile(0.75)IQRQ3-Q1 lowerQ1-1.5*IQR upperQ31.5*IQRprint(f正常范围{lower:.0f}~{upper:.0f})outliersdf[(df[age]lower)|(df[age]upper)]print(f异常值{len(outliers)}个\n{outliers})# 处理替换为上下限或 NaNdf[age]df[age].clip(lower,upper)# Z-score 方法正态分布的数据用fromscipyimportstats z_scoresnp.abs(stats.zscore(df[salary].dropna()))outliersz_scores3数据类型转换# 查看类型print(df.dtypes)# 转换df[age]df[age].astype(int)df[salary]pd.to_numeric(df[salary],errorscoerce)# 错的值变 NaNdf[date]pd.to_datetime(df[date],errorscoerce)# 字符串处理df[name]df[name].str.strip()# 去空白df[city]df[city].str.replace( ,)实战一份脏数据从头洗到尾# 模拟脏数据dfpd.DataFrame({name:[ 张三 ,李四,None,李四,王五],age:[25,30,22,30,abc],salary:[15000,20000,None,20000,18000],join_date:[2026-01-15,2026/02/20,2026-03-10,not a date,2026-05-01],})print( 洗前 )print(df)print(f\n缺失值\n{df.isnull().sum()})# 1. 去空白df[name]df[name].str.strip()# 2. 填充缺失df[name].fillna(未知,inplaceTrue)df[salary].fillna(df[salary].median(),inplaceTrue)# 3. 去重df.drop_duplicates(inplaceTrue)# 4. 类型转换df[age]pd.to_numeric(df[age],errorscoerce)df[join_date]pd.to_datetime(df[join_date],errorscoerce)# 5. 异常值检查df[age]df[age].clip(18,65)print(\n 洗后 )print(df)print(f\n缺失值\n{df.isnull().sum()})新手常见坑坑1inplace 不生效# 有些方法不支持 inplace# df.dropna(inplaceTrue) # OK# df.query(age 20, inplaceTrue) # 报错query 没有 inplace# 建议统一风格dfdf.dropna()dfdf.query(age 20)坑2fillna 的 method 在新版 Pandas 中已弃用# 旧写法已弃用# df.fillna(methodffill)# 新写法df.ffill()df.bfill()坑3异常值处理太激进# 别一上来就删先确认是不是真的异常# 比如 CEO 的工资就是比普通员工高很多那是真实数据动手试试创建一份含缺失值、重复值的 DataFrame完整清洗一遍用 IQR 方法检测一组数据中的异常值把一列字符串类型的数字转成整数处理非法值写在最后数据清洗没有银弹——不同数据用不同策略。关键是先看数据info、describe、isnull再动手。下一篇讲 groupby 和 agg这两招学完数据分析 80% 的活你就能干了。