在机器学习实验中,高质量的数据是模型成功的基石。Pandas作为Python数据分析的核心库,提供了强大的数据清洗能力。本文将深入探讨10个关键的Pandas数据清洗技巧,帮助您提升数据质量,为模型训练奠定坚实基础。
今儿详细聊的 Pandas 十大数据清洗技巧有:
- 处理缺失值
- 处理重复数据
- 数据类型转换
- 处理异常值
- 数据标准化与归一化
- 字符串操作
- 时间序列数据处理
- 条件筛选与过滤
- 数据分箱与离散化
- 数据合并与连接
一起来看下~
数据清洗的重要性
数据清洗直接影响模型的性能表现:
- 提高准确性:干净的数据减少噪声干扰
- 增强鲁棒性:处理异常值提升模型稳定性
- 加速训练:规范化的数据提高算法效率
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")
数据清洗关键要点总结
- 数据质量优先:始终从数据质量评估开始
- 循序渐进:按照缺失值→异常值→数据转换的顺序处理
- 业务理解:数据清洗方法要结合业务场景
- 可视化验证:通过图表验证清洗效果
最佳实践建议
- 建立数据清洗流水线:将常用清洗步骤封装为可重用的函数
- 版本控制:对原始数据和清洗后的数据都进行版本管理
通过掌握这10大Pandas数据清洗技巧,您将能够有效提升数据质量,为机器学习模型提供更可靠的训练数据,最终获得更好的模型性能。

被折叠的 条评论
为什么被折叠?



