代码来自书上,进行了一定修改,确保运行没有error
挖掘的总体思路:
首先癌症有不同的发展期
不同发展期的癌症有不同的症型
这些症状都处于不同的严重程度。
因此收集病人样本930条。
数据挖掘流程:
一(对应代码1)、
每人的6种症型(症状名字的代号是A~F,分别对应data.xls中的各种症型)都处于不同的严重程度,
每种症型按照严重程度高低,聚类为四个区间,并分别得到各个区间的数量。每个区间取名字例如:A1~A4
由于每个病人都有六种症状的不同区间,因此每个病人都可以由一个6维矢量来表示
二(对应代码2)、6维矢量扩充成24维矢量,使用Apriori算法,找出不同严重程度的症状和乳腺癌发展时期之间的联系,也就是说,找出哪些症状(例如A3和B5)极有可能导致乳腺癌的某个特定的时期(例如H5),挖掘结束。
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆第一部分代码开始☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
# -*- coding: utf-8 -*-
'''
聚类离散化,最后的result的格式为:
1 2 3 4
A 0 0.178698 0.257724 0.351843
An 240 356.000000 281.000000 53.000000
即(0, 0.178698]有240个,(0.178698, 0.257724]有356个,依此类推。
'''
from __future__ import print_function
import pandas as pd
from sklearn.cluster import KMeans # 导入K均值聚类算法
datafile = '../data/data.xls' #待聚类的数据文件
processedfile = '../tmp/data_processed.xls' # 数据处理后文件
typelabel = {u'肝气郁结证型系数': 'A', u'热毒蕴结证型系数': 'B', u'冲任失调证型系数': 'C', u'气血两虚证型系数': 'D', u'脾胃虚弱证型系数': 'E', u'肝肾阴虚证型系数': 'F'}
k = 4 # 需要进行的聚类类别数
# 读取数据并进行聚类分析
data = pd.read_excel(datafile) # 读取数据
keys = list(typelabel.keys())
result = pd.DataFrame()
if __name__ == '__main__': # 判断是否主窗口运行,如果是将代码保存为.py后运行,则需要这句,如果直接复制到命令窗口运行,则不需要这句。
for i in range(len(keys)):#每轮循环处理一个症状的聚类和数量统计
print("i=", i)
# 调用k-means算法,进行聚类离散化
print(u'正在进行“%s”的聚类...' % keys[i])
kmodel = KMeans(n_clusters=k, n_jobs=4) # n_jobs是并行数,一般等于CPU数较好
kmodel.fit(data[[keys[i]]].as_matrix()) # 训练模型
r1 = pd.DataFrame(kmodel.cluster_centers_, columns=[typelabel[keys[i]]]) # 聚类中心
print("r1=",r1)
print("label=", typelabel[keys[i]])
#
#
#
r2 = pd.Series(kmodel.labels_).value_counts() # 分类统计
print("r2=",r2)
print("-------------------------------------")
r2 = pd.DataFrame(r2, columns=[typelabel[keys[i]] + 'number']) # 转为DataFrame,记录各个类别的数目
print("r2=", r2)#这一段用来统计聚类中各个类别的数量,并且写入一列中,并改改名字
#
#
#
#
r=pd.concat([r1, r2], axis=1)
print("排序前r=",r)
r=r.sort_values(typelabel[keys[i]])
print("排序后r=",r)
r.index = [1, 2, 3, 4]#index指的是列标签,typelabel[keys[i]]指的是行标签
#
#
#
#
print("----------------------------------------------------")
print("计算均值前的表格r=",r)
r[typelabel[keys[i]]] = pd.rolling_mean(r[typelabel[keys[i]]], 2) # rolling_mean()用来计算相邻2列的均值,以此作为边界点。
r[typelabel[keys[i]]][1] = 0.0 # 这两句代码将原来的聚类中心改为边界点.这里的用法类似于C语言中的r[i][j]
print("计算均值后的表格r=",r)
result = result.append(r.T)#旋转
print("----------------------------------------------------")
#
#
#
#
print("result=",result)
result = result.sort_index() # 以Index排序,即以A,B,C,D,E,F顺序排,A代表症状的名称,BCDE各自代表该症状的严重程度
result.to_excel(processedfile)
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆第一部分代码结束☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
输出的结果是:
1 2 3 4
A 0 0.178697589 0.2577240643 0.3518431814
Anumber 240 356 281 53
B 0 0.1507662277 0.2966313131 0.4897045019
Bnumber 325 396 180 29
C 0 0.2021487293 0.2890611377 0.4235365546
Cnumber 297 394 204 35
D 0 0.1765046363 0.2571199525 0.3661899988
Dnumber 309 371 211 39
E 0 0.1526978022 0.2575413223 0.3748694473
Enumber 273 319 242 96
F 0 0.1791433755 0.2613863944 0.354642668
Fnumber 200 237 265 228
Dnumber表示D症状对应的病人样本数量
相关解释:
axis=0#表格纵轴
axis=1#表格横轴
每种症状都有四个量化区间
------------------------------------重点代码解释1-----------------------------------------
r=pd.concat([r1, r2], axis=1).sort_values(typelabel[keys[i]])
这里首先是把r1代表的症状严重程度的量化参数(也就是聚类中心)和各个聚类中心对应的分类数量各自整合成一行,如下,
A Anumber
0 0.220441 356
1 0.136954 240
2 0.408679 53
3 0.295007 281
然后按行排序(这里的行也就是最终结果中的列,因为后面的代码中存在转置)
A Anumber
1 0.136954 240
0 0.220441 356
3 0.295007 281
2 0.408679 53
axis=1#表示按行的意思
------------------------重点代码解释1----------------------------------------------------
------------------------------------重点代码解释2---------------------------------------------------------------------------
r[typelabel[keys[i]]] = pd.rolling_mean(r[typelabel[keys[i]]], 2) # rolling_mean()用来计算相邻2列的均值,以此作为边界点。
解释下这里的r1和r2如何组合成r
举例,输出的其中一段结果为:
计算均值前的表格r= C Cnumber
1 0.158734 297
2 0.245563 394
3 0.332559 204
4 0.514514 35
计算均值后的表格r= C Cnumber
1 0.000000 297
2 0.202149 394
3 0.289061 204
4 0.423537 35
(0.245563+0.332559)/2=0.289061
自己评论:这种处理方式并不合理,因为数据是对应于聚类中心的,作者在这里随意利用聚类中心进行计算得到的区间,显然是和分类数量在意义和实际上都是不对应的。
-----------------------------重点代码解释2--------------------------------------------------------
--------------------------后续处理-------------------------------------------------
得到聚类处理后的文件为data_processed.xls
结合输入文件data.xls
由于得到了每种症状的各种严重程度的量化区间,所以把data.xls中的各个参数(例如A症状)转化为A1~A4
转化办法如下:
data.xls中,对每行的首单元进行按行排序,由于data.xls中第一列的各项数据处于data_processed.xls的四个区间中,
分别改为A1~A4,从而得到下一份代码需要的文件apriori.txt文件
-------------------------------后续处理结束--------------------------------------------
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆接下来是第二部分☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
被调用函数apriori.py如下
#-*- coding: utf-8 -*-
from __future__ import print_function
import pandas as pd
#自定义连接函数,用于实现L_{k-1}到C_k的连接
def connect_string(x, ms):
x = list(map(lambda i:sorted(i.split(ms)), x))
l = len(x[0])
r = []
for i in range(len(x)):
for j in range(i,len(x)):
if x[i][:l-1] == x[j][:l-1] and x[i][l-1] != x[j][l-1]:
r.append(x[i][:l-1]+sorted([x[j][l-1],x[i][l-1]]))
return r
#寻找关联规则的函数
def find_rule(d, support, confidence, ms = u'--'):
result = pd.DataFrame(index=['support', 'confidence']) #定义输出结果
support_series = 1.0*d.sum()/len(d) #支持度序列
column = list(support_series[support_series > support].index) #初步根据支持度筛选
k = 0
while len(column) > 1:
k = k+1
print(u'\n正在进行第%s次搜索...' %k)
column = connect_string(column, ms)
print(u'数目:%s...' %len(column))
sf = lambda i: d[i].prod(axis=1, numeric_only = True) #新一批支持度的计算函数
#创建连接数据,这一步耗时、耗内存最严重。当数据集较大时,可以考虑并行运算优化。
d_2 = pd.DataFrame(map(sf,column), index = [ms.join(i) for i in column]).T
support_series_2 = 1.0*d_2[[ms.join(i) for i in column]].sum()/len(d) #计算连接后的支持度
column = list(support_series_2[support_series_2 > support].index) #新一轮支持度筛选
support_series = support_series.append(support_series_2)
column2 = []
for i in column: #遍历可能的推理,如{A,B,C}究竟是A+B-->C还是B+C-->A还是C+A-->B?
i = i.split(ms)
for j in range(len(i)):
column2.append(i[:j]+i[j+1:]+i[j:j+1])
cofidence_series = pd.Series(index=[ms.join(i) for i in column2]) #定义置信度序列
for i in column2: #计算置信度序列
cofidence_series[ms.join(i)] = support_series[ms.join(sorted(i))]/support_series[ms.join(i[:len(i)-1])]
for i in cofidence_series[cofidence_series > confidence].index: #置信度筛选
result[i] = 0.0
result[i]['confidence'] = cofidence_series[i]
result[i]['support'] = support_series[ms.join(sorted(i.split(ms)))]
result = result.T.sort_values(['confidence','support'], ascending = False) #结果整理,输出
print(u'\n结果为:')
print(result)
return result
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆第2部分代码开始☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
#-*- coding: utf-8 -*-
from __future__ import print_function
import pandas as pd
from apriori import * #导入自行编写的apriori函数
import time #导入时间库用来计算用时
inputfile = '../data/apriori.txt' #这个文件表示”哪些程度的症状“会导致“乳腺癌的不同发展期”的数据集,同时也包含无关症状
data = pd.read_csv(inputfile, header=None, dtype = object)
start = time.clock() #计时开始
print(u'\n转换原始数据至0-1矩阵...')
ct = lambda x : pd.Series(1, index = x) #转换0-1矩阵的过渡函数
b = map(ct, data.as_matrix()) #用map方式执行
print("b=",b)
data = pd.DataFrame(b)#转化成数据框的时候,一些没有的症状就会被加进来
data=data.fillna(0) #实现矩阵转换,空值用0填充
print("data=",data)
end = time.clock() #计时结束
print(u'\n转换完毕,用时:%0.2f秒' %(end-start))
del b #删除中间变量b,节省内存
#以上的工作是把各种症状的组合转化成矢量
#
support = 0.06 #最小支持度
confidence = 0.87 #最小置信度
ms = '---' #连接符,默认'--',用来区分不同元素,如A--B。需要保证原始表格中不含有该字符
start = time.clock() #计时开始
print(u'\n开始搜索关联规则...')
find_rule(data, support, confidence, ms)
end = time.clock() #计时结束
print(u'\n搜索完成,用时:%0.2f秒' %(end-start))
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆第2部分代码结束☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
输出结果(也就是所得挖掘结果):
support confidence
A3---F4---H4 0.078495 0.879518
C3---F4---H4 0.075269 0.875000
顺便解释下基本概念:
support指的是,或关系
Support(A=>B)=P(AUB)
置信度值得是:条件概率
Confidence(A=>B)=P(B|A)
那么以上第一行的结果可以解释为:当肝气郁结症型系数位于A3区间,并且 肝肾阴虚症型系数位于F4区间,在这两个前提的条件下,处于乳腺癌H4阶段的概率为0.879518
在总样本中,这种情况发生的概率是0.078495