Python数据清洗实战从脏数据到干净数据文章目录Python数据清洗实战从脏数据到干净数据前言一、缺失值检测与处理二、重复值处理三、数据类型转换四、apply函数灵活的数据变换五、异常值处理总结✅ 亮点总结适用场景扩展方向前言真实世界中的数据往往是脏的缺失值、重复记录、不一致的格式、异常值……这些问题如果不经过处理就投入分析轻则得出错误结论重则导致业务决策失误。数据分析和机器学习领域有一句名言“Garbage In, Garbage Out”——再好的模型也救不了脏数据。数据清洗是数据分析流程中耗时最长通常占70%-80%的时间但也是最关键的一步。本文聚焦pandas中数据清洗的六大核心技巧通过实战代码带你彻底搞定脏数据建立标准化的清洗流程。面试实录“你如何处理一份有50%缺失值的数据”——这不是一个简单的用均值填充的问题。面试官想听到的是你根据场景做决策的能力先分析缺失原因随机缺失还是系统缺失再判断能否通过其他字段推导最后选择删除、填充或保留为特殊类别的策略。这篇文章会帮你建立这种分析思维。一、缺失值检测与处理缺失值是数据清洗中最常见的问题。在pandas中None和np.nan都被视为缺失值。了解缺失值的分布——哪些列缺失、缺失比例是多少——是决定处理策略的前提。首先要了解缺失值的分布情况importpandasaspdimportnumpyasnp dfpd.DataFrame({姓名:[张三,李四,np.nan,赵六,孙七],年龄:[25,np.nan,28,35,np.nan],薪资:[15000,20000,np.nan,np.nan,22000],部门:[技术,销售,技术,None,销售],绩效:[85.5,90.0,78.0,88.5,None],})# 检测缺失值print(df.isnull().sum())# 每列缺失数量print(df.isnull().sum().sum())# 总缺失数量print(df[df[薪资].isnull()])# 薪资缺失的行isnull() vs notnull()isnull()返回布尔DataFrameTrue表示缺失配合sum()可以统计每列缺失数。isnull().sum().sum()统计全部缺失值总数。notnull()是isnull()的反向操作。在实际项目中通常先算缺失比例df.isnull().sum() / len(df)设定阈值决定是删除该列、删除该行还是填充。缺失值检测完成后有三种主流处理策略——删除、填充和插值。选择哪种策略取决于缺失比例和业务含义# 策略一删除缺失值df_dropnadf.dropna()# 删除含任何缺失值的行df_drop_alldf.dropna(howall)# 删除全为缺失值的行df_drop_subdf.dropna(subset[薪资])# 仅关注特定列df_drop_threshdf.dropna(thresh4)# 至少有4个非空值才保留# 策略二填充缺失值df[年龄]df[年龄].fillna(df[年龄].mean())# 均值填充df[部门]df[部门].fillna(未知)# 固定值填充df[薪资]df[薪资].fillna(methodffill)# 前向填充用前一行的值df[薪资]df[薪资].fillna(methodbfill)# 后向填充# 策略三插值法填充适合时间序列df[年龄]df[年龄].interpolate(methodlinear)删除 vs 填充的选择依据如果缺失比例很低5%删除缺失行通常损失不大但如果一组关键列如薪资缺失超过30%且无法从其他数据中推断删除该列可能更合适。填充策略中均值填充适合对称分布的数据中位数填充适合有偏分布因为中位数不受极端值影响众数填充适合分类数据。fillna的method参数ffill前向填充和bfill后向填充来自pandas中fillna的方法参数适合时间序列或有序数据——比如用前一天的温度填充今天的缺失值。interpolate()则做线性插值在两个已知值之间按距离比例估算中间值比简单的前向/后向填充更精确。二、重复值处理重复数据通常来自数据采集的多次提交、日志重复记录或多源数据合并时的交叉。去重看似简单实际需要判断什么是真正的重复——是按所有列完全相同才算重复还是某几个关键字段相同就算重复# 创建含重复的数据df.duplicated().sum()# 统计重复行数量print(df[df.duplicated()])# 查看重复行print(df[df.duplicated(keepFalse)])# 显示所有重复行# 删除重复值df_uniquedf.drop_duplicates()# 全部列重复才删除df_uniquedf.drop_duplicates(subset[姓名])# 按指定列去重df_uniquedf.drop_duplicates(subset[姓名],keepfirst)# 保留第一个df_uniquedf.drop_duplicates(subset[姓名],keeplast)# 保留最后一个df_uniquedf.drop_duplicates(subset[姓名],keepFalse)# 全不保留duplicated() 的工作原理默认情况下duplicated()将第一条出现的记录标记为False不重复后续相同的记录标记为True重复。keeplast则反过来保留最后一条。keepFalse把所有重复记录全部标记为True方便查看所有重复项。实际陷阱去重前一定要明确重复的定义。比如用户表中有两条张三但手机号不同——这可能是两个同名的人也可能是同一个人换了手机号。按姓名去重会误删数据。所以在实际业务中应使用ID、手机号等唯一标识字段来去重。三、数据类型转换正确的数据类型是高效计算的前提。pandas在读取数据时会自动推断类型但推断结果不一定准确——比如数字中混入了符号被误判为字符串类型。显式检查和转换类型是数据清洗的必要步骤。# 查看列类型print(df.dtypes)# 字符串转数值df[年龄]pd.to_numeric(df[年龄],errorscoerce)# 数值转类别df[部门]df[部门].astype(category)# 字符串转日期df[入职日期]pd.to_datetime(df[入职日期],format%Y-%m-%d)# 类型转换通用方法df[薪资]df[薪资].astype(float64)df[ID]df[ID].astype(int64)# 同时转换多列dfdf.astype({年龄:int64,薪资:float64,部门:category})# 处理转换异常df[年龄]df[年龄].apply(lambdax:pd.to_numeric(x,errorscoerce)ifisinstance(x,str)elsex)errors‘coerce’ 的作用当字符串无法转换时coerce会将其置为NaN而不是抛出异常。这是批量类型转换中最安全的参数。如果你希望严格校验——任何无法转换的值都应该报错提醒——可以使用errorsraise。category 类型的优势将重复值很多的列如部门只有技术/销售/市场三个值转换为category类型可以大幅节省内存可能减少90%以上同时加速分组和排序操作。对于大规模数据这是一个低成本高回报的优化。四、apply函数灵活的数据变换apply()是pandas极其强大的工具可以对每一行或每一列应用自定义函数# 对列应用函数df[薪资等级]df[薪资].apply(lambdax:高ifx20000else低)# 定义复杂函数defcategorize_salary(salary,dept):ifpd.isna(salary):return未知ifdept技术:return高ifsalary18000else中else:return高ifsalary20000else中df[薪资等级]df.apply(lambdarow:categorize_salary(row[薪资],row[部门]),axis1)# 批量文本清理df[姓名]df[姓名].apply(lambdax:str(x).strip()ifpd.notna(x)elsex)df[部门]df[部门].str.replace( ,).str.upper()axis1 vs axis0df.apply(func, axis1)按行应用每行作为一个Series传给funcaxis0按列应用。对于需要同时访问多列数据的场景如根据薪资和部门一起判断等级必须用axis1。但要注意axis1的性能比axis0差很多——大型DataFrame上慎用。str访问器df[部门].str是pandas为字符串列提供的专用访问器支持.replace()、.upper()、.split()等字符串方法。它在内部做向量化操作比apply(lambda x: x.upper())快很多。五、异常值处理# IQR方法检测异常值Q1df[薪资].quantile(0.25)Q3df[薪资].quantile(0.75)IQRQ3-Q1 lower_boundQ1-1.5*IQR upper_boundQ31.5*IQR outliersdf[(df[薪资]lower_bound)|(df[薪资]upper_bound)]print(f异常值数量:{len(outliers)})# 处理异常值df_clippeddf.copy()df_clipped[薪资]df_clipped[薪资].clip(lower_bound,upper_bound)# 截断# Z-Score方法fromscipyimportstats z_scoresstats.zscore(df[薪资].dropna())outliers_zdf[薪资].dropna()[abs(z_scores)3]IQR vs Z-Score的选择IQR方法基于四分位数对数据分布没有假设适合任意形状的分布是业界默认的稳健方法。Z-Score方法假设数据近似正态分布超过3个标准差视为异常。如果数据有偏分布如薪资往往右偏IQR的鲁棒性更好。异常值不一定都要删除有些异常值可能是业务最有价值的数据——比如高价值客户、峰值流量事件。处理方法除了删除和截断还可以单独拎出来做专项分析或者用对数变换压缩量级。不要机械地删除所有超出上下界的记录。defclean_data(df):标准化数据清洗流程dfdf.copy()# 1. 去除首尾空格str_colsdf.select_dtypes(includeobject).columnsforcolinstr_cols:df[col]df[col].str.strip()# 2. 处理缺失值df[数值列]df[数值列].fillna(df[数值列].median())df[类别列]df[类别列].fillna(未知)# 3. 去重beforelen(df)dfdf.drop_duplicates()print(f去重:{before}-{len(df)}条记录)# 4. 类型转换df[日期列]pd.to_datetime(df[日期列])# 5. 异常值处理numeric_colsdf.select_dtypes(includenumber).columnsforcolinnumeric_cols:mean,stddf[col].mean(),df[col].std()dfdf[df[col].between(mean-3*std,mean3*std)]returndf# 使用df_cleanclean_data(df)总结数据清洗没有万能公式但有一套标准化的方法论检测-决策删/填/转/去重/降噪-验证。本文涵盖了缺失值处理、重复值去重、类型转换、apply灵活变换、异常值处理的完整工具链。建议在每次分析前都执行一到两遍清洗流程并在清洗前后分别用df.describe()和df.info()对比数据特征确保清洗操作符合预期。✅ 亮点总结缺失值处理覆盖了删除dropna、填充fillna和插值三种策略应对不同业务场景drop_duplicates()配合subset/keep参数可精确控制去重逻辑astype()与pd.to_numeric(errorscoerce)是解决数据类型混乱的核心工具IQR法则Q1-1.5×IQR, Q31.5×IQR提供了统计意义上的异常值检测标准将清洗流程封装为可复用函数告别每次手写重复代码的低效工作方式适用场景数据分析项目预处理拿到一份脏数据后执行标准化清洗流程再做分析数据迁移与整合从多个外部系统导入数据时统一清洗格式、剔除无效记录自动化ETL任务编写定时脚本自动完成数据抽取、清洗、转换和入库扩展方向正则表达式进阶清洗学习str.extract()、str.replace()处理非结构化文本字段大规模数据清洗使用chunksize参数分块读取和清洗超大文件避免内存溢出推荐阅读下一篇《Python数据可视化matplotlib》将清洗后的数据用图表直观呈现
77_Python数据清洗实战技巧
发布时间:2026/7/6 8:09:34
Python数据清洗实战从脏数据到干净数据文章目录Python数据清洗实战从脏数据到干净数据前言一、缺失值检测与处理二、重复值处理三、数据类型转换四、apply函数灵活的数据变换五、异常值处理总结✅ 亮点总结适用场景扩展方向前言真实世界中的数据往往是脏的缺失值、重复记录、不一致的格式、异常值……这些问题如果不经过处理就投入分析轻则得出错误结论重则导致业务决策失误。数据分析和机器学习领域有一句名言“Garbage In, Garbage Out”——再好的模型也救不了脏数据。数据清洗是数据分析流程中耗时最长通常占70%-80%的时间但也是最关键的一步。本文聚焦pandas中数据清洗的六大核心技巧通过实战代码带你彻底搞定脏数据建立标准化的清洗流程。面试实录“你如何处理一份有50%缺失值的数据”——这不是一个简单的用均值填充的问题。面试官想听到的是你根据场景做决策的能力先分析缺失原因随机缺失还是系统缺失再判断能否通过其他字段推导最后选择删除、填充或保留为特殊类别的策略。这篇文章会帮你建立这种分析思维。一、缺失值检测与处理缺失值是数据清洗中最常见的问题。在pandas中None和np.nan都被视为缺失值。了解缺失值的分布——哪些列缺失、缺失比例是多少——是决定处理策略的前提。首先要了解缺失值的分布情况importpandasaspdimportnumpyasnp dfpd.DataFrame({姓名:[张三,李四,np.nan,赵六,孙七],年龄:[25,np.nan,28,35,np.nan],薪资:[15000,20000,np.nan,np.nan,22000],部门:[技术,销售,技术,None,销售],绩效:[85.5,90.0,78.0,88.5,None],})# 检测缺失值print(df.isnull().sum())# 每列缺失数量print(df.isnull().sum().sum())# 总缺失数量print(df[df[薪资].isnull()])# 薪资缺失的行isnull() vs notnull()isnull()返回布尔DataFrameTrue表示缺失配合sum()可以统计每列缺失数。isnull().sum().sum()统计全部缺失值总数。notnull()是isnull()的反向操作。在实际项目中通常先算缺失比例df.isnull().sum() / len(df)设定阈值决定是删除该列、删除该行还是填充。缺失值检测完成后有三种主流处理策略——删除、填充和插值。选择哪种策略取决于缺失比例和业务含义# 策略一删除缺失值df_dropnadf.dropna()# 删除含任何缺失值的行df_drop_alldf.dropna(howall)# 删除全为缺失值的行df_drop_subdf.dropna(subset[薪资])# 仅关注特定列df_drop_threshdf.dropna(thresh4)# 至少有4个非空值才保留# 策略二填充缺失值df[年龄]df[年龄].fillna(df[年龄].mean())# 均值填充df[部门]df[部门].fillna(未知)# 固定值填充df[薪资]df[薪资].fillna(methodffill)# 前向填充用前一行的值df[薪资]df[薪资].fillna(methodbfill)# 后向填充# 策略三插值法填充适合时间序列df[年龄]df[年龄].interpolate(methodlinear)删除 vs 填充的选择依据如果缺失比例很低5%删除缺失行通常损失不大但如果一组关键列如薪资缺失超过30%且无法从其他数据中推断删除该列可能更合适。填充策略中均值填充适合对称分布的数据中位数填充适合有偏分布因为中位数不受极端值影响众数填充适合分类数据。fillna的method参数ffill前向填充和bfill后向填充来自pandas中fillna的方法参数适合时间序列或有序数据——比如用前一天的温度填充今天的缺失值。interpolate()则做线性插值在两个已知值之间按距离比例估算中间值比简单的前向/后向填充更精确。二、重复值处理重复数据通常来自数据采集的多次提交、日志重复记录或多源数据合并时的交叉。去重看似简单实际需要判断什么是真正的重复——是按所有列完全相同才算重复还是某几个关键字段相同就算重复# 创建含重复的数据df.duplicated().sum()# 统计重复行数量print(df[df.duplicated()])# 查看重复行print(df[df.duplicated(keepFalse)])# 显示所有重复行# 删除重复值df_uniquedf.drop_duplicates()# 全部列重复才删除df_uniquedf.drop_duplicates(subset[姓名])# 按指定列去重df_uniquedf.drop_duplicates(subset[姓名],keepfirst)# 保留第一个df_uniquedf.drop_duplicates(subset[姓名],keeplast)# 保留最后一个df_uniquedf.drop_duplicates(subset[姓名],keepFalse)# 全不保留duplicated() 的工作原理默认情况下duplicated()将第一条出现的记录标记为False不重复后续相同的记录标记为True重复。keeplast则反过来保留最后一条。keepFalse把所有重复记录全部标记为True方便查看所有重复项。实际陷阱去重前一定要明确重复的定义。比如用户表中有两条张三但手机号不同——这可能是两个同名的人也可能是同一个人换了手机号。按姓名去重会误删数据。所以在实际业务中应使用ID、手机号等唯一标识字段来去重。三、数据类型转换正确的数据类型是高效计算的前提。pandas在读取数据时会自动推断类型但推断结果不一定准确——比如数字中混入了符号被误判为字符串类型。显式检查和转换类型是数据清洗的必要步骤。# 查看列类型print(df.dtypes)# 字符串转数值df[年龄]pd.to_numeric(df[年龄],errorscoerce)# 数值转类别df[部门]df[部门].astype(category)# 字符串转日期df[入职日期]pd.to_datetime(df[入职日期],format%Y-%m-%d)# 类型转换通用方法df[薪资]df[薪资].astype(float64)df[ID]df[ID].astype(int64)# 同时转换多列dfdf.astype({年龄:int64,薪资:float64,部门:category})# 处理转换异常df[年龄]df[年龄].apply(lambdax:pd.to_numeric(x,errorscoerce)ifisinstance(x,str)elsex)errors‘coerce’ 的作用当字符串无法转换时coerce会将其置为NaN而不是抛出异常。这是批量类型转换中最安全的参数。如果你希望严格校验——任何无法转换的值都应该报错提醒——可以使用errorsraise。category 类型的优势将重复值很多的列如部门只有技术/销售/市场三个值转换为category类型可以大幅节省内存可能减少90%以上同时加速分组和排序操作。对于大规模数据这是一个低成本高回报的优化。四、apply函数灵活的数据变换apply()是pandas极其强大的工具可以对每一行或每一列应用自定义函数# 对列应用函数df[薪资等级]df[薪资].apply(lambdax:高ifx20000else低)# 定义复杂函数defcategorize_salary(salary,dept):ifpd.isna(salary):return未知ifdept技术:return高ifsalary18000else中else:return高ifsalary20000else中df[薪资等级]df.apply(lambdarow:categorize_salary(row[薪资],row[部门]),axis1)# 批量文本清理df[姓名]df[姓名].apply(lambdax:str(x).strip()ifpd.notna(x)elsex)df[部门]df[部门].str.replace( ,).str.upper()axis1 vs axis0df.apply(func, axis1)按行应用每行作为一个Series传给funcaxis0按列应用。对于需要同时访问多列数据的场景如根据薪资和部门一起判断等级必须用axis1。但要注意axis1的性能比axis0差很多——大型DataFrame上慎用。str访问器df[部门].str是pandas为字符串列提供的专用访问器支持.replace()、.upper()、.split()等字符串方法。它在内部做向量化操作比apply(lambda x: x.upper())快很多。五、异常值处理# IQR方法检测异常值Q1df[薪资].quantile(0.25)Q3df[薪资].quantile(0.75)IQRQ3-Q1 lower_boundQ1-1.5*IQR upper_boundQ31.5*IQR outliersdf[(df[薪资]lower_bound)|(df[薪资]upper_bound)]print(f异常值数量:{len(outliers)})# 处理异常值df_clippeddf.copy()df_clipped[薪资]df_clipped[薪资].clip(lower_bound,upper_bound)# 截断# Z-Score方法fromscipyimportstats z_scoresstats.zscore(df[薪资].dropna())outliers_zdf[薪资].dropna()[abs(z_scores)3]IQR vs Z-Score的选择IQR方法基于四分位数对数据分布没有假设适合任意形状的分布是业界默认的稳健方法。Z-Score方法假设数据近似正态分布超过3个标准差视为异常。如果数据有偏分布如薪资往往右偏IQR的鲁棒性更好。异常值不一定都要删除有些异常值可能是业务最有价值的数据——比如高价值客户、峰值流量事件。处理方法除了删除和截断还可以单独拎出来做专项分析或者用对数变换压缩量级。不要机械地删除所有超出上下界的记录。defclean_data(df):标准化数据清洗流程dfdf.copy()# 1. 去除首尾空格str_colsdf.select_dtypes(includeobject).columnsforcolinstr_cols:df[col]df[col].str.strip()# 2. 处理缺失值df[数值列]df[数值列].fillna(df[数值列].median())df[类别列]df[类别列].fillna(未知)# 3. 去重beforelen(df)dfdf.drop_duplicates()print(f去重:{before}-{len(df)}条记录)# 4. 类型转换df[日期列]pd.to_datetime(df[日期列])# 5. 异常值处理numeric_colsdf.select_dtypes(includenumber).columnsforcolinnumeric_cols:mean,stddf[col].mean(),df[col].std()dfdf[df[col].between(mean-3*std,mean3*std)]returndf# 使用df_cleanclean_data(df)总结数据清洗没有万能公式但有一套标准化的方法论检测-决策删/填/转/去重/降噪-验证。本文涵盖了缺失值处理、重复值去重、类型转换、apply灵活变换、异常值处理的完整工具链。建议在每次分析前都执行一到两遍清洗流程并在清洗前后分别用df.describe()和df.info()对比数据特征确保清洗操作符合预期。✅ 亮点总结缺失值处理覆盖了删除dropna、填充fillna和插值三种策略应对不同业务场景drop_duplicates()配合subset/keep参数可精确控制去重逻辑astype()与pd.to_numeric(errorscoerce)是解决数据类型混乱的核心工具IQR法则Q1-1.5×IQR, Q31.5×IQR提供了统计意义上的异常值检测标准将清洗流程封装为可复用函数告别每次手写重复代码的低效工作方式适用场景数据分析项目预处理拿到一份脏数据后执行标准化清洗流程再做分析数据迁移与整合从多个外部系统导入数据时统一清洗格式、剔除无效记录自动化ETL任务编写定时脚本自动完成数据抽取、清洗、转换和入库扩展方向正则表达式进阶清洗学习str.extract()、str.replace()处理非结构化文本字段大规模数据清洗使用chunksize参数分块读取和清洗超大文件避免内存溢出推荐阅读下一篇《Python数据可视化matplotlib》将清洗后的数据用图表直观呈现