Python 数据清洗
在数据分析和机器学习项目中,数据清洗是一个至关重要的预处理步骤。原始数据通常包含缺失值、异常值、格式不一致等问题,这些问题如果不解决,将会影响后续分析的准确性。本文将介绍如何使用Python进行数据清洗工作。
什么是数据清洗?
数据清洗是指识别并纠正数据集中的错误、不一致和缺失部分的过程,目的是提高数据质量,为后续分析做好准备。
提示
良好的数据清洗能让分析结果更加可靠,也能让你的机器学习模型性能更好!
数据清洗的主要任务
数据清洗主要包括以下任务:
- 处理缺失值
- 删除重复数据
- 处理异常值
- 数据类型转换
- 格式标准化
开始之前:所需工具
在Python中进行数据清洗,我们主要使用以下几个库:
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
1. 处理缺失值
缺失值是数据集中常见的问题,它们可能导致分析结果偏差或使某些算法无法运行。
检测缺失值
python
# 创建一个包含缺失值的数据集
data = {'A': [1, 2, np.nan, 4],
'B': [5, np.nan, np.nan, 8],
'C': [9, 10, 11, 12]}
df = pd.DataFrame(data)
print(df)
输出:
A B C
0 1.0 5.0 9
1 2.0 NaN 10
2 NaN NaN 11
3 4.0 8.0 12
检查缺失值:
python
# 检查每列缺失值数量
print(df.isna().sum())
# 可视化缺失值
import missingno as msno
msno.matrix(df)
plt.show()
输出:
A 1
B 2
C 0
dtype: int64
处理缺失值的方法
- 删除缺失值
python
# 删除包含任何缺失值的行
df_dropped = df.dropna()
print("删除缺失值后:")
print(df_dropped)
# 只删除所有值都是缺失的行
df_dropped_all = df.dropna(how='all')
输出:
删除缺失值后:
A B C
0 1.0 5.0 9
3 4.0 8.0 12
- 填充缺失值
python
# 使用常数填充
df_filled = df.fillna(0)
print("用0填充:")
print(df_filled)
# 使用均值填充
df_mean = df.fillna(df.mean())
print("\n用均值填充:")
print(df_mean)
# 使用前向填充
df_ffill = df.fillna(method='ffill')
print("\n用前向值填充:")
print(df_ffill)
输出:
用0填充:
A B C
0 1.0 5.0 9
1 2.0 0.0 10
2 0.0 0.0 11
3 4.0 8.0 12
用均值填充:
A B C
0 1.000000 5.000000 9
1 2.000000 6.500000 10
2 2.333333 6.500000 11
3 4.000000 8.000000 12
用前向值填充:
A B C
0 1.0 5.0 9
1 2.0 5.0 10
2 2.0 5.0 11
3 4.0 8.0 12
2. 处理重复数据
重复数据会扭曲统计结果,并可能导致模型过度拟合特定样本。
python
# 创建包含重复行的数据集
data = {'A': [1, 2, 2, 3, 3],
'B': [5, 6, 6, 7, 7]}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
# 检查重复行
duplicates = df.duplicated()
print("\n重复行的布尔标记:")
print(duplicates)
# 删除重复行
df_unique = df.drop_duplicates()
print("\n删除重复后:")
print(df_unique)
输出:
原始数据:
A B
0 1 5
1 2 6
2 2 6
3 3 7
4 3 7
重复行的布尔标记:
0 False
1 False
2 True
3 False
4 True
dtype: bool
删除重复后:
A B
0 1 5
1 2 6
3 3 7
3. 处理异常值
异常值是偏离正常范围的数据点,可能会严重影响统计分析。
检测异常值
python
# 创建包含异常值的数据集
data = {'value': [10, 12, 12, 13, 12, 11, 50]}
df = pd.DataFrame(data)
# 绘制箱线图检测异常值
plt.figure(figsize=(8, 4))
plt.boxplot(df['value'])
plt.title('异常值检测')
plt.show()
# 使用IQR方法检测异常值
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df['value'] < lower_bound) | (df['value'] > upper_bound)]
print("检测到的异常值:")
print(outliers)
输出:
检测到的异常值:
value
6 50
处理异常值
python
# 移除异常值
df_clean = df[(df['value'] >= lower_bound) & (df['value'] <= upper_bound)]
print("移除异常值后:")
print(df_clean)
# 或者将异常值替换为上下限
df['value_capped'] = df['value'].clip(lower=lower_bound, upper=upper_bound)
print("\n截断异常值后:")
print(df)
输出:
移除异常值后:
value
0 10
1 12
2 12
3 13
4 12
5 11
截断异常值后:
value value_capped
0 10 10.0
1 12 12.0
2 12 12.0
3 13 13.0
4 12 12.0
5 11 11.0
6 50 14.5
4. 数据类型转换
正确的数据类型对于数据分析至关重要,可以提高运算效率并避免错误。
python
# 创建混合数据类型的数据集
data = {'id': ['1', '2', '3', '4'],
'age': ['25', '30', '35', 'unknown'],
'income': ['50000', '60,000', '75,000', '80000']}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print("\n数据类型:")
print(df.dtypes)
# 转换ID列为整数类型
df['id'] = pd.to_numeric(df['id'])
# 处理age列:先替换无效值,再转换类型
df['age'] = df['age'].replace('unknown', np.nan)
df['age'] = pd.to_numeric(df['age'], errors='coerce')
# 处理income列:移除非数字字符,再转换
df['income'] = df['income'].str.replace(',', '')
df['income'] = pd.to_numeric(df['income'])
print("\n类型转换后:")
print(df)
print("\n新的数据类型:")
print(df.dtypes)
输出:
原始数据:
id age income
0 1 25 50000
1 2 30 60,000
2 3 35 75,000
3 4 unknown 80000
数据类型:
id object
age object
income object
dtype: object
类型转换后:
id age income
0 1 25.0 50000
1 2 30.0 60000
2 3 35.0 75000
3 4 NaN 80000
新的数据类型:
id int64
age float64
income int64
dtype: object
5. 数据格式化和标准化
数据格式化确保数据的一致性和可比性,这对于多源数据集的合并尤为重要。
python
# 创建需要格式化的数据
data = {'date': ['2023-01-01', '2023/02/01', '01-Mar-2023', '2023.04.01'],
'name': ['JOHN DOE', 'jane smith', 'Bob Jones', 'Alice Brown'],
'code': ['ABC-123', 'DEF/456', 'GHI_789', 'JKL:012']}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
# 统一日期格式
df['date'] = pd.to_datetime(df['date'], errors='coerce')
df['date'] = df['date'].dt.strftime('%Y-%m-%d')
# 标准化名字格式
df['name'] = df['name'].str.title()
# 统一代码格式
df['code'] = df['code'].str.replace(r'[-/_;:]', '-', regex=True)
print("\n格式化后:")
print(df)
输出:
原始数据:
date name code
0 2023-01-01 JOHN DOE ABC-123
1 2023/02/01 jane smith DEF/456
2 01-Mar-2023 Bob Jones GHI_789
3 2023.04.01 Alice Brown JKL:012
格式化后:
date name code
0 2023-01-01 John Doe ABC-123
1 2023-02-01 Jane Smith DEF-456
2 2023-03-01 Bob Jones GHI-789
3 2023-04-01 Alice Brown JKL-012
6. 实际案例:分析用户数据
让我们通过一个实际案例,将所学的数据清洗技术应用到一个假设的用户数据集上。
python
# 创建一个模拟的"脏"用户数据集
data = {
'user_id': ['1', '2', '3', '4', '5', '5', '7', '8'],
'username': ['john_doe', 'jane_smith', 'bob123', np.nan, 'alice42', 'alice42', 'mike85', 'susan_j'],
'age': ['32', '28', 'unknown', '41', '35', '35', '120', '29'],
'signup_date': ['2023-01-15', '2023/02/20', '03/15/2023', '2023.04.01', '2023-05-10', '2023-05-10', '2023-06-15', np.nan],
'last_login': ['2023-06-15', '2023-06-10', '2023-06-14', np.nan, '2023-06-16', '2023-06-16', '2023-06-01', '2023-06-12'],
'purchases': ['5', '10', '0', '8', '3', '3', '500', '6']
}
df_users = pd.DataFrame(data)
print("原始用户数据:")
print(df_users)
输出:
原始用户数据:
user_id username age signup_date last_login purchases
0 1 john_doe 32 2023-01-15 2023-06-15 5
1 2 jane_smith 28 2023/02/20 2023-06-10 10
2 3 bob123 unknown 03/15/2023 2023-06-14 0
3 4 NaN 41 2023.04.01 NaN 8
4 5 alice42 35 2023-05-10 2023-06-16 3
5 5 alice42 35 2023-05-10 2023-06-16 3
6 7 mike85 120 2023-06-15 2023-06-01 500
7 8 susan_j 29 NaN 2023-06-12 6
现在我们将执行完整的数据清洗流程:
python
# 1. 处理缺失值
df_users['username'] = df_users['username'].fillna('anonymous')
df_users['last_login'] = df_users['last_login'].fillna(df_users['signup_date'])
df_users['signup_date'] = df_users['signup_date'].fillna('未知')
# 2. 删除重复记录
df_users = df_users.drop_duplicates()
# 3. 处理异常值
# 转换age为数值型,替换无法转换的值为NaN
df_users['age'] = df_users['age'].replace('unknown', np.nan)
df_users['age'] = pd.to_numeric(df_users['age'], errors='coerce')
# 处理异常年龄
age_mean = df_users['age'].mean()
df_users.loc[(df_users['age'] > 100) | (df_users['age'] < 13), 'age'] = age_mean
# 处理异常购买量
purchases_median = df_users['purchases'].astype(int).median()
df_users.loc[df_users['purchases'].astype(int) > 100, 'purchases'] = str(purchases_median)
# 4. 数据类型转换
df_users['user_id'] = pd.to_numeric(df_users['user_id'])
df_users['purchases'] = df_users['purchases'].astype(int)
# 5. 格式化日期
df_users['signup_date'] = pd.to_datetime(df_users['signup_date'], errors='coerce')
df_users['last_login'] = pd.to_datetime(df_users['last_login'], errors='coerce')
# 将日期格式化为统一格式
df_users['signup_date'] = df_users['signup_date'].dt.strftime('%Y-%m-%d')
df_users['last_login'] = df_users['last_login'].dt.strftime('%Y-%m-%d')
# 替换无法转换的日期为'未知'
df_users['signup_date'] = df_users['signup_date'].fillna('未知')
df_users['last_login'] = df_users['last_login'].fillna('未知')
print("\n清洗后的用户数据:")
print(df_users)
输出:
清洗后的用户数据:
user_id username age signup_date last_login purchases
0 1 john_doe 32.0 2023-01-15 2023-06-15 5
1 2 jane_smith 28.0 2023-02-20 2023-06-10 10
2 3 bob123 NaN 2023-03-15 2023-06-14 0
3 4 anonymous 41.0 2023-04-01 2023-04-01 8
4 5 alice42 35.0 2023-05-10 2023-06-16 3
6 7 mike85 33.0 2023-06-15 2023-06-01 6
7 8 susan_j 29.0 未知 2023-06-12 6
总结
数据清洗是数据分析流程中至关重要的一步。通过本教程,我们学习了以下数据清洗技术:
- 处理缺失值:识别、删除或填充缺失值
- 删除重复数据:识别和移除重复记录
- 处理异常值:检测和处理不符合预期范围的值
- 数据类型转换:确保数据具有正确的类型
- 数据格式化和标准化:统一数据格式,使其一致且可比较
掌握这些技术将帮助你准备出高质量的数据集,为后续的数据分析和机器学习模型构建打下坚实基础。
提示
记住,数据清洗通常是反复迭代的过程,需要结合特定的业务规则和领域知识!
练习题
- 创建一个包含缺失值的数据集,尝试使用不同的方法处理缺失值,并比较结果。
- 使用pandas读取一个CSV文件,检测并处理其中的重复记录。
- 实现一个函数,能够自动检测并处理数据集中的异常值。
- 尝试对一个包含多种格式日期的数据集进行标准化处理。
附加资源
- Pandas 官方文档
- Numpy 官方文档
- Matplotlib 官方文档
- 推荐书籍:《Python for Data Analysis》by Wes McKinney