数据离散化
数据离散化的一种常用方法是依据数据的相关性程度进行离散化,最常见的算法就是ChiMerge算法
定义
chimerge是基于chi-squre的,监督的,自底向上(合并的)一种数据离散化方法。
卡方检验
x
|
y
|
z
|
||
A
|
x1
|
y1
|
z1
|
a
|
B
|
x2
|
y2
|
z2
|
b
|
x
|
y
|
z
|
N
|
统计AB属性的独立性:
1. 分别计算期望频率,例如(A, x)期望频率为a * x / N
2. 计算卡方值k = ((x1 - E(A,x))/E(A,x))^2 + (x2 - E(B, x)/E(B, x))^2 + ...
3. 查询在某个置信度下拒绝假设(相互独立)的值,大于该值说明属性不是相互独立
chimerge的核心思想
某个属性的频率在相邻区间内应当相同,若其在相邻两个区间的分布独立,则这两个区间应当合并。
实现
1. 将一个属性划分为多个区间。
2. 设置卡方阈值。例如有3个类别的数据,在0.9的置信度时,卡方值为4.6,即对于小于4.6的相邻区间应当合并。
5.1,3.5,1.4,0.2,Iris-setosa // A类
......
6.6,2.9,4.6,1.3,Iris-versicolor // B类
......
6.0,3.0,4.8,1.8,Iris-virginica // C类
........
数据集有A,B,C三个类别(标称属性),四个区间属性。可以用chimerge的算法对着四个属性进行离散化。
代码实现
定义
chimerge是基于chi-squre的,监督的,自底向上(合并的)一种数据离散化方法。
卡方检验
x y z
A x1 y1 z1 a
B x2 y2 z2 b
x y z N
统计AB属性的独立性:
1. 分别计算期望频率,例如(A, x)期望频率为a * x / N
2. 计算卡方值k = ((x1 - E(A,x))/E(A,x))^2 + (x2 - E(B, x)/E(B, x))^2 + ...
3. 查询在某个置信度下拒绝假设(相互独立)的值,大于该值说明
chimerge的核心思想
某个属性的频率在相邻区间内应当相同,若其在相邻两个区间的分布独立,则这两个区间应当合并。
实现
1. 将一个属性划分为多个区间。
2. 设置卡方阈值。例如有3个类别的数据,在0.9的置信度时,卡方值为4.6,即对于小于4.6的相邻区间应当合并。
以鸢尾花数据集为例(http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data)
5.1,3.5,1.4,0.2,Iris-setosa // A类
......
6.6,2.9,4.6,1.3,Iris-versicolor // B类
......
6.0,3.0,4.8,1.8,Iris-virginica // C类
........
数据集有A,B,C三个类别(标称属性),四个区间属性。可以用chimerge的算法对着四个属性进行离散化。
代码实现
# coding=utf-8
from time import ctime
''' 读取数据'''
def read(file):
Instances = []
fp = open(file,'r')
for line in fp:
line = line.strip('\n')
if line!='':
Instances.append(line.split(','))
fp.close()
return Instances
''' 将第i个特征和类标签组合起来
如:[[0.2,'Iris-setosa'],[0.2,'Iris-setosa'],...]'''
def split(Instances,i):
log = []
for line in Instances:
log.append([line[i],line[4]])
return log
''' 统计每个属性值所具有的实例数量
[['4.3', 'Iris-setosa', 1], ['4.4', 'Iris-setosa', 3],...]'''
def count(log):
log_cnt = []
# 以第0列进行排序的 升序排序
log.sort(key = lambda log:log[0])
i = 0
while(i<len(log)):
cnt = log.count(log[i])
record = log[i][:]
record.append(cnt)
log_cnt.append(record)
i += cnt
return log_cnt
''' log_cnt 是形如: ['4.4', 'Iris-setosa', 3] 的
统计对于某个属性值,对于三个类所含有的数量量
返回结果形如:{4.4:[0,1,3],...} 属性值为4.4的对于三个类的实例数量分别是:0、1、3 '''
def build(log_cnt):
log_dict = {}
for record in log_cnt:
if record[0] not in log_dict.keys():
log_dict[record[0]] = [0,0,0]
if record[1] == 'Iris-setosa':
log_dict[record[0]][0] = record[2]
elif record[1] == 'Iris-versicolor':
log_dict[record[0]][1] = record[2]
elif record[1] == 'Iris-virginica':
log_dict[record[0]][2] = record[2]
else:
raise TypeError('Data Exception')
log_truple = sorted(log_dict.items())
return log_truple
def collect(Instances,i):
log = split(Instances,i)
log_cnt = count(log)
log_tuple = build(log_cnt)
return log_tuple
def combine(a,b):
''''' a=('4.4', [3, 1, 0]), b=('4.5', [1, 0, 2])
combine(a,b)=('4.4', [4, 1, 2]) '''
c = a[:]
for i in range(len(a[1])):
c[1][i] += b[1][i]
return c
def chi2(A):
'''计算两个区间的卡方值'''
m = len(A)
k = len(A[0])
R = []
'''第i个区间的实例数'''
for i in range(m):
sum = 0
for j in range(k):
sum += A[i][j]
R.append(sum)
C = []
'''第j个类的实例数'''
for j in range(k):
sum = 0
for i in range(m):
sum+= A[i][j]
C.append(sum)
N = 0
'''总的实例数'''
for ele in C:
N +=ele
res = 0.0
for i in range(m):
for j in range(k):
Eij = 1.0*R[i] *C[j]/N
if Eij!=0:
res = 1.0*res + 1.0*(A[i][j] - Eij)**2/Eij
return res
'''ChiMerge 算法'''
'''下面的程序可以看出,合并一个区间之后相邻区间的卡方值进行了重新计算,而原作者论文中是计算一次后根据大小直接进行合并的
下面在合并时候只是根据相邻最小的卡方值进行合并的,这个在实际操作中还是比较好的
'''
def ChiMerge(log_tuple,max_interval):
num_interval = len(log_tuple)
while num_interval>max_interval:
num_pair = num_interval -1
chi_values = []
''' 计算相邻区间的卡方值'''
for i in range(num_pair):
arr = [log_tuple[i][1],log_tuple[i+1][1]]
chi_values.append(chi2(arr))
min_chi = min(chi_values)
for i in range(num_pair - 1,-1,-1):
if chi_values[i] == min_chi:
log_tuple[i] = combine(log_tuple[i],log_tuple[i+1])
log_tuple[i+1] = 'Merged'
while 'Merged' in log_tuple:
log_tuple.remove('Merged')
num_interval = len(log_tuple)
split_points = [record[0] for record in log_tuple]
return split_points
def discrete(path):
Instances = read(path)
max_interval = 6
num_log = 4
for i in range(num_log):
log_tuple = collect(Instances,i)
split_points = ChiMerge(log_tuple,max_interval)
print split_points
if __name__=='__main__':
print('Start: ' + ctime())
discrete('iris.data')
print('End: ' + ctime())