Skip to content

数据清洗案例

数据清洗是数据分析的重要环节,本案例展示如何处理真实世界的脏数据。

案例背景

假设我们有一份从多个来源收集的客户数据,包含各种数据质量问题。

创建脏数据

python
import pandas as pd
import numpy as np

# 创建包含各种问题的脏数据
dirty_data = {
    '客户ID': ['C001', 'C002', 'C003', 'C001', 'C004', 'C005', 'C006', None],
    '姓名': [' 张三 ', '李四', '王五', ' 张三 ', '赵六', '孙七', '周八', '吴九'],
    '年龄': [25, -5, 150, 25, 30, None, 35, '未知'],
    '邮箱': ['zhangsan@email.com', 'lisi@test.com', None, 'zhangsan@email.com', 
             'invalid_email', 'wangwu@demo.com', 'zhaoliu@', 'qianqi@company.com'],
    '注册日期': ['2024-01-15', '2024/02/20', '2024-03-10', '2024-01-15', 
                '2024-04-05', None, '2024-05-01', '2024-06-12'],
    '消费金额': ['1000.50', '2000', 'invalid', '1000.50', '3500.75', '1500.25', None, '2800.00']
}

df = pd.DataFrame(dirty_data)
print("原始数据:")
print(df)
print(f"\n数据形状: {df.shape}")

问题诊断

python
print("\n=== 数据质量问题诊断 ===")

# 1. 缺失值
print("\n1. 缺失值统计:")
print(df.isnull().sum())

# 2. 重复值
print(f"\n2. 重复行数: {df.duplicated().sum()}")

# 3. 数据类型
print("\n3. 数据类型:")
print(df.dtypes)

# 4. 异常值
print("\n4. 年龄异常值检查:")
print(df['年龄'].value_counts())

# 5. 邮箱格式
print("\n5. 邮箱格式检查:")
print(df['邮箱'].value_counts())

数据清洗流程

步骤1:处理缺失值

python
print("\n=== 步骤1: 处理缺失值 ===")

# 删除客户ID为空的行
df_clean = df.dropna(subset=['客户ID'])

# 填充年龄缺失值(使用中位数)
# 首先将非数字转为NaN
df_clean['年龄'] = pd.to_numeric(df_clean['年龄'], errors='coerce')
age_median = df_clean['年龄'].median()
df_clean['年龄'] = df_clean['年龄'].fillna(age_median)

# 填充注册日期(使用前一个有效值)
df_clean['注册日期'] = df_clean['注册日期'].fillna(method='ffill')

# 填充消费金额(使用0)
df_clean['消费金额'] = df_clean['消费金额'].fillna('0')

print("处理后缺失值:")
print(df_clean.isnull().sum())

步骤2:处理重复值

python
print("\n=== 步骤2: 处理重复值 ===")

# 查看重复行
duplicates = df_clean[df_clean.duplicated(subset=['客户ID'], keep=False)]
print("重复客户:")
print(duplicates)

# 删除重复行(保留第一个)
df_clean = df_clean.drop_duplicates(subset=['客户ID'], keep='first')
print(f"\n去重后数据量: {len(df_clean)}")

步骤3:处理数据类型

python
print("\n=== 步骤3: 处理数据类型 ===")

# 转换消费金额为数值
df_clean['消费金额'] = pd.to_numeric(df_clean['消费金额'], errors='coerce')
df_clean['消费金额'] = df_clean['消费金额'].fillna(0)

# 转换注册日期为日期类型
df_clean['注册日期'] = pd.to_datetime(df_clean['注册日期'], errors='coerce')

print("处理后数据类型:")
print(df_clean.dtypes)

步骤4:处理异常值

python
print("\n=== 步骤4: 处理异常值 ===")

# 处理年龄异常值(假设合理范围为18-100)
df_clean.loc[df_clean['年龄'] < 18, '年龄'] = 18
df_clean.loc[df_clean['年龄'] > 100, '年龄'] = 100

print("年龄统计:")
print(df_clean['年龄'].describe())

步骤5:处理字符串

python
print("\n=== 步骤5: 处理字符串 ===")

# 去除姓名中的空格
df_clean['姓名'] = df_clean['姓名'].str.strip()

# 统一邮箱格式(小写)
df_clean['邮箱'] = df_clean['邮箱'].str.lower()

# 验证邮箱格式
def validate_email(email):
    import re
    if pd.isna(email):
        return False
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, str(email)))

df_clean['邮箱有效'] = df_clean['邮箱'].apply(validate_email)

print("邮箱验证结果:")
print(df_clean[['邮箱', '邮箱有效']])

步骤6:标准化数据

python
print("\n=== 步骤6: 标准化数据 ===")

# 统一日期格式
df_clean['注册日期'] = df_clean['注册日期'].dt.strftime('%Y-%m-%d')

# 格式化消费金额
df_clean['消费金额'] = df_clean['消费金额'].round(2)

print("清洗后的数据:")
print(df_clean)

清洗结果验证

python
print("\n=== 清洗结果验证 ===")

print("最终数据形状:", df_clean.shape)
print("\n数据类型:")
print(df_clean.dtypes)
print("\n缺失值:")
print(df_clean.isnull().sum())
print("\n数值列统计:")
print(df_clean.describe())

清洗流程总结

python
print("\n" + "="*50)
print("数据清洗流程总结")
print("="*50)
print("""
1. 缺失值处理
   - 删除关键字段缺失的记录
   - 使用合理值填充其他缺失值

2. 重复值处理
   - 识别重复记录
   - 根据业务规则保留或合并

3. 数据类型转换
   - 确保数值列为数值类型
   - 确保日期列为日期类型

4. 异常值处理
   - 识别超出合理范围的值
   - 使用边界值或统计值替换

5. 字符串处理
   - 去除多余空格
   - 统一大小写
   - 验证格式(如邮箱)

6. 数据标准化
   - 统一日期格式
   - 统一数值精度
   - 确保数据一致性
""")

数据清洗是数据分析的基础,高质量的清洗可以确保分析结果的准确性。