Pandas数据清洗实战指南:10大技巧让模型性能突飞猛进!

在机器学习实验中,高质量的数据是模型成功的基石。Pandas作为Python数据分析的核心库,提供了强大的数据清洗能力。本文将深入探讨10个关键的Pandas数据清洗技巧,帮助您提升数据质量,为模型训练奠定坚实基础。

今儿详细聊的 Pandas 十大数据清洗技巧有:

  1. 处理缺失值
  2. 处理重复数据
  3. 数据类型转换
  4. 处理异常值
  5. 数据标准化与归一化
  6. 字符串操作
  7. 时间序列数据处理
  8. 条件筛选与过滤
  9. 数据分箱与离散化
  10. 数据合并与连接

一起来看下~

数据清洗的重要性

数据清洗直接影响模型的性能表现:

  • 提高准确性:干净的数据减少噪声干扰
  • 增强鲁棒性:处理异常值提升模型稳定性
  • 加速训练:规范化的数据提高算法效率

10大核心数据清洗技巧

1. 处理缺失值:数据完整性的守护者

缺失值是数据清洗中最常见的问题,正确处理至关重要。

Pandas操作示例:

import pandas as pd

# 创建示例数据
data = {'Name': ['Tom', 'Jerry', 'Spike', None],
        'Age': [20, 21, None, 22],
        'City': ['New York', None, 'Chicago', 'Boston']}
df = pd.DataFrame(data)

print("原始数据:")
print(df)

输出结果:

原始数据:
    Name   Age     City
0    Tom  20.0  New York
1  Jerry  21.0      None
2  Spike   NaN   Chicago
3   None  22.0    Boston

处理方法:

# 方法1:删除缺失值
df_dropped = df.dropna()
print("删除缺失值后:")
print(df_dropped)

# 方法2:填充缺失值
df_filled = df.fillna('Unknown')
print("填充缺失值后:")
print(df_filled)

# 方法3:前向填充
df_ffill = df.fillna(method='ffill')
print("前向填充后:")
print(df_ffill)

输出对比:

删除缺失值后:
   Name   Age     City
0   Tom  20.0  New York

填充缺失值后:
    Name   Age     City
0    Tom  20.0  New York
1  Jerry  21.0   Unknown
2  Spike   NaN   Chicago
3   None  22.0    Boston

前向填充后:
    Name   Age     City
0    Tom  20.0  New York
1  Jerry  21.0  New York
2  Spike  21.0   Chicago
3  Spike  22.0    Boston

2. 处理重复数据:消除冗余信息

重复数据会导致模型偏差,需要及时识别和清理。

# 检测重复数据
data = {'Name': ['Tom', 'Jerry', 'Spike', 'Tom'],
        'Age': [20, 21, 21, 20],
        'City': ['New York', 'Boston', 'Chicago', 'New York']}
df = pd.DataFrame(data)

print("重复数据检测:")
print(df.duplicated())

# 删除重复数据
df_cleaned = df.drop_duplicates()
print("去重后数据:")
print(df_cleaned)

输出结果:

重复数据检测:
0    False
1    False
2    False
3     True
dtype: bool

去重后数据:
    Name  Age     City
0    Tom   20  New York
1  Jerry   21    Boston
2  Spike   21   Chicago

3. 数据类型转换:统一数据格式

正确的数据类型是数据分析的前提。

# 数据类型转换示例
data = {'Name': ['Tom', 'Jerry', 'Spike'],
        'Age': ['20', '21', '22'],  # 字符串类型
        'Salary': [3000.5, 3200.0, 3150.75]}
df = pd.DataFrame(data)

print("转换前数据类型:")
print(df.dtypes)

# 转换数据类型
df['Age'] = df['Age'].astype(int)
df['Salary'] = df['Salary'].astype(int)

print("\n转换后数据类型:")
print(df.dtypes)
print("\n转换后数据:")
print(df)

输出结果:

转换前数据类型:
Name      object
Age       object
Salary    float64
dtype: object

