Pandas数据清洗实战:深度解析缺失值与重复数据处理(五)

一、数据清洗:从理论到实践的全面认知

1.1 数据质量问题的影响矩阵

在真实业务场景中,数据质量问题会导致严重后果:

  • 缺失值:降低模型训练效果(准确率下降30%-50%)
  • 重复数据:导致统计指标虚高(如用户数虚增)
  • 异常值:影响均值计算(单个异常值可导致均值偏移20%)

1.2 数据清洗的完整生命周期

缺失值
重复值
异常值
原始数据
质量评估
问题诊断
填充/删除
去重
修正
验证
清洗后数据

二、环境配置与数据探索

2.1 环境初始化

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

# 设置全局样式
plt.style.use('ggplot')
pd.set_option('display.float_format', lambda x: '%.2f' % x)
np.random.seed(42)

2.2 数据加载与元分析

# 加载泰坦尼克数据集
url = "https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/stuff/titanic.csv"
titanic = pd.read_csv(url)

# 数据指纹分析
print(f"数据集维度: {titanic.shape}")
print("\n字段类型分布:")
print(titanic.dtypes.value_counts())

# 生成数据快照
titanic.sample(5, random_state=42)

2.3 缺失值三维分析

def missing_value_analysis(df):
    # 计算缺失统计
    missing_stats = pd.DataFrame({
        '缺失数量': df.isnull().sum(),
        '缺失占比': df.isnull().mean().round(4)*100,
        '数据类型': df.dtypes
    }).sort_values('缺失占比', ascending=False)
    
    # 可视化展示
    plt.figure(figsize=(10,6))
    sns.barplot(x=missing_stats['缺失占比'], y=missing_stats.index, palette='Reds_r')
    plt.title('字段缺失比例分布')
    plt.xlabel('缺失比例 (%)')
    plt.show()
    
    return missing_stats

missing_report = missing_value_analysis(titanic)

三、缺失值处理:策略与实战

3.1 缺失检测:isnull()的底层机制

Pandas使用NaN(Not a Number)表示缺失值,其特性包括:

  • np.nan == np.nan 返回False
  • np.isnan() 是唯一检测方法
  • 在计算中具有传染性(任何涉及NaN的操作结果都是NaN)

isnull()源码解析

def isnull(obj):
    if isinstance(obj, pd.Series):
        return obj.isna()
    return _isnan(obj)  # 调用NumPy的isnan方法

3.2 数据删除:dropna()的进阶用法

# 案例1:动态阈值删除
threshold = int(0.8 * len(titanic))  # 保留至少80%完整的行
titanic_thresh = titanic.dropna(thresh=threshold)

# 案例2:分组删除(按舱位分组处理)
titanic_grouped = titanic.groupby('Pclass').apply(
    lambda x: x.dropna(subset=['Age', 'Fare']))

# 案例3:时间序列前后填充
titanic.sort_values('Ticket', inplace=True)
titanic_ffill = titanic.dropna(subset=['Cabin']).ffill()

3.3 数据填充:fillna()的二十种策略

基础填充法
# 均值填充(适合正态分布)
age_mean = titanic['Age'].mean()
titanic['Age'].fillna(age_mean, inplace=True)

# 众数填充(分类变量)
embarked_mode = titanic['Embarked'].mode()[0]
titanic['Embarked'].fillna(embarked_mode, inplace=True)
高级填充法
# 随机森林填充(机器学习方法)
from sklearn.ensemble import RandomForestRegressor

# 拆分有/无缺失的数据
age_data = titanic[['Age', 'Pclass', 'Fare', 'SibSp']]
known_age = age_data[age_data['Age'].notnull()]
unknown_age = age_data[age_data['Age'].isnull()]

# 训练预测模型
X = known_age.drop('Age', axis=1)
y = known_age['Age']
model = RandomForestRegressor(n_estimators=100)
model.fit(X, y)

# 预测并填充
predicted_age = model.predict(unknown_age.drop('Age', axis=1))
titanic.loc[titanic['Age'].isnull(), 'Age'] = predicted_age
填充策略对照表
方法适用场景优点缺点
前向填充(ffill)时间序列数据保留趋势可能传播错误
多重插补(MICE)高维数据保持变量关系计算成本高
KNN填充局部相关数据利用相似样本需要特征缩放
深度学习填充复杂模式数据捕捉非线性关系需要大量数据

四、重复数据处理:从检测到根治

4.1 重复检测:duplicated()的运作原理

def duplicated(df, subset=None, keep='first'):
    # 生成哈希指纹
    if subset is not None:
        df = df[subset]
    hashed = pd.util.hash_pandas_object(df)
    # 标记重复项
    duplicated_mask = hashed.duplicated(keep=keep)
    return duplicated_mask

4.2 智能去重:drop_duplicates()的工业级应用

# 案例1:保留最新记录
titanic_sorted = titanic.sort_values('Ticket', ascending=False)
titanic_dedup = titanic_sorted.drop_duplicates(subset=['PassengerId'], keep='first')

# 案例2:多条件复合主键
complex_keys = ['Name', 'Age', 'Ticket']
titanic_complex = titanic.drop_duplicates(subset=complex_keys)

