数据的预处理(清洗、集成、变换、规约)
在进行数据挖掘中,原始海量的数据中存在着大量不完整(有缺失值)、不一致、有异常的数据,严重影响到数据挖掘建模的执行效率,甚至可能导致挖掘结果的偏差,所以对首先对数据进行清洗就显得尤为重要,在数据清洗完成后,后续伴随着数据集成、转换、规约等一系列的处理,该过程就是数据预处理。数据预处理一方面是要提高数据的质量,另一方面是要让数据更好地适应特定的挖掘技术或工具。统计发现,在数据挖掘的过程中,数据预处理工作量占到了整个过程的60%。
数据预处理的主要内容包括:数据清洗、数据集成、数据变换和数据规约
.
1、数据清洗(Data Cleaning)
目标:修复数据中的噪声、缺失、不一致等问题。
1.1 、具体内容:
1.1.1、缺失值处理
- 方法:删除缺失记录、均值/中位数填充、插值法、模型预测填充。
- 示例:年龄字段缺失时,用数据集的平均年龄填充。
1.1.2、异常值处理
- 方法:Z-score法(标准化)、IQR(四分位距法)、描述性分析、散点图、箱形图、直方图查找异常并处理。
- 示例:检测到某商品价格超过正常范围的3倍标准差,判定为异常并剔除。
1.1.3、噪声数据
- 方法:分箱、回归、孤点分析。
- 示例:
1.1.4、重复值处理
- 方法:删除完全重复的行。
- 示例:电商订单数据中同一订单被重复记录两次,删除冗余行。
示例代码:
import pandas as pd
import numpy as np
# 我们创建模拟数据,包含重复的行(基于'name'和'city'列)
data = {
'name': ['郭靖', '黄蓉', '李飞', '周伯通', '杨过', '小龙女', '金轮法王', '周伯通','郭靖','黄药师'],
'age': [56, np.nan, 35, 40, 45, 30, 40, 45, 56, 76],
'sex': ['男', '女', '女', '男', '男', '女', '男', '男','男',np.nan],
'salary': [20000, 60000, np.nan, 75000, 80000, 60000, 120000, 80000, 20000, 50000],
'department': ['人力资源部', 'IT部', '销售部', '市场部', '工会', 'IT部', '金融事业部', '市场部', '人力资源部', '无业游民'],
'city': ['广州市', '深圳市', '北京市', '南京市', '杭州市', '合肥市', '贵阳市', '南京市', '广州市', '东莞市']
}
df = pd.DataFrame(data)
# 查看原始数据
print("原始数据:")
print(df)
# 处理缺失值
# 使用均值填充'age'列的缺失值,使用中位数填充'salary'列的缺失值
df['age'].fillna(df['age'].mean(), inplace=True)
df['salary'].fillna(df['salary'].median(), inplace=True)
# 删除'sex'列缺失值'黄药师'
df = df.dropna(subset=['sex'])
# 处理异常值
# 假设'salary'列中的值应该在30000到80000之间,我们将其超出这个范围的值视为异常值
# 并用该列的中位数来替换这些异常值
df['salary'] = df['salary'].apply(lambda x: x if 30000 <= x <= 80000 else df['salary'].median())
# 处理重复值
# 针对'name'和'city'列上重复的行删除(郭靖&周伯通),只保留第一次出现的行
df.drop_duplicates(subset=['name', 'city'], keep='first', inplace=True)
# 查看清洗后的数据
print("\n清洗后的数据:")
print(df)
输出结果:
原始数据:
name age sex salary department city
0 郭靖 56.0 男 20000.0 人力资源部 广州市
1 黄蓉 NaN 女 60000.0 IT部 深圳市
2 李飞 35.0 女 NaN 销售部 北京市
3 周伯通 40.0 男 75000.0 市场部 南京市
4 杨过 45.0 男 80000.0 工会 杭州市
5 小龙女 30.0 女 60000.0 IT部 合肥市
6 金轮法王 40.0 男 120000.0 金融事业部 贵阳市
7 周伯通 45.0 男 80000.0 市场部 南京市
8 郭靖 56.0 男 20000.0 人力资源部 广州市
9 黄药师 76.0 NaN 50000.0 无业游民 东莞市
清洗后的数据:
name age sex salary department city
0 郭靖 56.0 男 60000.0 人力资源部 广州市
1 黄蓉 47.0 女 60000.0 IT部 深圳市
2 李飞 35.0 女 60000.0 销售部 北京市
3 周伯通 40.0 男 75000.0 市场部 南京市
4 杨过 45.0 男 80000.0 工会 杭州市
5 小龙女 30.0 女 60000.0 IT部 合肥市
6 金轮法王 40.0 男 60000.0 金融事业部 贵阳市
2、数据集成(Data Integration)
目标:涉及整合多源数据,解决冗余和实体识别冲突。
2.1、具体内容:
2.1.1、实体识别
- 方法:统一字段命名(如 CustomerID 和 Cust_ID 合并为同一字段)。
- 示例:合并销售表(含 ProductID)和库存表(含 ProdID),需统一字段名。
2.1.2、冗余数据处理
- 方法:分析相关性,删除冗余列(如同时存在 身高(cm) 和 身高(m))。
- 示例:合并两个客户表时,发现 年龄 和 出生年份 存在冗余,删除其一。
3、数据变换(Data Transformation)
目标:将数据转换为适合数据挖掘分析的格式,有规范化、离散化、特征构造等。
3.1、具体内容:
3.1.1、规范化(Normalization)
数值标准化和归一化是特征工程中常用的方法,用于将数值型特征的取值范围归一到一个共同的尺度或范围内,以便更好地处理和分析数据。
3.1.1.1、Z-score 标准化
该方法将每个特征值减去该特征的均值,并除以标准差,得到标准化后的特征值。这种方法使得每个特征的均值为 0,标准差为 1,具体计算公式如下:
其中,x 是原始特征值, μ 是特征的均值, σ 是特征的标准差, z 是标准化后的特征值
z
=
x
−
μ
σ
z = \frac{x- μ } {σ}
z=σx−μ
Z-score 标准化的优点是对数据的分布具有平移和缩放不变性,即标准化后的数据不受数据整体偏移和缩放的影响
。
import pandas as pd
from sklearn.preprocessing import StandardScaler
# 原始数据
data = {
'salary': [20000, 60000, 70000, 75000, 80000, 60000, 120000, 80000, 20000, 50000]
}
# 创建DataFrame
df = pd.DataFrame(data)
# 使用StandardScaler进行Z-score标准化
scaler = StandardScaler()
scaled_salary = scaler.fit_transform(df[['salary']]) # 注意输入需为二维数组
# 将结果添加到DataFrame
df['salary_zscore'] = scaled_salary.round(3) # 保留3位小数
# 输出对比
print("处理前数据(原始薪资):")
print(df[['salary']].T) # 转置显示更紧凑
print("\n处理后数据(Z-score标准化):")
print(df[['salary_zscore']].T)
# 验证参数
print(f"\n验证参数 → 均值: {scaler.mean_[0]:.1f}, 标准差: {scaler.scale_[0]:.1f}")
处理前数据(原始薪资):
0 1 2 3 4 5 6 7 8 9
salary 20000 60000 70000 75000 80000 60000 120000 80000 20000 50000
处理后数据(Z-score标准化):
0 1 2 3 4 5 6 7 8 9
salary_zscore -1.432 -0.278 0.028 0.206 0.384 -0.278 1.971 0.384 -1.432 -0.554
验证参数 → 均值: 63500.0, 标准差: 30368.6
3.1.1.2、Min-Max归一化(Normalization)
该方法将每个特征值映射到一个指定的范围内,通常是[0,1]或[-1,1]。具体计算公式如下:
x
′
=
x
−
m
i
n
(
x
)
m
a
x
(
x
)
−
m
i
n
(
x
)
x′ = \frac{x- min(x) } {max(x)-min(x)}
x′=max(x)−min(x)x−min(x)
其中, x′ 是归一化后的特征值, x 是原始特征值, min(x) 和 max(x) 分别是特征的最小值和最大值。
Min-Max 归一化的优点是简单易懂,并且可以将数据限制在特定的范围内。它对于一些需要将数据映射到特定范围的算法或可视化方法(如神经网络
)会非常合适。
from sklearn.preprocessing import MinMaxScaler
import numpy as np
# 原始数据
data = {
'salary': [20000, 60000, 70000, 75000, 80000, 60000, 120000, 80000, 20000, 50000]
}
# 将数据转换为二维数组格式
salaries = np.array(data['salary']).reshape(-1, 1)
# 初始化MinMaxScaler并归一化处理
scaler = MinMaxScaler(feature_range=(0, 1))
normalized_salaries = scaler.fit_transform(salaries)
# 输出结果
print("处理前数据:", data['salary'])
print("处理后数据:", normalized_salaries.flatten().tolist()) # 使用flatten()方法将二维数组转换为一维数组以便打印
处理前数据: [20000, 60000, 70000, 75000, 80000, 60000, 120000, 80000, 20000, 50000]
处理后数据: [0.0, 0.4, 0.5, 0.55, 0.6, 0.4, 1.0, 0.6, 0.0, 0.3]
3.1.1.3、独热编码(One-Hot)编码
独热编码(One-Hot)编码,又称为一位有效编码,是一种将分类变量转换为二进制向量的表示方法。这种方法主要是采用N位状态寄存器来对N个状态进行编码,每个状态都由其独立的寄存器位表示,并且在任意时候只有一位有效。
One-Hot编码过程如下:
- 首先,将分类值映射到整数值。例如,如果有三个工作类型,可以将它们映射为0、1和2。
- 然后,每个整数值被表示为二进制向量。对于上述的例子,三个工作类型可以表示为(1,0,0)、(0,1,0)和(0,0,1)。
One-Hot 编码适用于需要将分类变量转换为数值型变量的场景,例如在文本分类、推荐系统以及图像识别
中,将文本中的词汇、用户的兴趣爱好以及图像的标签转换为向量表示。此外,在使用逻辑回归、决策树等机器学习算法
时,One-Hot 编码可以应用于将离散特征的取值扩展到欧式空间,离散特征的某个取值就对应欧式空间的某个点,从而让特征之间的距离计算更加合理。
3.1.1.4、TF-IDF编码
TF-IDF(Term Frequency - Inverse Document Frequency)编码是一种常用的文本特征表示方法,用于衡量一个词在文档集合中的重要程度。
- 词频(TF):表示某个词在所在文档中出现的频率。一般来说,一个词在文档中出现的次数越多,它就越重要。但是,为了避免长文档中的词被过度重视,通常会对词频进行标准化处理。即词频 = 某个词在文档中出现的次数 / 文档的总词数。
- 逆文档频率(IDF):表示一个词在所有文档中的重要性。一个词如果在很多文档中都出现,那么它的IDF值就会较低;相反,如果一个词只在很少的文档中出现,那么它的IDF值就会较高。逆文档频率计算公式:IDF = log(总文档数 / 包含该词的文档数 + 1)。这里的加1是为了避免分母为零的情况。
示例1:Z-score数值标准化/归一化
3.1.2、离散化
有时候需要对数据进行离散化操作,比如决策树、朴素贝叶斯
等算法,不能直接处理连续型变量。再比如有些属性,希望处理数据成离散化区间。例如在处理年龄属性时,没有必要单独处理每一个年龄值,划分为几个区间会更便捷:“<30”、“30-60”和“>60”。通过离散化处理后,可以简化模型的复杂性,提高模型的计算效率,常用的离散化包括:
- 二值化:将连续数据转换为二值数据,其中每个数据点与某个阈值进行比较,大于阈值的设置为一个固定值(例如1),小于阈值的设置为另一个固定值(例如0)。二值化后的值的设置取决于具体场景。
- 等宽法:将连续变量的值域划分为具有相同宽度的区间。区间的个数可以由数据本身的特点决定,或者由用户指定。该方法类似于制作频率分布表。
- 等频法:将相同数量的记录放进每个区间。这意味着每个区间包含相同数量的数据点。该方法可以确保每个区间中的数据点数量相等,但可能导致某些区间包含许多数据,而其他区间的数据极少。
- 频率区间法:根据数据的频率分布进行排序,然后按照等频率或指定频率进行离散化。该方法将数据变换成均匀分布,但会改变原有数据的分布状态。
- 聚类法:例如使用K均值算法将样本集分为多个离散化的簇。这种方法基于数据的自然聚类或分组进行离散化。
- 卡方过滤:通过基于卡方的离散化方法,找出数据的最佳临近区间并合并,形成较大的区间。该方法旨在减少区间的数量,同时保持数据的重要特征。
示例1:一个简单的按年龄分段的离散化例子
import pandas as pd
# 创建一个包含年龄数据的数据框
data = {
'age': [25, 30, 15, 45, 60, 75, 90, 12, 18, 50]
}
df = pd.DataFrame(data)
# 定义年龄区间的边界
bins = [0, 12, 18, 60, float('inf')]
labels = ['儿童', '青少年', '成年人', '老年人']
# 使用pandas的cut函数进行离散化
df['age_category'] = pd.cut(df['age'], bins=bins, labels=labels, include_lowest=True)
# 打印离散化后的数据框
print(df)
示例1输出:
age age_category
0 25 成年人
1 30 成年人
2 15 青少年
3 45 成年人
4 60 成年人
5 75 老年人
6 90 老年人
7 12 儿童
8 18 青少年
9 50 成年人
示例1:等频法将学生成绩数据离散化
import pandas as pd
# 示例数据:某班级学生的考试成绩
data = { 'score': [85, 90, 78, 92, 88, 76, 95, 89, 77, 82] }
df = pd.DataFrame(data)
# 将数据转换为pandas series
scores_series = pd.Series(df['score'])
# 设置离散化区间数
num_bins = 3
# 使用等频法离散化数据,得到数据在每个区间的索引0-2
discretized_scores = pd.qcut(scores_series, q=num_bins, labels=False)
# 查看离散化后的数据
print("离散化后的数据:")
df['score_discret'] = pd.qcut(scores_series, q=num_bins, labels=False)
print(df)
# 查看离散化区间边界
bins = pd.qcut(scores_series, q=num_bins, retbins=True, labels=False)[1]
print("\n离散化区间边界:")
print(bins)
# 输出每个区间的范围及数据点数量
for i in range(num_bins):
mask = (discretized_scores == i)
bin_data = scores_series[mask]
print(f"区间 {i + 1}: {bins[i]} - {bins[i + 1]}, 数据点数量: {len(bin_data)}")
示例2输出:
离散化后的数据:
score score_discret
0 85 1
1 90 2
2 78 0
3 92 2
4 88 1
5 76 0
6 95 2
7 89 1
8 77 0
9 82 0
离散化区间边界:
[76. 82. 89. 95.]
区间 1: 76.0 - 82.0, 数据点数量: 4
区间 2: 82.0 - 89.0, 数据点数量: 3
区间 3: 89.0 - 95.0, 数据点数量: 3
3.1.3、特征构造
- 方法:生成新特征(如从日期中提取星期几)。
- 示例:将 购买日期 转换为 季节 或 是否为周末。
4、数据规约(Data Reduction)
目标:减少数据规模但保持完整性,同时保留关键信息。
4.1、具体内容:
4.1.1、属性(维)规约
- 方法:主成分分析(PCA)、删除低方差特征、逐步回归。
- 示例:用sklearn库中的鸢尾花数据集来做降维分析,数据集共有数据150组,每组包括花萼长(sepal lenth)、花萼宽(sepal width)、花瓣长(petal length)、花瓣宽(petal width)4个输入特征,同时给出了这一组特征对应的鸢尾花类别,类别包括Setosa Iris(狗尾花鸢尾),Versicolour Iris(杂色鸢尾),Virgincica Iris(弗吉尼亚鸢尾)三种类别,分别用0,1,2表示。
我们先用聚类方法处理数据集,然后用数据集的前两个特征输出聚类结果。下图中可以看出三种鸢尾花的聚类结果。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.cluster import KMeans
# 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data # 特征数据(4列)
y = iris.target # 真实类别标签(0,1,2)
feature_names = iris.feature_names[:2] # 前两个特征名(花萼长和花萼宽)
# 仅使用前两个特征(花萼长、花萼宽)
X_first_two = X[:, :2]
# 使用 K-means 聚类(假设分3类,与真实类别一致)
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(X_first_two)
cluster_labels = kmeans.predict(X_first_two)
plt.figure(figsize=(12, 5))
# 子图1:真实类别分布
plt.subplot(1, 2, 1)
plt.scatter(X_first_two[:, 0], X_first_two[:, 1], c=y, cmap="viridis", edgecolor="k")
plt.xlabel(feature_names[0])
plt.ylabel(feature_names[1])
plt.title("True Labels (0=Setosa, 1=Versicolour, 2=Virginica)")
plt.show()
示例:下面我们用PCA降维方法将鸢尾花数据集的四个特征降维到二维,然后再用聚类方法输出聚类结果。
import numpy as np
from sklearn import datasets
from sklearn.decomposition import PCA
# 加载鸢尾花数据集
iris = datasets.load_iris()
# 获取特征矩阵和目标向量
X = iris.data
y = iris.target
# 创建 PCA 实例,保留2个主成分
pca = PCA(n_components=2)
pca.fit(X)
# 投影数据到低维空间
X2 = pca.fit_transform(X)
from sklearn.cluster import AgglomerativeClustering
import matplotlib.pyplot as plt
# 定义层次聚类模型,距离计算使用欧式距离,簇间隔采用ward连接算法
clustering = AgglomerativeClustering(n_clusters=3, affinity='euclidean', linkage='ward')
# 拟合数据并预测聚类标签
labels = clustering.fit_predict(X2)
# 可视化聚类结果
plt.scatter(X2[:, 0], X2[:, 1], c=labels, cmap='viridis')
plt.xlabel('PCA X1')
plt.ylabel('PCA X2')
plt.title('Iris dataset clustering')
plt.show()
示例输出:
4.1.2、数值(数量)规约
- 方法:聚类抽样、分层抽样、直方图分箱。
- 示例:从 100 万条数据中随机抽取 10% 进行分析。
4.1.3、数据压缩
使用压缩算法减少数据存储空间