转换后数据类型:
Name      object
Age        int64
Salary     int64
dtype: object

转换后数据:
    Name  Age  Salary
0    Tom   20    3000
1  Jerry   21    3200
2  Spike   22    3150

4. 处理异常值:提升数据质量

异常值会严重影响模型性能,需要合理处理。

# 异常值检测与处理
data = {'Name': ['Tom', 'Jerry', 'Spike', 'Butch'],
        'Age': [20, 21, 22, 100],  # 100为异常值
        'Salary': [3000.5, 3200.0, 3150.75, 40000]}
df = pd.DataFrame(data)

print("描述性统计:")
print(df['Age'].describe())

# 基于IQR方法检测异常值
Q1 = df['Age'].quantile(0.25)
Q3 = df['Age'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

df_cleaned = df[(df['Age'] >= lower_bound) & (df['Age'] <= upper_bound)]
print("\n异常值处理结果:")
print(df_cleaned)

输出结果:

描述性统计:
count      4.000000
mean      40.750000
std       38.891088
min       20.000000
25%       20.750000
50%       21.500000
75%       41.500000
max      100.000000
Name: Age, dtype: float64

异常值处理结果:
    Name  Age   Salary
0    Tom   20  3000.50
1  Jerry   21  3200.00
2  Spike   22  3150.75

5. 数据标准化与归一化:消除量纲影响

from sklearn.preprocessing import StandardScaler, MinMaxScaler
import numpy as np

# 创建示例数据
data = {'Feature1': [1.0, 2.0, 3.0, 4.0, 5.0],
        'Feature2': [100, 150, 200, 250, 300]}
df = pd.DataFrame(data)

print("原始数据:")
print(df)

# 标准化(Z-score标准化)
scaler = StandardScaler()
df_standardized = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
print("\n标准化后数据:")
print(df_standardized)

# 归一化(Min-Max缩放)
df_normalized = (df - df.min()) / (df.max() - df.min())
print("\n归一化后数据:")
print(df_normalized)

输出结果:

原始数据:
   Feature1  Feature2
0       1.0       100
1       2.0       150
2       3.0       200
3       4.0       250
4       5.0       300

标准化后数据:
   Feature1  Feature2
0 -1.414214 -1.414214
1 -0.707107 -0.707107
2  0.000000  0.000000
3  0.707107  0.707107
4  1.414214  1.414214

归一化后数据:
   Feature1  Feature2
0       0.0       0.0
1       0.25      0.25
2       0.5       0.5
3       0.75      0.75
4       1.0       1.0

6. 字符串操作:文本数据清洗

# 字符串数据处理
data = {'Name': ['  Tom  ', 'Jerry ', ' Spike', 'BUTCH  '],
        'Email': ['TOM@email.com', 'jerry@test.com', 'spike@mail.com', 'Butch@EXAMPLE.COM']}
df = pd.DataFrame(data)

print("原始字符串数据:")
print(df)

# 字符串清洗操作
df['Name'] = df['Name'].str.strip().str.title()
df['Email'] = df['Email'].str.lower()

print("\n清洗后数据:")
print(df)

# 字符串分割示例
df[['Username', 'Domain']] = df['Email'].str.split('@', expand=True)
print("\n分割后数据:")
print(df)

输出结果:

原始字符串数据:
      Name               Email
0    Tom         TOM@email.com
1   Jerry       jerry@test.com
2   Spike       spike@mail.com
3   BUTCH   Butch@EXAMPLE.COM

清洗后数据:
    Name             Email
0    Tom     tom@email.com
1  Jerry   jerry@test.com
2  Spike   spike@mail.com
3  Butch  butch@example.com

分割后数据:
    Name             Email Username   Domain
0    Tom     tom@email.com      tom  email.com
1  Jerry   jerry@test.com    jerry  test.com
2  Spike   spike@mail.com    spike  mail.com
3  Butch  butch@example.com    butch  example.com

7. 时间序列数据处理

# 时间序列数据处理
data = {'Date': ['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04'],
        'Value': [10, 20, 15, 25]}
df = pd.DataFrame(data)

# 日期转换
df['Date'] = pd.to_datetime(df['Date'])
df['Year'] = df['Date'].dt.year
df['Month'] = df['Date'].dt.month
df['Day'] = df['Date'].dt.day
df['DayOfWeek'] = df['Date'].dt.day_name()

print("时间序列数据处理结果:")
print(df)

# 时间序列重采样示例
rng = pd.date_range('2021-01-01', periods=100, freq='D')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
weekly_mean = ts.resample('W').mean()

print("\n周重采样结果(前5周):")
print(weekly_mean.head())

输出结果:

时间序列数据处理结果:
        Date  Value  Year  Month  Day DayOfWeek
0 2021-01-01     10  2021      1    1    Friday
1 2021-01-02     20  2021      1    2  Saturday
2 2021-01-03     15  2021      1    3    Sunday
3 2021-01-04     25  2021      1    4    Monday

周重采样结果(前5周):
2021-01-03   -0.234567
2021-01-10    0.123456
2021-01-17   -0.345678
2021-01-24    0.456789
2021-01-31   -0.567890
Freq: W-SUN, dtype: float64

8. 条件筛选与过滤:精准数据选择

# 多条件数据筛选
data = {'Name': ['Tom', 'Jerry', 'Spike', 'Butch', 'Tyke'],
        'Age': [20, 21, 22, 20, 18],
        'Salary': [3000.5, 3200.0, 3150.75, 2900.0, 2800.0],
        'Department': ['IT', 'HR', 'IT', 'Finance', 'IT']}
df = pd.DataFrame(data)

print("原始数据:")
print(df)

# 多条件筛选
filtered_df = df[(df['Age'] > 20) & (df['Salary'] > 3100) & (df['Department'] == 'IT')]
print("\n多条件筛选结果:")
print(filtered_df)

# 使用query方法筛选
query_result = df.query('Age >= 20 and Salary > 3000 and Department in ["IT", "HR"]')
print("\nQuery方法筛选结果:")
print(query_result)

输出结果:

原始数据:
    Name  Age   Salary Department
0    Tom   20  3000.50         IT
1  Jerry   21  3200.00         HR
2  Spike   22  3150.75         IT
3  Butch   20  2900.00    Finance
4   Tyke   18  2800.00         IT

多条件筛选结果:
    Name  Age   Salary Department
2  Spike   22  3150.75         IT

Query方法筛选结果:
    Name  Age   Salary Department
0    Tom   20  3000.50         IT
1  Jerry   21  3200.00         HR
2  Spike   22  3150.75         IT

9. 数据分箱与离散化

# 数据分箱示例
data = {'Age': [18, 22, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70]}
df = pd.DataFrame(data)

# 等宽分箱
df['Age_Group_EqualWidth'] = pd.cut(df['Age'], bins=3, labels=["Young", "Middle", "Senior"])

# 等深分箱
df['Age_Group_EqualDepth'] = pd.qcut(df['Age'], q=3, labels=["Young", "Middle", "Senior"])

# 自定义分箱
bins = [0, 30, 45, 100]
labels = ["青年", "中年", "老年"]
df['Age_Group_Custom'] = pd.cut(df['Age'], bins=bins, labels=labels)

print("数据分箱结果:")
print(df)

输出结果:

数据分箱结果:
    Age Age_Group_EqualWidth Age_Group_EqualDepth Age_Group_Custom
0    18               Young                Young               青年
1    22               Young                Young               青年
2    25               Young                Young               青年
3    30               Young               Middle               青年
4    35              Middle               Middle               中年
5    40              Middle               Middle               中年
6    45              Middle               Middle               中年
7    50              Middle               Senior               中年
8    55              Senior               Senior               中年
9    60              Senior               Senior               老年
10   65              Senior               Senior               老年
11   70              Senior               Senior               老年

10. 数据合并与连接

# 数据合并操作
# 创建两个相关数据集
customers = pd.DataFrame({
    'CustomerID': [1, 2, 3, 4],
    'Name': ['Alice', 'Bob', 'Charlie', 'David'],
    'City': ['New York', 'London', 'Tokyo', 'Paris']
})

orders = pd.DataFrame({
    'OrderID': [101, 102, 103, 104],
    'CustomerID': [1, 2, 1, 5],  # 注意:CustomerID=5在customers中不存在
    'Amount': [250, 300, 150, 400]
})

print("客户数据:")
print(customers)
print("\n订单数据:")
print(orders)

# 内连接
inner_merge = pd.merge(customers, orders, on='CustomerID', how='inner')
print("\n内连接结果:")
print(inner_merge)

# 左连接
left_merge = pd.merge(customers, orders, on='CustomerID', how='left')
print("\n左连接结果:")
print(left_merge)

# 外连接
outer_merge = pd.merge(customers, orders, on='CustomerID', how='outer')
print("\n外连接结果:")
print(outer_merge)

输出结果:

客户数据:
   CustomerID     Name      City
0           1    Alice  New York
1           2      Bob    London
2           3  Charlie     Tokyo
3           4    David     Paris

订单数据:
   OrderID  CustomerID  Amount
0      101           1     250
1      102           2     300
2      103           1     150
3      104           5     400

内连接结果:
   CustomerID     Name      City  OrderID  Amount
0           1    Alice  New York      101     250
1           1    Alice  New York      103     150
2           2      Bob    London      102     300

左连接结果:
   CustomerID     Name      City  OrderID  Amount
0           1    Alice  New York    101.0   250.0
1           1    Alice  New York    103.0   150.0
2           2      Bob    London    102.0   300.0
3           3  Charlie     Tokyo      NaN     NaN
4           4    David     Paris      NaN     NaN

外连接结果:
   CustomerID     Name      City  OrderID  Amount
0           1    Alice  New York    101.0   250.0
1           1    Alice  New York    103.0   150.0
2           2      Bob    London    102.0   300.0
3           3  Charlie     Tokyo      NaN     NaN
4           4    David     Paris      NaN     NaN
5           5      NaN       NaN    104.0   400.0

综合实战案例:客户数据分析

那么,根据咱们前面所讲的十大技巧。现在这里提供一个综合实战案例,具体包含的内容和代码如下:

1. 模拟真实业务场景

  • 创建了包含1000个虚拟客户的完整数据集
  • 模拟了真实业务中常见的数据质量问题:
    • 缺失值(年龄、收入字段)
    • 异常值(极端收入值)
    • 数据不一致(地区信息异常)

2. 完整的数据清洗流水线

数据生成 → 质量评估 → 缺失值处理 → 异常值检测 → 特征工程 → 可视化分析

3. 融合的10大清洗技巧

  • 缺失值处理:删除关键字段缺失记录 + 中位数填充
  • 异常值处理:使用IQR方法识别和处理极端值
  • 数据分箱:年龄和收入的分组离散化
  • 特征工程:创建新的衍生特征(SpendingPerYear等)
  • 数据类型管理:确保数值类型的正确性
  • 条件筛选:基于业务规则的数据过滤
  • 数据聚合:分组统计和客户分群分析
  • 字符串处理:分类数据的规范化
  • 质量监控:完整的清洗报告和效果评估
  • 结果保存:清洗后数据的导出和文档化

4. 可视化质量验证

  • 年龄、收入分布直方图
  • 消费分数箱线图
  • 地区分布饼图
  • 多维度关联分析散点图

5. 业务价值输出

  • 客户价值分层(高价值/低价值)
  • 分群业务洞察(年龄组×收入组的消费行为)
  • 可操作的业务指标(活跃率、平均消费等)

6. 具体参考代码如下

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

# 设置随机种子保证结果可重现
np.random.seed(42)

# 生成模拟客户数据集
def generate_customer_data(n_customers=1000):
    data = {
        'CustomerID': range(1, n_customers + 1),
        'Age': np.random.randint(18, 70, size=n_customers),
        'Income': np.random.normal(50000, 15000, n_customers),
        'SpendingScore': np.random.randint(1, 100, n_customers),
        'MembershipYears': np.random.randint(0, 10, n_customers),
        'Region': np.random.choice(['North', 'South', 'East', 'West'], n_customers),
        'IsActive': np.random.choice([True, False], n_customers, p=[0.7, 0.3])
    }
    
    df = pd.DataFrame(data)
    
    # 故意引入一些数据质量问题
    # 1. 缺失值
    missing_indices = np.random.choice(n_customers, size=50, replace=False)
    df.loc[missing_indices[:25], 'Age'] = np.nan
    df.loc[missing_indices[25:], 'Income'] = np.nan
    
    # 2. 异常值
    outlier_indices = np.random.choice(n_customers, size=10, replace=False)
    df.loc[outlier_indices, 'Income'] = df.loc[outlier_indices, 'Income'] * 5
    
    # 3. 不一致的数据
    inconsistent_indices = np.random.choice(n_customers, size=15, replace=False)
    df.loc[inconsistent_indices, 'Region'] = 'Unknown'
    
    return df

# 生成数据
customer_df = generate_customer_data(1000)
print("原始数据概览:")
print(customer_df.head())
print(f"\n数据集形状: {customer_df.shape}")
print("\n数据基本信息:")
print(customer_df.info())

# 数据清洗流程
def clean_customer_data(df):
    # 创建数据清洗报告
    report = []
    
    # 1. 处理缺失值
    initial_count = len(df)
    df_cleaned = df.dropna(subset=['Age'])  # 年龄是关键特征,删除缺失值
    age_missing_removed = initial_count - len(df_cleaned)
    report.append(f"删除年龄缺失值: {age_missing_removed} 条记录")
    
    # 用中位数填充收入缺失值
    income_median = df_cleaned['Income'].median()
    income_missing_count = df_cleaned['Income'].isna().sum()
    df_cleaned['Income'] = df_cleaned['Income'].fillna(income_median)
    report.append(f"用中位数填充收入缺失值: {income_missing_count} 条记录")
    
    # 2. 处理异常值(使用IQR方法)
    Q1 = df_cleaned['Income'].quantile(0.25)
    Q3 = df_cleaned['Income'].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    
    before_outlier = len(df_cleaned)
    df_cleaned = df_cleaned[(df_cleaned['Income'] >= lower_bound) & 
                           (df_cleaned['Income'] <= upper_bound)]
    outliers_removed = before_outlier - len(df_cleaned)
    report.append(f"移除收入异常值: {outliers_removed} 条记录")
    
    # 3. 数据分箱和特征工程
    # 年龄分箱
    age_bins = [0, 25, 35, 45, 55, 65, 100]
    age_labels = ['18-25', '26-35', '36-45', '46-55', '56-65', '66+']
    df_cleaned['AgeGroup'] = pd.cut(df_cleaned['Age'], bins=age_bins, labels=age_labels)
    
    # 收入分箱
    income_bins = [0, 30000, 50000, 70000, 100000, float('inf')]
    income_labels = ['<30K', '30K-50K', '50K-70K', '70K-100K', '>100K']
    df_cleaned['IncomeGroup'] = pd.cut(df_cleaned['Income'], bins=income_bins, labels=income_labels)
    
    # 4. 创建新特征
    df_cleaned['SpendingPerYear'] = df_cleaned['SpendingScore'] / (df_cleaned['MembershipYears'] + 1)
    df_cleaned['ValueSegment'] = np.where(df_cleaned['SpendingScore'] > 50, 'High', 'Low')
    
    report.append(f"最终数据量: {len(df_cleaned)} 条记录")
    report.append(f"数据清洗完成,保留了 {len(df_cleaned)/1000*100:.1f}% 的原始数据")
    
    return df_cleaned, report

# 执行数据清洗
cleaned_df, cleaning_report = clean_customer_data(customer_df)

print("\n=== 数据清洗报告 ===")
for item in cleaning_report:
    print(f"• {item}")

print(f"\n清洗后数据形状: {cleaned_df.shape}")
print("\n清洗后数据前5行:")
print(cleaned_df.head())

# 数据质量评估
print("\n=== 数据质量评估 ===")
print("缺失值统计:")
print(cleaned_df.isnull().sum())

print("\n数值列描述性统计:")
print(cleaned_df[['Age', 'Income', 'SpendingScore', 'MembershipYears']].describe())

# 可视化分析
plt.figure(figsize=(15, 10))

# 1. 年龄分布
plt.subplot(2, 3, 1)
sns.histplot(cleaned_df['Age'], bins=20, kde=True, color='skyblue')
plt.title('年龄分布')
plt.xlabel('Age')

# 2. 收入分布
plt.subplot(2, 3, 2)
sns.histplot(cleaned_df['Income'], bins=20, kde=True, color='lightgreen')
plt.title('收入分布')
plt.xlabel('Income')

# 3. 消费分数箱线图
plt.subplot(2, 3, 3)
sns.boxplot(y=cleaned_df['SpendingScore'], color='lightcoral')
plt.title('消费分数分布')

# 4. 各地区客户数量
plt.subplot(2, 3, 4)
region_counts = cleaned_df['Region'].value_counts()
plt.pie(region_counts.values, labels=region_counts.index, autopct='%1.1f%%')
plt.title('客户地区分布')

# 5. 年龄组与平均消费分数
plt.subplot(2, 3, 5)
age_spending = cleaned_df.groupby('AgeGroup')['SpendingScore'].mean()
sns.barplot(x=age_spending.index, y=age_spending.values, palette='viridis')
plt.title('各年龄组平均消费分数')
plt.xticks(rotation=45)

# 6. 收入与消费分数散点图
plt.subplot(2, 3, 6)
sns.scatterplot(data=cleaned_df, x='Income', y='SpendingScore', hue='ValueSegment', alpha=0.6)
plt.title('收入 vs 消费分数')

plt.tight_layout()
plt.show()

# 高级分析:客户分群
print("\n=== 客户价值分析 ===")
customer_analysis = cleaned_df.groupby(['AgeGroup', 'IncomeGroup']).agg({
    'SpendingScore': ['mean', 'count'],
    'IsActive': 'mean'
}).round(2)

customer_analysis.columns = ['AvgSpending', 'CustomerCount', 'ActiveRate']
customer_analysis['ActiveRate'] = customer_analysis['ActiveRate'] * 100

print("客户分群分析:")
print(customer_analysis)

# 保存清洗后的数据
cleaned_df.to_csv('cleaned_customer_data.csv', index=False)
print(f"\n清洗后的数据已保存至 'cleaned_customer_data.csv'")

# 数据质量总结
print("\n=== 数据清洗效果总结 ===")
print(f"原始数据量: 1000 条记录")
print(f"清洗后数据量: {len(cleaned_df)} 条记录")
print(f"数据保留率: {len(cleaned_df)/1000*100:.1f}%")
print(f"处理后的特征数量: {len(cleaned_df.columns)} 个")
print(f"创建的新特征: AgeGroup, IncomeGroup, SpendingPerYear, ValueSegment")

数据清洗关键要点总结

  1. 数据质量优先:始终从数据质量评估开始
  2. 循序渐进:按照缺失值→异常值→数据转换的顺序处理
  3. 业务理解:数据清洗方法要结合业务场景
  4. 可视化验证:通过图表验证清洗效果

最佳实践建议

  • 建立数据清洗流水线:将常用清洗步骤封装为可重用的函数
  • 版本控制:对原始数据和清洗后的数据都进行版本管理

通过掌握这10大Pandas数据清洗技巧,您将能够有效提升数据质量,为机器学习模型提供更可靠的训练数据,最终获得更好的模型性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值