項目目標:根據(jù)乳腺癌患者的病理信息,挖掘患者的癥狀與中醫(yī)證型之間的關聯(lián)關系,特別是各中醫(yī)證素與乳腺癌TNM分期之間的關系。
原始數(shù)據(jù)是根據(jù)問卷調查得到,基本挖掘流程為:
1. 數(shù)據(jù)預處理
屬性歸約:收集到的數(shù)據(jù)集案由73個屬性,但為了更有效的挖掘,此處僅僅選擇6種證型得分,TNM分期的屬性值構成數(shù)據(jù)集。如下:
1.1 數(shù)據(jù)變換
主要采用屬性構造和數(shù)據(jù)離散化兩種方法來處理。首先通過屬性構造,獲得證型系數(shù),然后通過聚類算法對數(shù)據(jù)進行離散化處理,形成建模數(shù)據(jù)。
本案例提供的數(shù)據(jù)集是已經(jīng)經(jīng)過屬性構造得到了證型系數(shù),故而只需要離散化處理即可。
由于Apriori關聯(lián)規(guī)則算法無法處理連續(xù)型數(shù)值變量,為了將原始數(shù)據(jù)格式轉換為適合建模的格式,需要對數(shù)據(jù)進行離散化處理。此處采用聚類算法對各證型系數(shù)進行離散化處理,將每個屬性聚成4類。
比如:
from sklearn.cluster import KMeans #導入K均值聚類算法
k=4
result = pd.DataFrame()
for col in list('ABCDEF'):
print('正在進行{}列的聚類'.format(col))
kmodel=KMeans(n_clusters = k, n_jobs = 4)
kmodel.fit(data[col].values.reshape(-1,1)) #訓練模型
r1=pd.DataFrame(kmodel.cluster_centers_,columns=[col]) # 聚類中心
r2=pd.Series(kmodel.labels_).value_counts() # 分類統(tǒng)計
r2=pd.DataFrame(r2,columns=[col+'n']) # 記錄各個類別的數(shù)目
r=pd.concat([r1,r2],axis=1).sort_values(col)
# 匹配聚類中心和類別數(shù)目
r.index=[1,2,3,4]
r[col]=r[col].rolling(2).mean()
#rolling mean()用來計算相鄰2列的均值,以此作為邊界點。
r[col][1]=0.0
# 這兩句代碼將原來的聚類中心改為邊界點。
result=result.append(r.T)
得到的result結果為:
for col in 'ABCDEF':
bins=np.append(result.loc[col,:].values,1000)
data[col+'_cls']=pd.cut(data[col],bins=bins,labels=[col+str(i) for i in range(k)],include_lowest=True)
2. 關聯(lián)規(guī)則挖掘
關聯(lián)規(guī)則挖掘的一個難點是設置合理的最小支持度,最小置信度,目前并沒有統(tǒng)一的標準,大部分都是根據(jù)業(yè)務經(jīng)驗設置初始值,經(jīng)過多次調整,獲取與業(yè)務相符的關聯(lián)規(guī)則結果。
此處根據(jù)多次調整,結果實際業(yè)務分析,選取的輸入?yún)?shù)為:最小支持度6%,最小置信度75%。
首先要對上面的數(shù)據(jù)進行one-hot編碼,可以用pd.get_dummies來完成
data2=pd.get_dummies(data)
print(data2.shape)
print(data2.head(10))
然后使用關聯(lián)規(guī)則挖掘,代碼為:
#自定義連接函數(shù),用于實現(xiàn)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
#尋找關聯(lián)規(guī)則的函數(shù)
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) #初步根據(jù)支持度篩選
k = 0
while len(column) > 1:
k = k+1
print(u'\n正在進行第%s次搜索...' %k)
column = connect_string(column, ms)
print(u'數(shù)目:%s...' %len(column))
sf = lambda i: d[i].prod(axis=1, numeric_only = True) #新一批支持度的計算函數(shù)
#創(chuàng)建連接數(shù)據(jù),這一步耗時、耗內存最嚴重。當數(shù)據(jù)集較大時,可以考慮并行運算優(yōu)化。
d_2 = pd.DataFrame(list(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
support = 0.06 #最小支持度
confidence = 0.75 #最小置信度
ms = '---' #連接符,默認'--',用來區(qū)分不同元素,如A--B。需要保證原始表格中不含有該字符
find_rule(data2, support, confidence, ms)
上面的關聯(lián)規(guī)則表示的含義是:A2和F3可以推導出H4證型。且C2和F3也可以推導出H4證型。
需要注意的是:并非所有關聯(lián)規(guī)則都有意義,所以需要根據(jù)實際情況來篩選最終的規(guī)則。
參考資料:
《Python數(shù)據(jù)分析和挖掘實戰(zhàn)》張良均等