在数据挖掘过程中,海量的原始数据存在大量的不一致,有缺失的数据,严重影响到数据挖掘的效率和准确率,数据清洗尤为重要,数据清洗之后进行或同时进行数据集成,转换,规约等一系列过程,该过程就是数据预处理 .数据预处理一方面提高数据质量,另一方面使数据更好地适应特定的数据挖掘或者工具.
数据清洗就是删除掉原始数据集中的无关数据,重复数据,平滑噪声数据,筛选掉与挖掘主题无关的数据,处理缺失值和异常值等.
处理缺失值包括:删除记录,数据插补,不处理三种方法.其中常见的插补方法:
插补方法 | 方法描述 |
---|---|
均值/中位数/众数插补 | 根据属性值的类型,用该属性值的平均值/中位数/众数插补 |
使用固定值 | 将缺失的值使用固定值替换,譬如一年级学生年龄,缺失部分用当地入学平均年龄 |
最近临插补 | 在记录中找见与缺失样本最接近样本的属性值进行插补 |
回归方法 | 通过已有数据与其有关的其他变量(因变量)的数据建立拟合模型来预测缺失值 |
插值法 | 利用已知点建立合适的插值函数f(x),未知值由对应的点x求出的函数值f(x)近似代替. |
在数据集记录比较少的情况下,删除小部分记录会丢弃大量隐藏在其中的有用信息,一些模型可以将缺失值作为一种特殊取值,允许在含有缺失值的模型上进行建模,本节介绍拉格朗日插值法和牛顿插值法,其他插值法还有Hermite插值,分段插值,样条插值法等.
下面结合具体案例介绍拉格朗日插值的计算方法,在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.
直接将异常值删除会在记录很少的情况下导致样本量不足,造成结果不准确,视为缺失值的好处是可以利用现有信息进行填补,在很多情况下,先分析异常值出现原因,在判断是否舍弃,异常值处理常用方法:
异常值处理方法 | 方法描述 |
---|---|
删除含有异常值的记录 | 直接将数据删除 |
视为缺失值 | 利用缺失值处理的方法进行插补 |
平均值修正 | 前后两个记录的均值进行修正 |
不处理 | 直接在具有异常数据集上进行数据挖掘(异常为正确数据) |
数据挖掘的数据往往在不同的数据源中存储,数据集成就是将多个数据源的数据存储在同一数据仓库的过程,有可能会存在匹配问题,实体识别问题,属性冗余问题,需要将源数据在底层上加以转换提炼和集成.
实体识别是在不同的源数据中识别出现实世界的实体,任务是统一不同源数据的矛盾之初,常见的形式如下:
检测和解决这些冲突是实体识别的任务.
数据集成往往会导致数据冗余出现:
对于冗余属性,要先分析,检测到后再将其删除,有些冗余属性可以使用相关性分析检测,使用相关系数来衡量一个属性值在多大程度影响另一个属性.
数据的变换主要是对数据的规范化处理,使数据适合数据挖掘的算法.
简单函数变化是对数据进行某些简单的数学变换,常用的变换包括平方,开平方,取对数,差分运算等:
简单的函数变换将不具有正态分布的数据变为正态分布,在时间序列中,使用对数变换或者差分运算将非平稳序列变为平稳序列.在数据挖掘中,简单的函数变换可以将个人年收入从1w到10亿使用对数变换进行压缩.
数据规范化(归一化)是为了消除数据挖掘的多个评价模型之间的量纲和取值范围的影响,将其标准化处理,将数据按照比例进行缩放,使之落入一个特定区域,便于综合分析,其对基于距离的挖掘算法尤为重要.
下面通过实例比较三种规范方法处理结果:
#-*- 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())) #小数定标规范化
某些分类的数据挖掘算法(如ID3算法,Apriori算法等),需要数据是分类属性形式,所以,将连续属性变换成分类属性叫做连续属性离散化.
连续属性离散化是在数据的取值范围内设定若干个离散的划分点,将取值范围变为离散化的区间,最后用不同的符号或整数值代表每个子区间的数据集.涉及到两步:确定划分点,将连续属性值映射到离散区间.
常用离散方法包括等宽法,等频法和(一维)聚类.
使用以上三种方法对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()
在数据挖掘过程中,为了提取更有用的信息,我们需要利用已有属性构造新的属性,并加入到现有属性集合中.例如在电力漏电用户识别中,已有属性包括供入电量,供出电量(用电量之和),理论上二者应该相等,如果该条线路出现供入电量明显大于供出电量,则表明存在窃漏电行为.为了判断用户是否窃漏电,可以构造一个新的指标-线损率,即(供入电量-供出电量)/供入电量 *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) #保存结果
小波变化在时域和频域都具有表征信号局部特征的能力,而不是傅里叶变换(将信号分为多个不同频率正弦函数)只具有频域分析,提供一种非平稳信号的时频分析手段,可以从信号提取更多有用信息.信号的特征量往往隐藏在某个或某些分量重,小波变换将非平稳信号分解为不同层次不同频率的数据序列,即小波系数.选择适当的小波系数,既可以完成信号的特征量提取.下面介绍信号特征提取方法:
基于小波变换的特征提取方法,详见下表:
基于小波变换的特征提取方法 | 方法描述 |
---|---|
基于小波变换的多尺度空间能量分布特征提取方法 | 各尺度空间内的平滑信号和细节信号能提供原始信号的时频局域信息,特别是提供不同频段的信号的构成信息,将能量尺度按顺序排列,形成特征量 |
基于小波变换的多尺度空间的模极大值特征量提取方法 | 通过小波变换的局域化分析能力,将小波变换模极大值的尺度参数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个数字,第一个数组为逼近系数数组,后面的依次是细节系数数组
数据规约可以产生更小但保持原数据完整性的新数据集,在规约后的数据集上进行分析和挖掘将更有效率.
属性规约通过属性合并来创建新的属性维数,或者直接删除不相关的属性来减少数据维数.属性规约的目标是寻找出最小的属性子集并确保新数据集子集的概率分布尽可能接近原来数据集的概率分布,属性规约的常见方法如下图:
属性规约方法 | 方法描述 | 方法解析 |
---|---|---|
合并属性 | 将旧属性合并新属性 | 初始属性集{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),参数说明
运行以上程序获得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]])
数值规约指通过选择替代的,较小的数据来减少数据量,包括有参数方法和无参数方法两类.有参数方法是使用一个模型来评估数据,只需存放参数,而不需存放实际数据,例如回归(线性回归和多元回归)和对数线性模型(近似离散属性集中的多维概率分布).无参数方法需要存放实际数据,例如直方图,聚类,抽样(采样).
下面给出本文用到的数据预处理函数:
函数名 | 函数功能 | 所属扩展库 |
---|---|---|
interpolate | 一维,高维数据插值 | Scipy |
unique | 除去数据中重复元素,得到单值元素列表,对象方法名 | Pandas/Numpy |
isnull | 判断是否空值 | Pandas |
notnull | 判断是否非空值 | Pandas |
PCA | 对指标变量矩阵进行主要成分分析 | Scikit-Learn |
random | 生成随机矩阵 | Numpy |