在数据挖掘过程中,海量的原始数据存在大量的不一致,有缺失的数据,严重影响到数据挖掘的效率和准确率,数据清洗尤为重要,数据清洗之后进行或同时进行数据集成,转换,规约等一系列过程,该过程就是数据预处理 .数据预处理一方面提高数据质量,另一方面使数据更好地适应特定的数据挖掘或者工具.
1. 数据清洗
数据清洗就是删除掉原始数据集中的无关数据,重复数据,平滑噪声数据,筛选掉与挖掘主题无关的数据,处理缺失值和异常值等.
1.1 缺失值处理
处理缺失值包括:删除记录,数据插补,不处理三种方法.其中常见的插补方法:
插补方法 | 方法描述 |
均值/中位数/众数插补 | 根据属性值的类型,用该属性值的平均值/中位数/众数插补 |
使用固定值 | 将缺失的值使用固定值替换,譬如一年级学生年龄,缺失部分用当地入学平均年龄 |
最近临插补 | 在记录中找见与缺失样本最接近样本的属性值进行插补 |
回归方法 | 通过已有数据与其有关的其他变量(因变量)的数据建立拟合模型来预测缺失值 |
插值法 | 利用已知点建立合适的插值函数f(x),未知值由对应的点x求出的函数值f(x)近似代替. |
在数据集记录比较少的情况下,删除小部分记录会丢弃大量隐藏在其中的有用信息,一些模型可以将缺失值作为一种特殊取值,允许在含有缺失值的模型上进行建模,本节介绍拉格朗日插值法和牛顿插值法,其他插值法还有Hermite插值,分段插值,样条插值法等.
- 拉格朗日插值法,也就是通过已知n个点得出经过该n个点的近似函数,把缺失值x代入,求出其缺失的近似属性,使用的数学原理被叫做拉格朗日插值法.
根据数学知识,对于平面n个点可以找到n-1次多项式:
使得该函数满足这n个点(别问为什么,记住,这是定理),所以将n个点代入该多项式:
解出该方程,得到函数:
将缺失值x代入该函数可以得到属性值的近似值,缺陷是当插值节点增减时,解得函数会发生变化,在实际计算中非常不方便,所以提出了牛顿插值法. - 牛顿插值法
根据数学知识得到n个点的所有的阶差商公式:
联立求解以上差商公式求得P(x)牛顿插值逼近函数,R(x)误差函数:
将缺失值代入函数即可获得近似值,从本质上来说两者给出的结果是一致的,都是基于多项式的插值,只不过表示的形式不同,因此,在Scipy库中,只提供比较容易实现的拉格朗日插值法函数.
下面结合具体案例介绍拉格朗日插值的计算方法,在demo/data/catering_sale.xls中2.14出现数据缺失,我们可以使用拉格朗日插值法进行插补,代码:
#拉格朗日插值代码
import pandas as pd #导入数据分析库Pandas
from scipy.interpolate import lagrange #导入拉格朗日插值函数
inputfile = '../data/catering_sale.xls' #销量数据路径
outputfile = '../tmp/sales.xls' #输出数据路径
data = pd.read_excel(inputfile) #读入数据
data[u'销量'][(data[u'销量'] < 400) | (data[u'销量'] > 5000)] = None #过滤异常值,将其变为空值
#自定义列向量插值函数
#s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5
def ployinterp_column(s, n, k=5):
y = s[list(range(n-k, n)) + list(range(n+1, n+1+k))] #取数,前后5个,共10个
y = y[y.notnull()] #剔除空值,获得取数的列表
return lagrange(y.index, list(y))(n) #插值并返回插值结果
#逐个元素判断是否需要插值
for i in data.columns: #获得每一列
for j in range(len(data)): #获得数据行数,遍历一列中每一行数据
if (data[i].isnull())[j]: #如果为空即插值。
data[i][j] = ployinterp_column(data[i], j)#传入该列和第几个是空值
data.to_excel(outputfile) #输出结果,写入文件
在进行插值之前会进行异常值检测,将异常值2.21的属性设为空值,同时与2.14进行插补,获得插补值4275.255和4156.86.
1.2 异常值处理
直接将异常值删除会在记录很少的情况下导致样本量不足,造成结果不准确,视为缺失值的好处是可以利用现有信息进行填补,在很多情况下,先分析异常值出现原因,在判断是否舍弃,异常值处理常用方法:
异常值处理方法 | 方法描述 |
删除含有异常值的记录 | 直接将数据删除 |
视为缺失值 | 利用缺失值处理的方法进行插补 |
平均值修正 | 前后两个记录的均值进行修正 |
不处理 | 直接在具有异常数据集上进行数据挖掘(异常为正确数据) |
2. 数据集成
数据挖掘的数据往往在不同的数据源中存储,数据集成就是将多个数据源的数据存储在同一数据仓库的过程,有可能会存在匹配问题,实体识别问题,属性冗余问题,需要将源数据在底层上加以转换提炼和集成.
2.1 实体识别
实体识别是在不同的源数据中识别出现实世界的实体,任务是统一不同源数据的矛盾之初,常见的形式如下:
- 同名异义
数据源A中的属性ID和数据源B中的属性ID分别描述的是客户编号和订单编号,即描述不同实体. - 异名同义
数据源A的student_name和数据源B中的borrowing_card_name都是描述名字,即student_name=borrowing_card_name. - 单位不统一
描述一个实体使用不同单位.
检测和解决这些冲突是实体识别的任务.
2.2 冗余属性识别
数据集成往往会导致数据冗余出现:
- 同一属性多次出现.
- 同一属性不同名字多次出现.
对于冗余属性,要先分析,检测到后再将其删除,有些冗余属性可以使用相关性分析检测,使用相关系数来衡量一个属性值在多大程度影响另一个属性.
3. 数据变换
数据的变换主要是对数据的规范化处理,使数据适合数据挖掘的算法.
3.1 简单函数变换
简单函数变化是对数据进行某些简单的数学变换,常用的变换包括平方,开平方,取对数,差分运算等:
简单的函数变换将不具有正态分布的数据变为正态分布,在时间序列中,使用对数变换或者差分运算将非平稳序列变为平稳序列.在数据挖掘中,简单的函数变换可以将个人年收入从1w到10亿使用对数变换进行压缩.
3.2 规范化
数据规范化(归一化)是为了消除数据挖掘的多个评价模型之间的量纲和取值范围的影响,将其标准化处理,将数据按照比例进行缩放,使之落入一个特定区域,便于综合分析,其对基于距离的挖掘算法尤为重要.
- 最大-最小规范化
最大最小规范化也叫做离差标准化.对原始数据进行线性变换,映射到[0,1]之间.转换公式:
max为样本最大值,min为样本最小值,max-min为极差.离差标准化是消除量纲保留数据关系的最简单方法,缺点是遇到新的值超过边界值会出现系统错误,需要重新计算. - 零-均值规范化
又叫做标准差标准化,是目前用到的最多的数据标准化方法,经过处理的数据均值为0,标准差为1,其中x-原始数据的均值,之后除以原始数据标准差,公式: - 小数定标规范化
移动属性值的小数点,将其属性值映射到[-1,1]之间,移动的小数位数取决于属性值绝对值的最大值,公式为:
下面通过实例比较三种规范方法处理结果:
#-*- coding: utf-8 -*-
#数据规范化
import pandas as pd
import numpy as np
datafile = '../data/normalization_data.xls' #参数初始化
data = pd.read_excel(datafile, header = None) #读取数据
(data - data.min())/(data.max() - data.min()) #最小-最大规范化
(data - data.mean())/data.std() #零-均值规范化
data/10**np.ceil(np.log10(data.abs().max())) #小数定标规范化
3.3 连续属性离散化
某些分类的数据挖掘算法(如ID3算法,Apriori算法等),需要数据是分类属性形式,所以,将连续属性变换成分类属性叫做连续属性离散化.
3.3.1 离散化过程
连续属性离散化是在数据的取值范围内设定若干个离散的划分点,将取值范围变为离散化的区间,最后用不同的符号或整数值代表每个子区间的数据集.涉及到两步:确定划分点,将连续属性值映射到离散区间.
3.3.2 常用离散方法
常用离散方法包括等宽法,等频法和(一维)聚类.
- 等宽法,将属性值域划分相同宽度区间,区间个数有数据本身特点决定,或者有用户决定.
- 等频法:将相同数量记录放在每个区间.
以上两种方法易于操作,缺点在于等宽法对离群点比较敏感,区间数据分布不均衡,破坏决策模型.等频法可能将同一数据复制到不同区间满足个数要求. - 基于聚类分析的方法,一维聚类包括两个步骤,首先将连续的属性的值用聚类算法(如K-Means算法)进行聚类,然后将聚类得到的簇进行处理,合并到一个簇的连续属性值做同一标记.需要用户来指定簇个数,从而决定产生的区间数.
使用以上三种方法对demo/data/discretization_data.xls连续属性离散化,将数据分成4类,然后将每一类打上标识,具体代码:
#-*- coding: utf-8 -*-
#数据规范化
import pandas as pd
datafile = '../data/discretization_data.xls' #参数初始化
data = pd.read_excel(datafile) #读取数据
data = data[u'肝气郁结证型系数'].copy()
k = 4
d1 = pd.cut(data, k, labels = range(k)) #等宽离散化,各个类比依次命名为0,1,2,3
#等频率离散化
w = [1.0*i/k for i in range(k+1)]
w = data.describe(percentiles = w)[4:4+k+1] #使用describe函数自动计算分位数
w[0] = w[0]*(1-1e-10)
d2 = pd.cut(data, w, labels = range(k))
from sklearn.cluster import KMeans #引入KMeans
kmodel = KMeans(n_clusters = k, n_jobs = 4) #建立模型,n_jobs是并行数,一般等于CPU数较好
kmodel.fit(data.reshape((len(data), 1))) #训练模型
c = pd.DataFrame(kmodel.cluster_centers_).sort(0) #输出聚类中心,并且排序(默认是随机序的)
w = pd.rolling_mean(c, 2).iloc[1:] #相邻两项求中点,作为边界点
w = [0] + list(w[0]) + [data.max()] #把首末边界点加上
d3 = pd.cut(data, w, labels = range(k))
def cluster_plot(d, k): #自定义作图函数来显示聚类结果
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
plt.figure(figsize = (8, 3))
for j in range(0, k):
plt.plot(data[d==j], [j for i in d[d==j]], 'o')
plt.ylim(-0.5, k-0.5)
return plt
cluster_plot(d1, k).show()
cluster_plot(d2, k).show()
cluster_plot(d3, k).show()
3.4 属性构造
在数据挖掘过程中,为了提取更有用的信息,我们需要利用已有属性构造新的属性,并加入到现有属性集合中.例如在电力漏电用户识别中,已有属性包括供入电量,供出电量(用电量之和),理论上二者应该相等,如果该条线路出现供入电量明显大于供出电量,则表明存在窃漏电行为.为了判断用户是否窃漏电,可以构造一个新的指标-线损率,即(供入电量-供出电量)/供入电量 *100%,一般正常范围在3%-15%.根据计算公式,由供入电量,供出电量进行线损率属性构造代码:
import pandas as pd
#参数初始化
inputfile= '../data/electricity_data.xls' #供入供出电量数据
outputfile = '../tmp/electricity_data.xls' #属性构造后数据文件
data = pd.read_excel(inputfile) #读入数据
data[u'线损率'] = (data[u'供入电量'] - data[u'供出电量'])/data[u'供入电量']
data.to_excel(outputfile, index = False) #保存结果
3.5 小波变换
小波变化在时域和频域都具有表征信号局部特征的能力,而不是傅里叶变换(将信号分为多个不同频率正弦函数)只具有频域分析,提供一种非平稳信号的时频分析手段,可以从信号提取更多有用信息.信号的特征量往往隐藏在某个或某些分量重,小波变换将非平稳信号分解为不同层次不同频率的数据序列,即小波系数.选择适当的小波系数,既可以完成信号的特征量提取.下面介绍信号特征提取方法:
基于小波变换的特征提取方法,详见下表:
基于小波变换的特征提取方法 | 方法描述 |
基于小波变换的多尺度空间能量分布特征提取方法 | 各尺度空间内的平滑信号和细节信号能提供原始信号的时频局域信息,特别是提供不同频段的信号的构成信息,将能量尺度按顺序排列,形成特征量 |
基于小波变换的多尺度空间的模极大值特征量提取方法 | 通过小波变换的局域化分析能力,将小波变换模极大值的尺度参数s,平移参数t和其幅值作为目标的特征量. |
基于小波包变换的特征提取方法 | 利用小波分解,将时域随机信号序列映射到尺度域各子空间随机系数序列,将不确定性程度最低,将最佳子空间的熵值和二叉树位置参数作为特征量. |
基于适应性小波神经网络的特征提取方法 | 把信号通过分析小波拟合表示,进行特征量提取 |
小波基函数:小波基函数具有局部支持集的函数,平均值为0,常见的小波基函数有Hear小波基,db系列小波基等.
在Python中Scipy本身提供一系列信号处理函数,但不全面,而更好的实现库是pywt,具体示例:
#-*- coding: utf-8 -*-
#利用小波分析进行特征分析
#参数初始化
inputfile= '../data/leleccum.mat' #提取自Matlab的信号文件
from scipy.io import loadmat #mat是MATLAB专用格式,需要用loadmat读取它
mat = loadmat(inputfile)
signal = mat['leleccum'][0]
import pywt #导入PyWavelets
coeffs = pywt.wavedec(signal, 'bior3.7', level = 5)
#返回结果为level+1个数字,第一个数组为逼近系数数组,后面的依次是细节系数数组
4.数据规约
数据规约可以产生更小但保持原数据完整性的新数据集,在规约后的数据集上进行分析和挖掘将更有效率.
4.1 属性规约
属性规约通过属性合并来创建新的属性维数,或者直接删除不相关的属性来减少数据维数.属性规约的目标是寻找出最小的属性子集并确保新数据集子集的概率分布尽可能接近原来数据集的概率分布,属性规约的常见方法如下图:
属性规约方法 | 方法描述 | 方法解析 |
合并属性 | 将旧属性合并新属性 | 初始属性集{A1,A2,B1,B2,C}规约后的属性集{A,B,C} |
逐步向前选择 | 从一个空的属性集开始,逐步从原有属性集中选择当前最优属性添加到空集中,直至没有或者满足阈值约束 | 初始属性集{A1,A2,A3,A4,A5},{} => {A1} => {A1,A4} => 规约后的{A1,A4,A5} |
逐步向后删除 | 从当前属性集开始,逐步删除当前最差属性,直至没有或者满足阈值约束 | 初始属性集{A1,A2,A3,A4,A5}=>{A1,A3,A4,A5}=>规约后的属性集{A1,A4,A5} |
决策树归纳 | 利用决策树归纳对初始集进行归纳学习,删除没有出现的数据属性 | 初始属性集{A1,A2,A3,A4,A5}=>A4决策树归纳=>规约后的属性集{A1,A4,A5} |
主成分分析 | 用较少的变量去解释原始数据中的大部分变量,即将许多相关性高的变量转化为不相关变量 | 详见下例 |
前三种方法都是直接删除不相关属性方法,主成分分析是一种用于连续属性的数据降维方法.通过正交变换,去除原始空间基底下的相关性,只使用少量变量即可解释原始数据中的大部分变异,少量变量即被称为主成分,来代替原始变量进行建模,计算步骤我们略过,具体可以参考图书Python数据分析与挖掘实战,我们来介绍分装后的代码:
#-*- coding: utf-8 -*-
#主成分分析 降维
import pandas as pd
#参数初始化
inputfile = '../data/principal_component.xls'
outputfile = '../tmp/dimention_reducted.xls' #降维后的数据
data = pd.read_excel(inputfile, header = None) #读入数据
from sklearn.decomposition import PCA
pca = PCA()
pca.fit(data)
pca.components_ #返回模型的各个特征向量
pca.explained_variance_ratio_ #返回各个成分各自的方差百分比
其中PCA()函数默认参数为sklearn.decomposition.PCA(n_components = None,copy = True , whiten = False),参数说明
- n_components为算法中保留的主成分个数,也就是保留下来的特征个数n,int或者string,缺省时所有的成分保留,n_components=1,将把原始数据维度降到1,n_components=‘mle’,自动选取特征个数,使得满足所要求的的方差百分比.
- copy类型为bool,默认为True,表示是否将原始数据复制一份,默认对原始数据不会有任何改变.
- whiten类型为bool,默认为False,白化,使得每个特征具有相同的方法.
运行以上程序获得8个特征根,对应的单位特征向量和各自的方差百分比(贡献率),其中贡献率越大,向量权重越大:
>>> pca.components_ #返回模型的各个特征向量
array([[ 0.56788461, 0.2280431 , 0.23281436, 0.22427336, 0.3358618 ,
0.43679539, 0.03861081, 0.46466998],
[ 0.64801531, 0.24732373, -0.17085432, -0.2089819 , -0.36050922,
-0.55908747, 0.00186891, 0.05910423],
[-0.45139763, 0.23802089, -0.17685792, -0.11843804, -0.05173347,
-0.20091919, -0.00124421, 0.80699041],
[-0.19404741, 0.9021939 , -0.00730164, -0.01424541, 0.03106289,
0.12563004, 0.11152105, -0.3448924 ],
[-0.06133747, -0.03383817, 0.12652433, 0.64325682, -0.3896425 ,
-0.10681901, 0.63233277, 0.04720838],
[ 0.02579655, -0.06678747, 0.12816343, -0.57023937, -0.52642373,
0.52280144, 0.31167833, 0.0754221 ],
[-0.03800378, 0.09520111, 0.15593386, 0.34300352, -0.56640021,
0.18985251, -0.69902952, 0.04505823],
[-0.10147399, 0.03937889, 0.91023327, -0.18760016, 0.06193777,
-0.34598258, -0.02090066, 0.02137393]])
>>> pca.explained_variance_ratio_ #返回各个成分各自的方差百分比
array([7.74011263e-01, 1.56949443e-01, 4.27594216e-02, 2.40659228e-02,
1.50278048e-03, 4.10990447e-04, 2.07718405e-04, 9.24594471e-05])
通过贡献率分析,选取前3个就已经大于97.3%,因此重建PCA模型设置n_components=3,计算结果.
#-*- coding: utf-8 -*-
#主成分分析 降维
import pandas as pd
#参数初始化
inputfile = '../data/principal_component.xls'
outputfile = '../tmp/dimention_reducted.xls' #降维后的数据
data = pd.read_excel(inputfile, header = None) #读入数据
from sklearn.decomposition import PCA
pca = PCA(3)
pca.fit(data)
data
low_d = pca.transform(data)
pd.DataFrame(low_d).to_excel(outputfile) #保存结果
pca.inverse_transform(low_d) #必要时复原数据
原始数据从8维降为3维,同时这3维占据了原始数据97%以上的信息.
>>> low_d
array([[ 8.19133694, 16.90402785, 3.90991029],
[ 0.28527403, -6.48074989, -4.62870368],
[-23.70739074, -2.85245701, -0.4965231 ],
[-14.43202637, 2.29917325, -1.50272151],
[ 5.4304568 , 10.00704077, 9.52086923],
[ 24.15955898, -9.36428589, 0.72657857],
[ -3.66134607, -7.60198615, -2.36439873],
[ 13.96761214, 13.89123979, -6.44917778],
[ 40.88093588, -13.25685287, 4.16539368],
[ -1.74887665, -4.23112299, -0.58980995],
[-21.94321959, -2.36645883, 1.33203832],
[-36.70868069, -6.00536554, 3.97183515],
[ 3.28750663, 4.86380886, 1.00424688],
[ 5.99885871, 4.19398863, -8.59953736]])
4.2 数值规约
数值规约指通过选择替代的,较小的数据来减少数据量,包括有参数方法和无参数方法两类.有参数方法是使用一个模型来评估数据,只需存放参数,而不需存放实际数据,例如回归(线性回归和多元回归)和对数线性模型(近似离散属性集中的多维概率分布).无参数方法需要存放实际数据,例如直方图,聚类,抽样(采样).
- 直方图,直方图使用分箱来近似数据分布,属性A的直方图将属性A的数据划分为互斥的子集(桶),如果每个桶只代表单个数据,这个桶叫做单桶.通常,桶是一个连续区间.如果数据集A的值{1,2,3,4,5,6,7,8,9,2,4,5,6,7,8,4,5,6,7,5},第一张图使用单桶显示数据直方图:
为了进一步压缩数据,通常让每一个桶代表一个连续值域,例如每一桶代表3的区间,可以分为三组,1-3,4-6,7-9: - 聚类:聚类技术将数据元组(即记录Series,数据表中每一行)视为处理对象,它将对象划分为簇,使一个簇中对象相互相似,而与其他对象簇相斥.在数据规约中,将用数据的簇替换实际数据,该技术的有效性依赖于簇的定义是否符合数据的分布性质.
- 抽样: 抽样用比原始数据小的多的随机样本数据集代表原始数据.假定原始数据集D包括N个元组,抽取S个样本下面介绍常用方法:
S个样本无放回简单随机抽样:从D的N个元组中抽取S个样本,其中D中任意元组被抽取概率为1/N,即抽取等可能性.
S个样本有放回简单随机抽样:每一次元组从D中抽取后,记录它,然后返回原处.
聚类抽样:将D中元素划分为N个互斥的簇,可以进行S个簇的简单随机抽样,得到S后在对每一簇抽取.
分层抽样:将D中元素划分为N个互斥的层,可以进行S个层的简单随机抽样,得到S后在对每一层抽取.
用于数据规约时,抽样经常被用来估计聚集查询结果,通过中间极限定理指定固定样本大小,通过改变样本使集合求精. - 参数回归:简单线性模型和对数线性模型可以用来近似描述给定的数据.简单的线性模型对数据建模,使之拟合直线,通过最小二乘法得到斜率和截距.对数线性模型用于维规约(由于低维空间的点通常比原来点占据较少空间)和数据光滑(与较高维空间估计相比低维空间的聚集估计较少受抽样方差影响).
5. Python主要数据预处理函数
下面给出本文用到的数据预处理函数:
函数名 | 函数功能 | 所属扩展库 |
interpolate | 一维,高维数据插值 | Scipy |
unique | 除去数据中重复元素,得到单值元素列表,对象方法名 | Pandas/Numpy |
isnull | 判断是否空值 | Pandas |
notnull | 判断是否非空值 | Pandas |
PCA | 对指标变量矩阵进行主要成分分析 | Scikit-Learn |
random | 生成随机矩阵 | Numpy |
- interpolate,是Scipy的一个子库,包括大量插值函数,如拉格朗日插值,样条插值,高维插值等,使用前需要导入 from scipy.interpolate import *引入对应插值函数,示例拉格朗日f=scipy.interpolate.lagrange(x,y),x,y为对应的因变量自变量数据.
- unique,除去数据中重复元素,得到单值元素列表,既是Numpy的函数(np.unique()),又是Series方法.使用格式为np.unique(D),D是一维数组,可以是list,array,Series.也可以D.unique(),D是Pandas的Series对象.
- isnull/notnull,判断每一个元素是否是空值/非空值,使用格式D.isnull()/D.notnull(),D为Series对象,返回一个布尔Series.通过D[D.isnull()]或者D[D.notnull()]找出D中的空值或非空值.
- random是Numpy的一个子库,可以生成服从特定分布的随机矩阵,抽样时使用,使用格式np.random.rand(k,m,n,…),生成kmn*…随机矩阵,其元素均匀分布在(0,1)区间上,np.randn(k,m,n,…),生成kmn*…随机矩阵,其元素服从标准正态分布.
- PCA对指标变量矩阵上进行主成分分析,使用前需要from sklearn.decomposition import PCA引入该函数,使用格式model = PCA().在Scikit-Learn下的PCA是一个建模式对象,也就是符合一般建模流程,首先PCA()建模,然后model.fit(D)训练模型,D是进行主成分分析矩阵,训练结束后获得模型参数,如.components_获取特征向量,以及.explained_variance_ratio_获取各个属性贡献.