數據離散化
數據離散化的一種常用方法是依據數據的相關性程度進行離散化,最常見的算法就是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())