# 案例3:基于时间窗口去重
titanic['BookingDate'] = pd.to_datetime(titanic['Ticket'].str[:4], errors='coerce')
window = pd.DateOffset(days=7)
titanic_rolling = titanic.set_index('BookingDate').sort_index()
titanic_clean = titanic_rolling.groupby('PassengerId').apply(
    lambda x: x.drop_duplicates(subset=['Cabin'], keep='last'))

五、泰坦尼克数据集全流程清洗实战

5.1 清洗路线图

原始数据
删除无效列
年龄填充
票价修正
舱位处理
重复数据删除
数据验证

5.2 分步实施代码

# 步骤1:删除低价值列
cols_to_drop = ['Name', 'Ticket', 'Cabin']
titanic_clean = titanic.drop(cols_to_drop, axis=1)

# 步骤2:分舱位填充年龄
class_age = titanic_clean.groupby('Pclass')['Age'].transform(
    lambda x: x.fillna(x.median()))
titanic_clean['Age'] = class_age

# 步骤3:处理票价异常值
Q1 = titanic_clean['Fare'].quantile(0.25)
Q3 = titanic_clean['Fare'].quantile(0.75)
IQR = Q3 - Q1
fare_cap = Q3 + 1.5 * IQR
titanic_clean['Fare'] = titanic_clean['Fare'].clip(upper=fare_cap)

# 步骤4:高级去重
titanic_clean = titanic_clean.sort_values('PassengerId', ascending=False)
titanic_clean = titanic_clean.drop_duplicates(
    subset=['PassengerId', 'Embarked'], 
    keep='first')

# 步骤5:最终验证
assert titanic_clean.duplicated().sum() == 0, "存在重复记录!"
assert titanic_clean.isnull().sum().sum() == 0, "存在缺失值!"
print("数据清洗完成,最终维度:", titanic_clean.shape)

六、工业级数据清洗最佳实践

6.1 内存优化技巧

def optimize_memory(df):
    # 类型转换字典
    type_map = {
        'int64': 'int32',
        'float64': 'float32',
        'object': 'category'
    }
    
    # 遍历转换
    for col in df.columns:
        col_type = str(df[col].dtype)
        if col_type in type_map:
            df[col] = df[col].astype(type_map[col_type])
    
    # 时间类型转换        
    datetime_cols = df.select_dtypes(include=['datetime']).columns
    for col in datetime_cols:
        df[col] = pd.to_datetime(df[col], format='%Y%m%d')
    
    return df

titanic_opt = optimize_memory(titanic_clean)
print("内存节省比例:", 
      (1 - titanic_opt.memory_usage().sum() / titanic.memory_usage().sum())*100)

6.2 自动化质量监控

class DataQualityMonitor:
    def __init__(self, df):
        self.df = df
    
    def check_missing(self, threshold=0.05):
        missing_rate = self.df.isnull().mean()
        violations = missing_rate[missing_rate > threshold]
        return violations.empty
    
    def check_duplicates(self, keys=None):
        if keys is None:
            return self.df.duplicated().sum() == 0
        else:
            return self.df.duplicated(subset=keys).sum() == 0
    
    def generate_report(self):
        report = {
            'missing_pass': self.check_missing(),
            'duplicates_pass': self.check_duplicates(['PassengerId']),
            'shape': self.df.shape
        }
        return report

dqm = DataQualityMonitor(titanic_clean)
print("数据质量报告:", dqm.generate_report())

七、扩展:分布式数据清洗实战

7.1 Dask并行处理

import dask.dataframe as dd

# 转换Pandas DataFrame为Dask DataFrame
ddf = dd.from_pandas(titanic, npartitions=4)

# 并行处理函数
def clean_chunk(df):
    df = df.dropna(subset=['Age'])
    df = df.drop_duplicates()
    return df

# 应用并执行
ddf_clean = ddf.map_partitions(clean_chunk)
result = ddf_clean.compute()

7.2 基于Spark的大规模清洗

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("TitanicCleaning").getOrCreate()
df_spark = spark.read.csv("titanic.csv", header=True, inferSchema=True)

# Spark清洗操作
df_clean = df_spark.dropna(how='any', subset=['Age']) \
                   .dropDuplicates(['PassengerId']) \
                   .filter("Fare < 500") 

df_clean.show(5)

八、总结

8.1 关键知识点回顾

  • 缺失值处理三原则:删除高缺失率字段、合理填充关键字段、保留缺失标记
  • 重复数据四维度检测:完全重复、业务主键重复、时间窗口重复、相似度重复
  • 工业级清洗四要素:可追溯性、可扩展性、自动化验证、文档完整性

8.2 常见问题解答

Q:如何处理非结构化数据中的缺失值?
A:对于文本数据可以使用特殊标记(如[UNK]),图像数据可以使用插值修复

Q:大数据场景下如何加速数据清洗?
A:采用分块处理、Dask/Spark并行计算、GPU加速等技术

Q:数据清洗后如何保持可追溯性?
A:使用数据版本控制工具(如DVC)、记录清洗日志、保存中间结果


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值