本文主要介绍数据分析与挖掘中的数据预处理知识点:包括各类数据缺失值填充、数据类型转换、函数值转换、贝叶斯插值法等
在数据挖掘中,海量的原始数据中存在着大量不完整(有缺失值)、不一致、有异常的数据,严重影响到数据挖掘建模的执行效率,甚至可能导致挖掘结果的偏差,所以进行数据清洗就显得尤为重要,数据清洗完成后接着进行或者同时进行数据集成、转换、规约等一系列的处理,该过程就是数据预处理。数据预处理一方面是要提高数据的质量,另一方面是要让数据更好地适应特定的挖掘技术或工具。统计发现,在数据挖掘的过程中,数据预处理工作量占到了整个过程的60%。
数据预处理的主要内容包括:数据清洗、数据集成、数据变换和数据规约
数据清洗主要是删除原始数据集中无关数据、重复数据、平滑噪声数据,筛选掉与挖掘主题无关的数据,处理缺失值、异常值等。
a、缺失值处理:
插补方法 | 方法描述 |
---|---|
均值/中位数/众数插补 | 根据属性值的类型,用该属性取值的平均数/中位数/众数进行插补 |
使用固定值 | 将缺失的属性值用一个常量替换。如广州一个工厂普通外来务工人员的“基本工资”属性的空缺值可以用2015年广州市普通外来务工人员工资标准1895元/月,该方法就是使用固定值 |
最近临插补 | 在记录中找到与缺失样本最接近的样本的该属性值插补 |
回归方法 | 对带有缺失值的变量,根据已有数据和与其有关的其他变量(因变量)的数据建立拟合模型来预测缺失的属性值 |
插值法 | 插值法是利用已知点建立合适的插值函数f(x),未知值由对应点x1求出的函数值f(x1)近似代替 |
如果通过简单的删除小部分记录达到既定的目标,那么删除含有缺失值的记录的方法是最有效的。然而,这种方法却有很大的局限性。它是以减少历史数据来换取数据的完备,会造成资源的大量浪费,将丢弃了大量隐藏在这些记录中的信息。尤其在数据集本来就包含很少记录的情况下,删除少量记录可能会严重影响到分析结果的客观性和正确性。一些模型可以将缺失值视作一种特殊的取值,允许直接在含有缺失值的数据上进行建模。
两种插值法:
例子:
#拉格朗日插值代码
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))] #取数
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) #输出结果,写入文件
应用拉格朗日插值法算对表中的缺失值进行插补,使用缺失值前后各5个未缺失的数据参与建模,得插值结果如下所示。
时间 | 原始值 | 插值 |
---|---|---|
2015/2/21 | 6607.4 | 4275.255 |
2015/2/14 | 空值 | 4156.86 |
在进行插值之前会对数据进行异常值检测,发现2015/2/21日的数据是异常的(数据大于5000),所以也把此日期数据定义为空缺值,进行补数。利用拉格朗日插值对这2015/2/21和2015/2/14的数据进行插补,结果是4275.255和4156.86,这两天都是周末,而周末的销售额一般要比周一到周五要多,所以插值结果比较符合实际情况。
b、异常值处理
异常值的常见处理方法如下:
异常值处理方法 | 方法描述 |
---|---|
删除含有异常值的记录 | 直接将含有异常值的记录删除 |
视为缺失值 | 将异常值视为缺失值,利用缺失值处理的方法进行处理 |
平均值修正 | 可用前后两个观测的平均值修正该异常值 |
不处理 | 直接具有异常的数据集上进行挖掘建模 |
将含有异常值的记录直接删除的方法简单易行,但缺点也很明显,在观测值很少的情况下,这种删除会造成样本量不足,可能会改变变量的原有分布,从而造成分析结果的不准确。视为缺失值处理的好处是可以利用现有变量的信息,对异常值(缺失值)进行填补。
在很多情况下,要先分析异常值出现的可能原因,再判断异常值是否应该舍弃,如果是正确的数据,可以直接在具有异常值的数据集上进行挖掘建模。
数据挖掘需要的数据往往分布在不同的数据源中,数据集成就是将多个数据源合并存放在一个一致的数据存储(如数据仓库)中的过程。
在数据集成时,来自多个数据源的现实世界实体的表达形式是不一样的,有可能不匹配,要考虑实体识别问题和属性冗余问题,从而将源数据在最低层上加以转换、提炼和集成。
a、实体识别
是指从不同数据源识别出现实世界的实体,他的任务是统一不同源数据的矛盾之处
同名异义:
数据源A中的属性ID和数据源B中的属性ID分别描述的是菜品编号和订单编号,即描述的是不同的实体。
异名同义:
数据源A中的sales_dt 和数据源B中的sales_ date 都是描述销售日期的,即A. sales_dt = B. sales_ date。
单位不统一:
描述同一个实体分别用的是国际单位和中国传统的计量单位,检测和解决这些冲突就是实体识别的任务。
b、冗余属性识别
数据集成往往导致数据冗余,例如:
1) 同一属性多次出现。
2) 同一属性命名不一致导致重复。
仔细整合不同源数据能减少甚至避免数据冗余与不一致,从而提高数据挖掘的速度和质量。对于冗余属性要先分析,检测到后再将其删除。
主要是对数据进行规范化处理,将数据转换成 适当的 形式,以适用于挖掘任务以及算法的需要。
a、简单函数变换
简单函数变换是对原始数据进行某些数学函数变换,常用的变换包括平方、开方、取对数、差分运算等
b、规范化
数据规范化(归一化)处理是数据挖掘的一项 基础工作。不同评价指标往往具有不同的量纲,数值间的差别可能很大,不进行处理可能会影响到数据分析的结果。为了消除指标之间的量纲和取值范围差异的影响,需要进行标准化处理,将数据按照比例进行缩放,使之落入一个特定的区域,便于进行综合分析。如将工资收入属性值映射到[-1,1]或者[0,1]内。
数据规范化对于基于距离的挖掘算法尤为重要。
(1)最小 — 最大规范化
最 小—最大规范化也称为离差标准化,是对原始数据的线性变换,将数值值映射到[0,1]之间。
转换公式如下:
x ∗ = x − m i n m a x − m i n x^* = \frac{x - min}{max - min} x∗=max−minx−min
其中,max为样本数据的最大值,min 为样本数据的最小值。max-min 为极差。离差标准化保留了原来数据中存在的关系,是消除量纲和数据取值范围影响的最简单方法。这种处理方法的缺点是若数值集中且某个数值很大,则规范化后各值会接近于0,并且将会相差不大。若将来遇到超过目前属性[min, max]取值范围的时候,会引起系统出错,需要重新确定min和max。
(2) 零—均值规范化
零—均值规范化也称标准差标准化,经处理的数据的均值为0,标准差为1。
x ∗ = x − x ‾ 1 0 k x^* = \frac{x-\overline{x}}{10^k} x∗=10kx−x
其中x的平均数为原始数据的均值,σ为原始数据的标准差。
(3) 小数定标规范化
通过移动属性值的小数位数,将属性值映射到[-1,1]之间,移动的小数位数取决于属性值绝对值的最大值。
转化公式为:
x ∗ = x 1 0 k x^* = \frac{x}{10^k} x∗=10kx
实例:
对一个矩阵使用上面三种规范化的方法进行处理,对比结果。
#-*- coding: utf-8 -*-
#数据规范化
import pandas as pd
import numpy as np
datafile = '../data/normalization_data.xls' #参数初始化
data = pd.read_excel(datafile, header = None) #读取数据
a = (data - data.min())/(data.max() - data.min()) #最小-最大规范化
b = (data - data.mean())/data.std() #零-均值规范化
c = data/10**np.ceil(np.log10(data.abs().max())) #小数定标规范化
print(a)
print("*"*30)
print(b)
print("*"*30)
print(c)
结果:
0 1 2 3
0 0.074380 0.937291 0.923520 1.000000
1 0.619835 0.000000 0.000000 0.850941
2 0.214876 0.119565 0.813322 0.000000
3 0.000000 1.000000 1.000000 0.563676
4 1.000000 0.942308 0.996711 0.804149
5 0.264463 0.838629 0.814967 0.909310
6 0.636364 0.846990 0.786184 0.929571
******************************
0 1 2 3
0 -0.905383 0.635863 0.464531 0.798149
1 0.604678 -1.587675 -2.193167 0.369390
2 -0.516428 -1.304030 0.147406 -2.078279
3 -1.111301 0.784628 0.684625 -0.456906
4 1.657146 0.647765 0.675159 0.234796
5 -0.379150 0.401807 0.152139 0.537286
6 0.650438 0.421642 0.069308 0.595564
******************************
0 1 2 3
0 0.078 0.521 0.602 0.2863
1 0.144 -0.600 -0.521 0.2245
2 0.095 -0.457 0.468 -0.1283
3 0.069 0.596 0.695 0.1054
4 0.190 0.527 0.691 0.2051
5 0.101 0.403 0.470 0.2487
6 0.146 0.413 0.435 0.2571
Process finished with exit code 0
c、连续属性离散化
(1)离散化的过程
连续属性的离散化就是在数据的取值范围内设定若千个离散的划分点,将取值范围划分为一些离散化的区间,最后用不同的符号或整数值代表落在每个子区间中的数据值。所以,离散化涉及两个子任务:确定分类数以及如何将连续属性值映射到这些分类值。
(2)常用的离散化方法
等宽法
将属性的值域分成具有相同宽度的区间,区间的个数由数据本身的特点决定,或者由用户指定,类似于制作频率分布表。
等频法
将相同数量的记录放进每个区间。
基于聚类分析的方法
维聚类的方法包括两个步骤,首先将连续属性的值用聚类算法(如K-Means算法)进行聚类,然后再将聚类得到的簇进行处理,合并到一个簇的连续属性值并做同一标记。聚类分析的离散化方法也需要用户指定簇的个数,从而决定产生的区间数。
实例:
使用上述三种离散化方法对“医学中中医证型的相关数据”进行连续属性离散化的对比。
#-*- 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.values.reshape((len(data), 1))) #训练模型
c = pd.DataFrame(kmodel.cluster_centers_).sort_values(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()
d、属性改造
在数据挖掘的过程中,为了提取更有用的信息,挖掘更深层次的模式,提高挖掘结果的精度,我们需要利用已有的属性集构造出新的属性,并加人到现有的属性集合中。
为了判断是否有大用户存在窃漏电行为,可以构造一个新的指标——线损率,在过程就是构造属性。
线 损 率 = 供 入 电 量 − 供 出 电 量 供 入 电 量 ∗ 100 % 线损率 = \frac{供入电量 - 供出电量}{供入电量} * 100\% 线损率=供入电量供入电量−供出电量∗100%
线损率的正常范围一般在3% ~ 15%,如果远远超过该范围,就可以认为该条线路的大用户很可能存在窃漏电等用电异常行为。
实例:
线损率属性构造
#-*- coding: utf-8 -*-
#线损率属性构造
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) #保存结果
e、小波变换
小波变换是一种新型的数据分析 工具,是近年来兴起的信号分析手段。小波分析的理论和方法在信号处理、图像处理、语音处理、模式识别、量子物理等领域得到越来越广泛的应用,它被认为是近年来在工具及方法上的重大突破。小波变换具有多分辨率的特点,在时域和频域都具有表征信号局部特征的能力,通过伸缩和平移等运算过程对信号进行多尺度聚焦分析,提供了一种非平稳信号的时频分析手段,可以由粗及细地逐步观察信号,从中提取有用信息。
实例:
小波变换特征提取
#-*- 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个数字,第一个数组为逼近系数数组,后面的依次是细节系数数组
★如有不足或错误请指出,谢谢。