数据预处理的主要内容包括数据清洗、数据集成、数据变换和数据规约,它的工作量在数据挖掘过程中占60%。
4.1.1缺失值处理
删除记录
不处理
数据插补
# -*- coding:utf-8 -*-
# 牛顿插值多项式
def get_n_diff_quo(xi, fi):
"""
计算n阶差商(difference quotient)
"""
if len(xi) > 2:
return (get_n_diff_quo(xi[:len(xi)-1], fi[:len(fi)-1])-get_n_diff_quo(xi[1:len(xi)], fi[1:len(fi)]))/(xi[0]-xi[-1])
return (fi[0]-fi[1])/(xi[0]-xi[1])
def get_w(xi):
"""
计算差商前面的系数
"""
def w(x):
result = 1.0
for i, j in enumerate(xi, start=1):
# 跳过xi的最后一项
if i == len(xi):
break
# print(j)
result *= (x - j)
return result
return w
def get_newton_interpolate(xi, fi):
"""
:param xi: xi存放点对的x坐标
:param fi: fi存放点对的y坐标
:return: 牛顿插值函数
"""
def newton_interpolate(x):
result = fi[0]
for i in range(2, len(xi)+1):
result += (get_n_diff_quo(xi[:i], fi[:i]) * get_w(xi[:i])(x))
return result
return newton_interpolate
if __name__ == '__main__':
train_x = [i for i in range(-10, 11)]
train_y = [i**2 for i in train_x]
newton_inter = get_newton_interpolate(train_x, train_y)
test_x = [i for i in range(11, 20)]
predict_y = [newton_inter(i) for i in test_x]
print(predict_y)
4.1.2异常值处理
数据挖掘所需要的数据往往来自不同的数据源,数据集成就是将多个的数据源合并存放在一个一致的数据存储(数据仓库)中的过程。
4.21实体识别
4.2.2冗余属性识别
数据变换是对数据进行规范化处理,将数据转换成“适当的”形式,以适用于挖掘任务和算法的需要。
4.3.1简单的函数变换
通常对原始数据进行一些比较简单的函数变化,比如平方、开方、取对数或者差分运算。比如人的个人年收入10000元到10亿元,可以通过取对数压缩年收入的取值区间,方便完成数据挖掘任务。
4.3.2数据规范化
最大-最小规范化
最大-最小规范化也称离差规范化,可以将原始数据映射到[0, 1]区间内。离差标准化保留了原始数据之间的关系,是消除量纲和数据取值范围影响的最简单的方法。
变换公式如下:
缺点:如果最大值max的值很大,会导致变换后的数据都接近于0,并且相差不大。
标准差标准化
对原始数据进行数据标准化,标准化后的数据均值为0,方差为1。这是使用的最多的数据规范化处理的方法。
变换公式如下:
x拔为均值,西格玛为标准差。
小数定标规范化
通过移动原始数据的小数点的位置,将原始数据映射到[-1, 1]区间内,小数点移动的位数取决于原始数据中绝对值的最大值。
变换公式如下:
k=np.ceil(np.log10(data.abs().max())),k为原始数据中绝对值的最大值对10取对数然后上取整得到。
4.3.3连续属性离散化
比如决策树算法,它需要每个属性的取值是有限个(也就是离散的),这样就可以选个最优的属性利用属性值来划分数据集。如西瓜数据集,按照西瓜的色泽属性(该属性取值有黑色、青绿色和浅白色)可以将数据集划分为黑色瓜,青绿色瓜和浅白色瓜。但是如果按照西瓜的含糖率属性(这是一个连续属性)无法根据它的取值来划分数据集,这时可以将连续属性离散化,比如选择一个划分点0.5,将含糖率>0.5和含糖率<0.5作为含糖率属性的取值。
常用的离散化方法如下:
例子:
# -*- coding: utf-8 -*-
# 连续属性离散化
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans # 引入KMeans
datafile = 'F:/DataMining/chapter4/discretization_data.xls'
data = pd.read_excel(datafile) # 读取数据
data = data['肝气郁结证型系数'].copy()
print('data: \n', data)
# 等宽离散化,各区间依次命名为0,1,2,3
k = 4
d1 = pd.cut(data, k, labels=range(k))
print('d1: \n', d1)
# 等频率离散化
w = [1.0*i/k for i in range(k+1)]
print('w: \n', w)
print('describe: \n', data.describe())
print(type(data.describe()))
# data.describe()得到的结果类型是Series,它的前4行分别计算了count、mean、std、min
# 从第五行开始计算分位数,[4:4+k+1]取出计算出来的分位数
quantiles = data.describe(percentiles=w)[4:4+k+1]
print('quantiles: \n', type(quantiles))
# 0%分位数就是最小值,100%分位数就是最大值
print(data.min())
print(data.max())
# include_lowest=True, right=False表示左闭右开
# 如quantiles=[a, b, c, d],那么将按照[a, b)、[b,c)、[c,d)来划分数据集data
d2 = pd.cut(data, quantiles, include_lowest=True, right=False, labels=range(k))
print('d2: \n', d2)
# 聚类离散化
# 建立模型,n_jobs是并行数,一般等于CPU数较好
kmodel = KMeans(n_clusters=k, n_jobs=1)
# 训练模型
# data为DataFrame格式的,可以直接转换为数组格式
# KMeans需要的数组或者矩阵格式的训练数据,每行表示一个样本,每列表示一个特征
kmodel.fit_predict(np.array(data).reshape(-1, 1))
# kmodel.labels_可以查看每个样本的聚类标签,以数组格式1行n列返回
# print('kmodel.labels_: \n', kmodel.labels_)
# 将1行n列的数组或者一个列表转换成category格式的数据
d3 = pd.Series(data=kmodel.labels_, dtype='category')
print('d3: \n', d3)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
titles = ['等宽离散化', '等频率离散化', '聚类离散化']
def cluster_plot(d, t, k): # 自定义作图函数来显示聚类结果
plt.figure(figsize=(8, 3))
for j in range(k):
plt.plot(data[d == j], [i for i in d[d == j]], 'o')
# 添加注释,标明每个区间的数据个数
tmp = np.array(data[d == j])
tmp.sort(axis=0)
plt.annotate('总数: %d' % len(tmp), xy=(tmp[len(tmp)//2], j+0.2))
plt.ylim(-0.5, k-0.5)
plt.title(t)
return plt
cluster_plot(d1, titles[0], k).show()
cluster_plot(d2, titles[1], k).show()
cluster_plot(d3, titles[2], k).show()
4.3.4属性构造
利用已有属性构造新的属性。比如已知一定时期内人口的平均数,出生人口数和死亡人口数我们可以得到新的属性出生率和死亡率。
在庞大的数据集上进行数据分析和数据挖掘是一件复杂且耗时的任务,数据规约可以产生较小而且保持原数据集完整性的新数据集,在新数据集上进行数据分析和数据挖掘将更有效率。
优点如下:
(1)降低错误、无效数据对建模的影响
(2)降低数据存储的成本
(3)少量且具有代表性的数据集大幅缩减了数据挖掘的时间
4.4.1属性规约
4.4.2数值规约
数值规约通过选择较小的、替代的数据来较少数据量,包括有参方法和无参方法。
(1) unique()
(2) isnull()/notnull()
(3) cut()
# def cut(x, bins, right=True, labels=None, retbins=False, precision=3,
# include_lowest=False, duplicates='raise'):
# x:支持一维数据如array、series、list等
# bins: 支持int或数字序列。
# * int: 如bins为int型k,则将数据x分为k个等宽的区间。区间长度:(x.max()-x.min())/k
# * 数字序列:如bins为list=[2, 20, 34, 50],则将x为(2,20]、(20, 34]、(34, 50]三个区间
# labels:支持数组或列表,也可以为bool类型,也可以不写
# * 数组或列表:如labels=['a', 'b', 'c'],bins返回的第一个区间内的所有数据的标签都是'a'
# 返回的第二个区间内的所有数据的标签都是'b',返回的第二个区间内的所有数据的标签都是'c'
# * bool: 如labels=False,则bins返回的第一个区间内的所有数据的标签都是'0'
# 返回的第二个区间内的所有数据的标签都是'1',以此类推
# * 不写:返回的是每个数据所在的区间
# retbins: 支持bool类型默认为False。retbins=True则在print()的时候返回划分的所有区间
# right=True: 划分后的所有区间默认为右闭区间
# include_lowest=False :第一个区间是否为左闭区间默认为否
cut_data = pd.cut(df['col1'], 4, labels=False)
print('cut_data: \n', cut_data)
结果如下: