1. DataFrame数据清洗实战指南
在真实业务场景中,我们拿到的原始数据往往存在各种质量问题。根据统计,数据科学家60%的时间都花在数据清洗上。本文将带你系统掌握Pandas DataFrame的三大数据清洗场景:重复数据、缺失数据和异常值的处理技巧。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 重复数据处理全解析
2.1 重复数据检测方法
使用duplicated()方法可以快速识别重复行:
python复制# 标记所有重复行(保留第一个出现)
df.duplicated()
# 标记所有重复行(包括第一个出现)
df.duplicated(keep=False)
# 检查特定列的重复
df.duplicated(subset=['列名'])
注意:默认参数keep='first'会保留首次出现的行,这在去重时非常重要
2.2 重复数据删除方案
drop_duplicates()提供多种删除策略:
python复制# 基本去重(保留第一个)
df.drop_duplicates()
# 基于关键列去重
df.drop_duplicates(subset=['订单ID'])
# 保留最后出现的记录
df.drop_duplicates(keep='last')
# 完全删除所有重复项
df.drop_duplicates(keep=False)
实际业务中,我常遇到订单数据重复的情况。这时需要结合业务逻辑判断:是系统重复提交,还是正常的多商品订单?通常我会先用groupby+size()分析重复模式:
python复制df.groupby(['用户ID','下单时间']).size().sort_values(ascending=False)
3. 缺失数据处理深度攻略
3.1 缺失值检测技术
python复制# 查看每列缺失比例
df.isnull().mean().sort_values(ascending=False)
# 可视化缺失分布
import missingno as msno
msno.matrix(df)
3.2 缺失值处理五大策略
- 直接删除:
python复制# 删除全为NA的列
df.dropna(axis=1, how='all')
# 删除包含NA的行
df.dropna(thresh=5) # 至少保留5个非NA值
- 固定值填充:
python复制df.fillna({'年龄':0, '收入':df['收入'].median()})
- 统计值填充:
python复制# 按性别分组填充平均年龄
df['年龄'] = df.groupby('性别')['年龄'].transform(
lambda x: x.fillna(x.mean()))
- 插值法:
python复制# 时间序列的前向填充
df.fillna(method='ffill')
# 线性插值
df.interpolate()
- 模型预测填充:
python复制from sklearn.ensemble import RandomForestRegressor
# 构建预测模型填充缺失值
model = RandomForestRegressor()
known = df[df['年龄'].notnull()]
unknown = df[df['年龄'].isnull()]
model.fit(known[['收入','教育年限']], known['年龄'])
df.loc[df['年龄'].isnull(), '年龄'] = model.predict(unknown[['收入','教育年限']])
4. 异常值检测与处理
4.1 异常值检测方法
- 描述统计法:
python复制df.describe(percentiles=[.01, .05, .25, .5, .75, .95, .99])
- 3σ原则(正态分布数据):
python复制mean = df['金额'].mean()
std = df['金额'].std()
outliers = df[(df['金额'] < mean-3*std) | (df['金额'] > mean+3*std)]
- IQR方法:
python复制Q1 = df['年龄'].quantile(0.25)
Q3 = df['年龄'].quantile(0.75)
IQR = Q3 - Q1
df[(df['年龄'] < Q1-1.5*IQR) | (df['年龄'] > Q3+1.5*IQR)]
- 可视化检测:
python复制import seaborn as sns
sns.boxplot(x=df['体温'])
4.2 异常值处理方案
- 删除法:
python复制df = df[(df['金额'] >= lower_bound) & (df['金额'] <= upper_bound)]
- 盖帽法:
python复制df['金额'] = np.where(df['金额'] > upper, upper,
np.where(df['金额'] < lower, lower, df['金额']))
- 分箱处理:
python复制df['年龄分组'] = pd.cut(df['年龄'], bins=[0,18,35,60,100])
- 转换法:
python复制# 对数转换
df['金额_log'] = np.log1p(df['金额'])
5. 综合实战案例
5.1 电商数据清洗流程
python复制# 1. 加载数据
df = pd.read_csv('ecommerce_data.csv')
# 2. 处理重复订单
df = df.drop_duplicates(subset=['order_id'], keep='first')
# 3. 处理缺失值
df['payment_method'] = df['payment_method'].fillna('unknown')
df['discount'] = df.groupby('user_level')['discount'].transform(
lambda x: x.fillna(x.median()))
# 4. 处理异常金额
Q1 = df['amount'].quantile(0.05)
Q3 = df['amount'].quantile(0.95)
df = df[(df['amount'] >= Q1) & (df['amount'] <= Q3)]
# 5. 保存清洗结果
df.to_csv('cleaned_data.csv', index=False)
5.2 金融风控数据清洗要点
在金融领域,我总结出几个关键原则:
- 交易金额为0的记录需要单独分析
- 身份证号缺失的记录必须人工复核
- 异常时间戳(如未来日期)要重点检查
- 同一设备多账号的情况需要标记
python复制# 典型金融数据清洗代码
df['交易时间'] = pd.to_datetime(df['交易时间'])
df = df[df['交易时间'] <= pd.Timestamp.now()] # 过滤未来日期
# 处理金额异常
amt_mean = df['交易金额'].mean()
amt_std = df['交易金额'].std()
df = df[(df['交易金额'] <= amt_mean + 5*amt_std)]
6. 性能优化技巧
处理大型DataFrame时,这些技巧可以显著提升性能:
- 使用高效数据类型:
python复制# 转换object类型为category
df['城市'] = df['城市'].astype('category')
# 向下转换数值类型
df['年龄'] = pd.to_numeric(df['年龄'], downcast='integer')
- 分批处理:
python复制chunksize = 100000
for chunk in pd.read_csv('large_file.csv', chunksize=chunksize):
process(chunk)
- 使用eval()优化计算:
python复制df.eval('BMI = 体重/(身高**2)', inplace=True)
- 并行处理:
python复制from pandarallel import pandarallel
pandarallel.initialize()
df['新特征'] = df.parallel_apply(complex_function, axis=1)
7. 常见问题排查
- 内存不足问题:
- 解决方案:使用dtypes查看内存占用,转换数据类型
python复制df.info(memory_usage='deep')
- 去重后数据意外减少:
- 检查点:是否有多列组合去重?subset参数是否正确?
- 填充后数据分布异常:
- 诊断方法:比较填充前后的统计描述和分布图
- 异常值处理过度:
- 建议:保留原始数据列,只创建清洗后的新列
- 类别型缺失值处理:
- 最佳实践:为缺失值创建新类别(如"unknown")而非简单填充
在长期实践中我发现,数据清洗没有标准答案。比如电商场景中,同一用户短时间内多次浏览同一商品,是否算重复数据?这需要结合业务目标判断。我的经验法则是:清洗前后都要保存数据快照,并记录每个处理步骤的决策逻辑。
