Day 38Pandas 数据清洗——让脏数据变干净现实世界的数据几乎永远是脏的——缺失值、重复行、类型错误、异常值。今天学 Pandas 数据清洗的标准流程。一、缺失值处理缺失值是数据清洗的第一大问题。Pandas 用NaNNot a Number表示缺失。importpandasaspdimportnumpyasnp dfpd.DataFrame({姓名:[小明,小红,np.nan,小丽,小王],年龄:[18,np.nan,19,20,18],成绩:[85,92,np.nan,95,None],# None 也会被转成 NaN城市:[北京,上海,广州,np.nan,杭州],})print(df)# 姓名 年龄 成绩 城市# 0 小明 18.0 85.0 北京# 1 小红 NaN 92.0 上海# 2 NaN 19.0 NaN 广州# 3 小丽 20.0 95.0 NaN# 4 小王 18.0 NaN 杭州发现缺失值# 每列有多少缺失print(df.isnull().sum())# 姓名 1# 年龄 1# 成绩 2# 城市 1# 缺失比例print((df.isnull().sum()/len(df)*100).round(1))# 姓名 20.0%# 年龄 20.0%# 成绩 40.0%# 城市 20.0%# 任何一列有缺失的行print(df[df.isnull().any(axis1)])处理缺失值# 策略1删除有缺失的行df_cleandf.dropna()# 任意列有缺失就删df_cleandf.dropna(subset[姓名,成绩])# 只在指定列删除df_cleandf.dropna(howall)# 整行都是缺失才删df_cleandf.dropna(thresh3)# 至少有3个非缺值才保留# 策略2填充缺失值df[年龄]df[年龄].fillna(df[年龄].mean())# 均值填充df[成绩]df[成绩].fillna(df[成绩].median())# 中位数填充df[城市]df[城市].fillna(未知)# 固定值填充df[姓名]df[姓名].fillna(methodffill)# 用前一个值填充# 策略3特定列的不同策略fill_values{姓名:未知,年龄:df[年龄].median(),成绩:0,城市:未知,}dfdf.fillna(fill_values)二、重复值处理dfpd.DataFrame({姓名:[小明,小红,小明,小红,小王],年龄:[18,19,18,19,18],成绩:[85,92,85,92,88],})# 查看是否有重复print(df.duplicated().sum())# 有几行完全重复# 查看重复行print(df[df.duplicated(keepFalse)])# keepFalse 显示所有重复行# 删除重复行保留第一次出现dfdf.drop_duplicates()print(df)# 姓名 年龄 成绩# 0 小明 18 85# 1 小红 19 92# 4 小王 18 88# 按特定列去重dfdf.drop_duplicates(subset[姓名],keepfirst)# 名字相同的只留第一个三、数据类型转换dfpd.DataFrame({年龄:[18,19,20,21],成绩:[85.5,92.0,78.3,95.8],日期:[2026-01-01,2026-01-02,2026-01-03,invalid],是否合格:[是,否,是,是],})# astype 转换类型df[年龄]df[年龄].astype(int)# to_numeric 安全转换转换不了的变 NaNdf[成绩]pd.to_numeric(df[成绩])df[成绩]pd.to_numeric(df[成绩],errorscoerce)# 转换失败的变 NaN# to_datetime 转换日期df[日期]pd.to_datetime(df[日期],errorscoerce)# map 替换值df[是否合格]df[是否合格].map({是:True,否:False})# apply 自定义转换df[年龄分组]df[年龄].apply(lambdax:成年ifx18else未成年)print(df.dtypes)四、异常值处理# 用分位数检测异常值IQR 法deffind_outliers_iqr(series):Q1series.quantile(0.25)Q3series.quantile(0.75)IQRQ3-Q1 lowerQ1-1.5*IQR upperQ31.5*IQRreturn(serieslower)|(seriesupper)scorespd.Series([85,92,78,95,88,150,72,-5,86,91])outliersfind_outliers_iqr(scores)print(f异常值{scores[outliers].to_list()})# [150, -5]# 处理异常值# 方式1替换为上下限Q1,Q3scores.quantile(0.25),scores.quantile(0.75)IQRQ3-Q1 lower,upperQ1-1.5*IQR,Q31.5*IQR scores_clippedscores.clip(lower,upper)# 方式2替换为中位数scores[outliers]scores.median()# 方式3直接删除scores_cleanscores[~outliers]五、综合清洗流程实战defclean_student_data(filepath):标准的数据清洗流程# 1. 读取dfpd.read_csv(filepath,encodingutf-8)print(f原始数据{df.shape})print(f缺失值\n{df.isnull().sum()})# 2. 删除完全重复的行beforelen(df)dfdf.drop_duplicates()print(f删除重复{before-len(df)}行)# 3. 处理缺失值df[姓名]df[姓名].fillna(未知)df[年龄]df[年龄].fillna(df[年龄].median())df[成绩]df[成绩].fillna(0)# 4. 类型转换df[年龄]pd.to_numeric(df[年龄],errorscoerce).fillna(0).astype(int)df[成绩]pd.to_numeric(df[成绩],errorscoerce).fillna(0)# 5. 异常值处理成绩应该在 0-100 之间df[成绩]df[成绩].clip(0,100)# 6. 去除姓名前后的空格df[姓名]df[姓名].str.strip()# 7. 重置索引dfdf.reset_index(dropTrue)print(f清洗后数据{df.shape})returndf dfclean_student_data(raw_students.csv)六、今日学习总结学习内容掌握情况一句话要点发现缺失值✅ 重点df.isnull().sum()填充缺失值✅ 重点fillna(值/均值/中位数)删除缺失值✅ 理解dropna()重复值✅ 重点duplicated()/drop_duplicates()类型转换✅ 重点astype/pd.to_numeric/pd.to_datetime异常值检测✅ 了解IQR 法Q1 - 1.5*IQR清洗流程✅ 实战读→去重→填缺失→转类型→处理异常今日踩坑记录fillna不修改原 DataFramedf.fillna(0)返回新 DataFrame要df df.fillna(0)或传inplaceTrue。astype遇到非法值会报错先用pd.to_numeric(col, errorscoerce)把非法值转成 NaN再 fillna 填充最后 astype。重复数据不只是完全重复有时候需要按子集判断。比如同一个人的多条记录用drop_duplicates(subset[姓名])。七、明天学什么数据洗干净了明天开始玩数据——筛选、排序、分组、聚合。这是数据分析最有趣的部分。清洗数据就像做饭前洗菜——是最不炫酷但最必要的步骤。脏数据进去错误结论出来。第38天打卡完成。明天见本系列是个人学习笔记如有错误欢迎在评论区指正交流。