数据清洗是指对原始数据进行系统性的审查、校验与修正,旨在识别并处理其中存在的错误、不一致、不完整、重复和无效的部分。该过程的核心目标,是生成高质量、可信赖的“干净”数据,为后续的数据分析与建模奠定准确、一致的基础。
数据清洗并非一系列彼此孤立的操作,而是需要遵循一套系统化且循环迭代的科学流程。这一流程构建起了一个从问题诊断直至效果验证的完整闭环。
一、数据质量评估与探索
在对原始数据进行清洗之前,首先需要对其质量进行全面且系统的评估,明确数据中存在的具体问题,例如缺失值、重复值、异常值以及不一致性等。只有在准确诊断问题的基础上,才能制定出具有针对性的清洗策略。
(1)数据概览与结构分析
使用.info()方法快速获取数据集的整体情况:
import pandas as pd
# 读取数据
df = pd.read_csv('用户订单原始数据.csv')
# 查看数据概览
print("数据集概览(info):")
print(df.info())
运行结果如下所示:
数据集概览(info):
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 36 entries, 0 to 35
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 订单ID 36 non-null int64
1 用户ID 36 non-null int64
2 订单日期 36 non-null object
3 城市 36 non-null object
4 年龄 35 non-null float64
5 订单金额(元) 34 non-null float64
6 商品类别 36 non-null object
7 商品名称 36 non-null object
8 数量 36 non-null int64
9 支付方式 36 non-null object
10 订单状态 36 non-null object
11 最后登录时间 36 non-null object
12 会员等级 36 non-null object
13 客服备注 7 non-null object
dtypes: float64(2), int64(3), object(9)
memory usage: 4.1+ KB
None
(2)数值型字段分布与异常检测
使用.describe()方法查看数值型字段的统计描述:
print("\n数值型字段描述性统计(describe):")
print(df.describe())
运行结果如下所示:
数值型字段描述性统计(describe):
订单ID 用户ID 年龄 订单金额(元) 数量
count 36.000000 36.000000 35.000000 34.000000 36.000000
mean 10017.527778 217.527778 37.114286 879.726471 1.222222
std 10.489413 10.489413 29.246188 1812.952159 0.540429
min 10001.000000 201.000000 22.000000 -88.500000 1.000000
25% 10008.750000 208.750000 27.000000 61.375000 1.000000
50% 10017.500000 217.500000 31.000000 299.000000 1.000000
75% 10026.250000 226.250000 37.000000 599.000000 1.000000
max 10035.000000 235.000000 200.000000 8888.000000 3.000000
(3)缺失值精确统计
通过.isnull().sum()精确计算每列的缺失值数量:
# 进行缺失值的精确统计
print("\n各字段缺失值统计:")
print(df.isnull().sum())
运行结果如下所示:
各字段缺失值统计:
订单ID 0
用户ID 0
订单日期 0
城市 0
年龄 1
订单金额(元) 2
商品类别 0
商品名称 0
数量 0
支付方式 0
订单状态 0
最后登录时间 0
会员等级 0
客服备注 29
dtype: int64
(4)重复记录检测
使用.duplicated().sum()检查完全重复的行:
# 进行重复行的统计
print(f"\n完全重复的行数:{df.duplicated().sum()}")
运行结果如下所示:
完全重复的行数:1
(5)分类型字段唯一值检查
针对分类型字段(如城市、支付方式),使用.value_counts()查看取值分布:
print("\n城市取值分布:")
print(df['城市'].value_counts())
print("\n支付方式分布:")
print(df['支付方式'].value_counts())
运行结果如下所示:
城市取值分布:
城市
北京市 5
北京 4
广州市 3
上海市 3
杭州市 3
深圳市 2
重庆市 2
武汉市 2
南京市 2
天津市 2
苏州市 2
西安市 2
深圳 1
SH 1
广州 1
成都市 1
Name: count, dtype: int64
支付方式分布:
支付方式
支付宝 11
微信支付 9
信用卡 8
借记卡 8
Name: count, dtype: int64
二、数据清洗
在完成前述系统性的数据评估后,我们已全面识别出数据集中存在的各类质量问题。接下来,将依据评估结果,针对不同问题制定并执行相应的数据清洗方案。
(1)缺失值处理
存在缺失值的字段有“年龄”、“订单金额(元)”以及“客服备注”:
- 其中“年龄”与“订单金额(元)”的缺失值较少,并且整体的数据分布偏斜,可以考虑使用中位数进行填充。
- “客服备注”字段的缺失率高达80.6%,并且分析价值较低,因此可以考虑整列删除。
缺失值的具体处理操作代码如下所示。
import pandas as pd
# 读取数据
df = pd.read_csv('用户订单原始数据.csv')
# 处理缺失值
# 先创建副本或先处理其他列,再删除备注列
df_clean = df.copy()
# 首先处理数值型字段的缺失值
# 确保中位数计算不包括NaN值
age_median = df_clean['年龄'].median()
revenue_median = df_clean['订单金额(元)'].median()
# 使用中位数来填充缺失值
df_clean['年龄'] = df_clean['年龄'].fillna(age_median)
df_clean['订单金额(元)'] = df_clean['订单金额(元)'].fillna(revenue_median)
# 最后删除客服备注列
df_clean = df_clean.drop(columns=['客服备注'])
print("处理后的数据集概览:")
print(df_clean.info())
# 保存修改后的数据
df_clean.to_csv('清理缺失值后的数据.csv', index=False)
运行结果如下所示:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 36 entries, 0 to 35
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 订单ID 36 non-null int64
1 用户ID 36 non-null int64
2 订单日期 36 non-null object
3 城市 36 non-null object
4 年龄 36 non-null float64
5 订单金额(元) 36 non-null float64
6 商品类别 36 non-null object
7 商品名称 36 non-null object
8 数量 36 non-null int64
9 支付方式 36 non-null object
10 订单状态 36 non-null object
11 最后登录时间 36 non-null object
12 会员等级 36 non-null object
dtypes: float64(2), int64(3), object(8)
memory usage: 3.8+ KB
None
(2)异常值处理
对异常值的具体处理代码如下所示:
import pandas as pd
# 读取数据
df = pd.read_csv('清理缺失值后的数据.csv')
# 创建副本
df_clean = df.copy()
print("处理前的异常值统计:")
print(f"年龄 > 100的记录: {len(df_clean[df_clean['年龄'] > 100])}条")
print(f"订单金额 < 0的记录: {len(df_clean[df_clean['订单金额(元)'] < 0])}条")
# 处理年龄异常值: 将大于100的值替换为中位数
df_clean.loc[df_clean['年龄'] > 100, '年龄'] = df_clean['年龄'].median()
# 将负值设为0(假设负值代表退款或错误)
df_clean.loc[df_clean['订单金额(元)'] < 0, '订单金额(元)'] = 0
print("\n处理后的异常值统计:")
print(f"年龄 > 100的记录: {len(df_clean[df_clean['年龄'] > 100])}条")
print(f"订单金额 < 0的记录: {len(df_clean[df_clean['订单金额(元)'] < 0])}条")
# 保存修改后的数据
df_clean.to_csv('异常值处理后的数据.csv', index=False)
运行结果如下所示:
处理前的异常值统计:
年龄 > 100的记录: 1条
订单金额 < 0的记录: 1条
处理后的异常值统计:
年龄 > 100的记录: 0条
订单金额 < 0的记录: 0条
(3)不一致值的处理
import pandas as pd
# 读取数据
df = pd.read_csv('异常值处理后的数据.csv')
# 创建副本
df_clean = df.copy()
# 处理不一致值:标准化城市名称
print("处理前的城市取值分布:")
print(df_clean['城市'].value_counts())
# 创建城市名称映射字典,将不同表达统一为标准名称
city_mapping = {
'北京': '北京市',
'SH': '上海市',
'广州': '广州市',
'深圳': '深圳市',
# 可以继续添加其他需要标准化的映射
}
# 应用映射,标准化城市名称
df_clean['城市'] = df_clean['城市'].map(city_mapping).fillna(df_clean['城市'])
print("\n处理后的城市取值分布:")
print(df_clean['城市'].value_counts())
# 保存修改后的数据
df_clean.to_csv('清理不一致值后的数据.csv', index=False)
运行结果如下所示:
处理前的城市取值分布:
城市
北京市 5
北京 4
广州市 3
上海市 3
杭州市 3
深圳市 2
重庆市 2
武汉市 2
南京市 2
天津市 2
苏州市 2
西安市 2
深圳 1
SH 1
广州 1
成都市 1
Name: count, dtype: int64
处理后的城市取值分布:
城市
北京市 9
上海市 4
广州市 4
深圳市 3
杭州市 3
重庆市 2
武汉市 2
西安市 2
南京市 2
苏州市 2
天津市 2
成都市 1
Name: count, dtype: int64
(4)数据类型的处理
在前期数据质量评估过程中,我们发现"订单日期"与"最后登录时间"两个字段的数据类型均为Object(通常存储为字符串),而非标准的时间格式。这一情况可能源于数据采集或导入过程中的格式差异。为确保后续能够顺利进行时间序列分析、趋势统计等基于时间的深度分析,需要将这两个字段转换为datetime类型。具体的转换操作方法如下:
import pandas as pd
# 读取数据
df = pd.read_csv('清理不一致值后的数据.csv')
# 创建副本
df_clean = df.copy()
# 检查转换前的时间字段类型和样例
print("处理前的时间字段类型:")
print(df_clean[['订单日期', '最后登录时间']].dtypes)
print("\n处理前的时间字段前5条记录:")
print(df_clean[['订单日期', '最后登录时间']].head())
# 将日期时间字段从字符串转换为datetime类型
# 预处理:标准化日期字符串格式
def standardize_date_str(date_str):
if isinstance(date_str, str):
date_str = date_str.strip()
if len(date_str) == 8 and date_str.isdigit():
return f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:]}"
return date_str
df_clean['订单日期'] = df_clean['订单日期'].apply(standardize_date_str) # 格式化日期格式
# 使用errors='coerce'参数确保转换失败时返回NaT而非报错
df_clean['订单日期'] = pd.to_datetime(df_clean['订单日期'], errors='coerce')
df_clean['最后登录时间'] = pd.to_datetime(df_clean['最后登录时间'], errors='coerce')
# 检查转换结果,查看是否有转换失败的值
print("\n转换后时间字段的缺失值情况:")
print(df_clean[['订单日期', '最后登录时间']].isnull().sum())
# 验证转换后的时间字段类型
print("\n转换后的时间字段类型:")
print(df_clean[['订单日期', '最后登录时间']].dtypes)
# 显示转换后的样例数据
print("\n转换后的时间字段前5条记录:")
print(df_clean[['订单日期', '最后登录时间']].head())
# 保存处理后的数据
df_clean.to_csv('用户订单处理后的数据.csv', index=False)
print("\n时间格式处理完成,数据已保存!")
运行效果如下所示:
处理前的时间字段类型:
订单日期 object
最后登录时间 object
dtype: object
处理前的时间字段前5条记录:
订单日期 最后登录时间
0 2024-10-27 2024-10-27 09:15:32
1 2024/10/26 2024-10-26 14:20:18
2 20241025 2024-10-25 16:45:22
3 2024-10-24 2024-10-24 11:30:45
4 2024-10-23 2024-10-23 10:15:10
转换后时间字段的缺失值情况:
订单日期 1
最后登录时间 0
dtype: int64
转换后的时间字段类型:
订单日期 datetime64[ns]
最后登录时间 datetime64[ns]
dtype: object
转换后的时间字段前5条记录:
订单日期 最后登录时间
0 2024-10-27 2024-10-27 09:15:32
1 NaT 2024-10-26 14:20:18
2 2024-10-25 2024-10-25 16:45:22
3 2024-10-24 2024-10-24 11:30:45
4 2024-10-23 2024-10-23 10:15:10
时间格式处理完成,数据已保存!