【python資料探勘課程】二十四.KMeans文字聚類分析互動百科語料

Eastmount發表於2018-07-06

這是《Python資料探勘課程》系列文章,也是我上課內容及書籍中的一個案例。本文主要講述文字聚類相關知識,包括中文分詞、資料清洗、特徵提取、TF-IDF、KMeans聚類等步驟。
本篇文章為基礎性文章,希望對你有所幫助,提供些思路,也是自己教學的內容。如果文章中存在錯誤或不足之處,還請海涵。同時,推薦大家閱讀我以前的文章瞭解其他知識。

 

前文參考:
【Python資料探勘課程】一.安裝Python及爬蟲入門介紹
【Python資料探勘課程】二.Kmeans聚類資料分析及Anaconda介紹
【Python資料探勘課程】三.Kmeans聚類程式碼實現、作業及優化
【Python資料探勘課程】四.決策樹DTC資料分析及鳶尾資料集分析
【Python資料探勘課程】五.線性迴歸知識及預測糖尿病例項
【Python資料探勘課程】六.Numpy、Pandas和Matplotlib包基礎知識
【Python資料探勘課程】七.PCA降維操作及subplot子圖繪製
【Python資料探勘課程】八.關聯規則挖掘及Apriori實現購物推薦
【Python資料探勘課程】九.迴歸模型LinearRegression簡單分析氧化物資料
【python資料探勘課程】十.Pandas、Matplotlib、PCA繪圖實用程式碼補充
【python資料探勘課程】十一.Pandas、Matplotlib結合SQL語句視覺化分析
【python資料探勘課程】十二.Pandas、Matplotlib結合SQL語句對比圖分析
【python資料探勘課程】十三.WordCloud詞雲配置過程及詞頻分析
【python資料探勘課程】十四.Scipy呼叫curve_fit實現曲線擬合

【python資料探勘課程】十五.Matplotlib呼叫imshow()函式繪製熱圖
【python資料探勘課程】十六.邏輯迴歸LogisticRegression分析鳶尾花資料
【python資料探勘課程】十七.社交網路Networkx庫分析人物關係(初識篇)

【python資料探勘課程】十八.線性迴歸及多項式迴歸分析四個案例分享
【python資料探勘課程】十九.鳶尾花資料集視覺化、線性迴歸、決策樹花樣分析
【python資料探勘課程】二十.KNN最近鄰分類演算法分析詳解及平衡秤TXT資料集讀取

【python資料探勘課程】二十一.樸素貝葉斯分類器詳解及中文文字輿情分析
【python資料探勘課程】二十二.Basemap地圖包安裝入門及基礎知識講解
【python資料探勘課程】二十三.時間序列金融資料預測及Pandas庫詳解
 

這篇文章程式碼和實驗分析為主,不進行詳細講解,詳見:
[python] 基於k-means和tfidf的文字聚類程式碼簡單實現
[python] Kmeans文字聚類演算法+PAC降維+Matplotlib顯示聚類影象

 

PSS:最近參加CSDN2018年部落格評選,希望您能投出寶貴的一票。我是59號,Eastmount,楊秀璋。投票地址:https://bss.csdn.net/m/topic/blog_star2018/index


五年來寫了314篇部落格,12個專欄,是真的熱愛分享,熱愛CSDN這個平臺,也想幫助更多的人,專欄包括Python、資料探勘、網路爬蟲、影象處理、C#、Android等。現在也當了兩年老師,更是覺得有義務教好每一個學生,讓貴州學子好好寫點程式碼,學點技術,"師者,傳到授業解惑也",提前祝大家新年快樂。2019我們攜手共進,為愛而生。

 

一. Python文字抓取


爬蟲主要通過Python+Selenium+Phantomjs實現,爬取互動百科旅遊景點資訊,其中爬取百度百科程式碼如下。
參考前文:[Python爬蟲] Selenium獲取百度百科旅遊景點的InfoBox訊息盒
爬取的資料集如下圖所示,互動百科的每類主題各100篇網頁文字,涉及人物明星、旅遊景區、動物、世界國家四個主題。執行結果如下圖所示:

 

 


實現原理:

 

 

我們首先分析互動百科搜尋詞條的一些規則,比如搜尋人物“貴州”,對應的超鏈為“http://www.baike.com/wiki/貴州”,對應頁面如圖9.16所示,從圖中可以看到,頂部的超連結URL、詞條為“貴州”、第一段為“貴州”的摘要資訊、“右邊為對應的圖片等資訊。

 


同理,搜尋程式語言“Python”,對應的超連結為“http://www.baike.com/wiki/Python”,可以得出一個簡單的規則,即“http://www.baike.com/wiki/條”可以搜尋對應的知識,如程式語言“Java”對應的“http://www.baike.com/wiki/Java&prd=button_doc_entry”,這裡定義了搜尋方式,它是通過點選按鈕“進入詞條”進行搜尋的,省略prd=button_doc_entry引數同樣可以得到相同的結果。
 

 

然後,需要分佈獲取這十門語言的摘要資訊。在瀏覽器中選中摘要部分,右鍵滑鼠點選“審查元素”返回結果如圖9.18所示,可以在底部看到摘要部分對應的HTML原始碼。

"Python" 詞條摘要部分對應的HTML核心程式碼如下所示:

<div class="summary" name="anchor" id="anchor">
<p>Python(英語發音:/ˈpaɪθən/),是一種物件導向、解釋型計算機程式設計語言,由Guido van Rossum於1989年發明...</p>
  <span><a action="editsummaryhref" href="javascript:void(0);" 
onclick="editSummary();return false;">
編輯摘要
</a></span>
</div>

呼叫Selenium的find_element_by_xpath("//summary[@class='summary']/p")函式,可以獲取摘要段落資訊,核心程式碼如下。

driver = webdriver.Firefox()
url = "http://www.baike.com/wiki/" + name
driver.get(url)
elem = driver.find_element_by_xpath("//div[@class='summary']/p")  
print elem.text

這段程式碼的基本步驟是:
  1.首先呼叫webdriver.Firefox()驅動,開啟火狐瀏覽器。
  2.分析網頁超連結,並呼叫driver.get(url)函式訪問。
  3.分析網頁DOM樹結構,呼叫driver.find_element_by_xpath()進行分析。
  4.輸出結果,部分網站的內容需要儲存至本地,並且需要過濾掉不需要的內容等。
下面是完整的程式碼及詳細講解,參考自己的書籍《Python網路資料爬取及分析從入門到精通(爬取篇)》。
 

# coding=utf-8  
# test09_03.py                
import os  
import codecs
from selenium import webdriver      
from selenium.webdriver.common.keys import Keys       

driver = webdriver.Firefox()

#獲取摘要資訊
def getAbstract(name):  
    try:
        #新建資料夾及檔案
        basePathDirectory = "Hudong_Coding"  
        if not os.path.exists(basePathDirectory):  
            os.makedirs(basePathDirectory)  
        baiduFile = os.path.join(basePathDirectory,"HudongSpider.txt")
        #檔案不存在新建,存在則追加寫入
        if not os.path.exists(baiduFile):  
            info = codecs.open(baiduFile,'w','utf-8')  
        else:  
            info = codecs.open(baiduFile,'a','utf-8')  

        url = "http://www.baike.com/wiki/" + name
        print url
        driver.get(url)  
        elem = driver.find_element_by_xpath("//div[@class='summary']/p")  
        print elem.text
        info.writelines(elem.text+'\r\n')  
          
    except Exception,e: 
        print "Error: ",e  
    finally:  
        print '\n'  
        info.write('\r\n')  
  
#主函式  
def main():
    languages = ["JavaScript", "Java", "Python", "Ruby", "PHP",
                 "C++", "CSS", "C#", "C", "GO"]
    print u'開始爬取'
    for lg in languages:  
        print lg
        getAbstract(lg)  
    print u'結束爬取'  

if __name__ == '__main__':
    main()  

輸出如下圖所示,將資料集 換成對應的景區即可。
 

 

 

 

二. 資料預處理


由於爬取的資料集都是分佈於四個主題資料夾中,每個檔案中共100個,所以需要將這四個主題的文字合併成一個txt檔案,其程式碼如下:

# coding=utf-8            
import re          
import os  
import sys
import codecs
import shutil
 
def merge_file():
    path = "BaiduSpiderSpots\\"
    resName = "BaiduSpider_Result.txt"
    if os.path.exists(resName):
        os.remove(resName)
    result = codecs.open(resName, 'w', 'utf-8')
 
    num = 1
    while num <= 100:
        name = "%04d" % num 
        fileName = path + str(name) + ".txt"
        source = open(fileName, 'r')
        line = source.readline()
        line = line.strip('\n')
        line = line.strip('\r')
 
        while line!="":
            line = unicode(line, "utf-8")
            line = line.replace('\n',' ')
            line = line.replace('\r',' ')
            result.write(line+ ' ')
            line = source.readline()
        else:
            print 'End file: ' + str(num)
            result.write('\r\n')
            source.close()
        num = num + 1
        
    else:
        print 'End All'
        result.close()    
 
if __name__ == '__main__':
    merge_file()

合併後的結果如下圖所示,生成的HudongSpider_Result.txt共400個,1-100為動物、101-200為景區、201-300位人物、301-400為國家,每一行表示所爬取的一個網頁文字。

 

 

 

 

三. 中文分詞


中文分詞主要使用的是Python+Jieba分詞工具,程式碼如下:

#encoding=utf-8
import sys
import re
import codecs
import os
import shutil
import jieba
import jieba.analyse
 
#匯入自定義詞典
#jieba.load_userdict("dict_baidu.txt")
 
#Read file and cut
def read_file_cut():

    fileName = "HudongSpider_Result.txt"
    source = open(fileName, 'r')
    resName = "Stop_HudongSpider_Result.txt"
    result = codecs.open(resName, 'w', 'utf-8')
    line = source.readline()
        
    while line!="":
        line = unicode(line, "utf-8")
        seglist = jieba.cut(line,cut_all=False)  #精確模式
        output = ' '.join(list(seglist))         #空格拼接
        #print output
        result.write(output)
        line = source.readline()
    else:
        source.close()
        result.close()
        print 'End All'
 
#Run function
if __name__ == '__main__':
    read_file_cut()sc

輸出結果如下圖所示,採用空格連線,同時可以匯入詞典進行分詞。分詞之後,也可以進行停用詞過濾、特殊符號去除等資料清洗,這裡不再介紹,詳見前文。
 

 

 

 

四. KMeans聚類分析


需要將文件相似度問題轉換為數學向量矩陣問題,可以通過向量空間模型來儲存每個文件的詞頻和權重,特徵抽取完後,因為每個詞語對實體的貢獻度不同,所以需要對這些詞語賦予不同的權重。計算詞項在向量中的權重方法——TF-IDF。

相關介紹:
它表示TF(詞頻)和IDF(倒文件頻率)的乘積:

 

 

其中TF表示某個關鍵詞出現的頻率,IDF為所有文件的數目除以包含該詞語的文件數目的對數值。

 

 

 

 

|D|表示所有文件的數目,|w∈d|表示包含詞語w的文件數目。
最後TF-IDF計算權重越大表示該詞條對這個文字的重要性越大,它的目的是去除一些"的、了、等"出現頻率較高的常用詞。


參考前文:Python簡單實現基於VSM的餘弦相似度計算
                 基於VSM的命名實體識別、歧義消解和指代消解

 

 

下面是使用scikit-learn工具呼叫CountVectorizer()和TfidfTransformer()函式計算TF-IDF值,同時呼叫sklearn.cluster中的KMeans演算法進行文字聚類。

完整程式碼:

# coding=utf-8  
import time          
import re          
import os  
import sys
import codecs
import shutil
import numpy as np
import matplotlib
import scipy
import matplotlib.pyplot as plt
from sklearn import feature_extraction  
from sklearn.feature_extraction.text import TfidfTransformer  
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import HashingVectorizer 
 
if __name__ == "__main__":
    
    #########################################################################
    #                           第一步 計算TFIDF
    
    #文件預料 空格連線
    corpus = []
    
    #讀取預料 一行預料為一個文件
    for line in open('Stop_HudongSpider_Result.txt', 'r').readlines():
        #print line
        corpus.append(line.strip())
    #print corpus
 
    #參考: http://blog.csdn.net/abcjennifer/article/details/23615947
    #vectorizer = HashingVectorizer(n_features = 4000)
    
    #將文字中的詞語轉換為詞頻矩陣 矩陣元素a[i][j] 表示j詞在i類文字下的詞頻
    vectorizer = CountVectorizer()
 
    #該類會統計每個詞語的tf-idf權值
    transformer = TfidfTransformer()
 
    #第一個fit_transform是計算tf-idf 第二個fit_transform是將文字轉為詞頻矩陣
    tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))
 
    #獲取詞袋模型中的所有詞語  
    word = vectorizer.get_feature_names()
    
    #將tf-idf矩陣抽取出來,元素w[i][j]表示j詞在i類文字中的tf-idf權重
    weight = tfidf.toarray()
 
    #列印特徵向量文字內容
    print 'Features length: ' + str(len(word))
    resName = "BHTfidf_Result.txt"
    result = codecs.open(resName, 'w', 'utf-8')
    for j in range(len(word)):
        result.write(word[j] + ' ')
    result.write('\r\n\r\n')
 
    #列印每類文字的tf-idf詞語權重,第一個for遍歷所有文字,第二個for便利某一類文字下的詞語權重  
    for i in range(len(weight)):
        #print u"-------這裡輸出第", i, u"類文字的詞語tf-idf權重------"  
        for j in range(len(word)):
            #print weight[i][j],
            result.write(str(weight[i][j]) + ' ')
        result.write('\r\n\r\n')
 
    result.close()
 
 
    ########################################################################
    #                               第二步 聚類Kmeans
 
    print 'Start Kmeans:'
    from sklearn.cluster import KMeans
    clf = KMeans(n_clusters=4)   #景區 動物 人物 國家
    s = clf.fit(weight)
    print s
 
    '''
    print 'Start MiniBatchKmeans:'
    from sklearn.cluster import MiniBatchKMeans
    clf = MiniBatchKMeans(n_clusters=20)
    s = clf.fit(weight)
    print s
    '''
 
    #中心點
    print(clf.cluster_centers_)
    
    #每個樣本所屬的簇
    label = []               #儲存400個類標 
    print(clf.labels_)
    i = 1
    while i <= len(clf.labels_):
        print clf.labels_[i-1]
        label.append(clf.labels_[i-1])
        i = i + 1
 
    #用來評估簇的個數是否合適,距離越小說明簇分的越好,選取臨界點的簇個數  958.137281791
    print(clf.inertia_)
    y_pred = clf.labels_
    
 
 
    ########################################################################
    #                               第三步 圖形輸出 降維
 
    from sklearn.decomposition import PCA
    pca = PCA(n_components=2)             #輸出兩維
    newData = pca.fit_transform(weight)   #載入N維
    print newData

    x = [n[0] for n in newData]
    y = [n[1] for n in newData]

    x1, y1 = [], []   
    x2, y2 = [], [] 
    x3, y3 = [], []
    x4, y4 = [], []
    
    #分佈獲取類標為0、1、2、3的資料 賦值給(x1,y1) (x2,y2) (x3,y3) (x4,y4)
    i = 0  
    while i < len(newData):  
        if y_pred[i]==0:  
            x1.append(newData[i][0])  
            y1.append(newData[i][1])  
        elif y_pred[i]==1:  
            x2.append(newData[i][0])  
            y2.append(newData[i][1])  
        elif y_pred[i]==2:  
            x3.append(newData[i][0])  
            y3.append(newData[i][1])
        elif y_pred[i]==3:  
            x4.append(newData[i][0])  
            y4.append(newData[i][1])
        i = i + 1
        
    #四種顏色 紅 綠 藍,marker='x'表示型別,o表示圓點 *表示星型 x表示點   
    plot1, = plt.plot(x1, y1, 'or', marker="o", markersize=10)    
    plot2, = plt.plot(x2, y2, 'og', marker="o", markersize=10)    
    plot3, = plt.plot(x3, y3, 'ob', marker="o", markersize=10)
    plot4, = plt.plot(x4, y4, 'oy', marker="o", markersize=10)   
    #plt.title("K-Means Text Clustering")  #繪製標題
    plt.legend((plot1, plot2, plot3, plot4), ('A', 'B', 'C', 'D'), fontsize=10)  
 
    #四種顏色 紅 綠 藍 黑
    #plt.scatter(x1, x2, c=clf.labels_,  s=100)
    plt.show()

輸出結果如下所示,將400個網頁文字聚整合4類(類標0~3),每一類表示人物、景區、國家、動物。
 

[3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 0 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 0 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

同時繪製相關圖形如下所示,注意散點圖類標的繪製。
 

 

 

 

五. 結果評價


這裡我想結合文字聚類簡單敘述下最常用的評估方法:
        正確率 Precision = 正確識別的個體總數 /  識別出的個體總數
        召回率 Recall = 正確識別的個體總數 /  測試集中存在的個體總數
        F值 F-measure = 正確率 * 召回率 * 2 / (正確率 + 召回率)

由於"clf.labels_"會返回聚類每個樣本所屬的簇,比如400行資料,就會返回400個label值。同時,clf = KMeans(n_clusters=4)設定了類簇為4,故每個值對應在0、1、2、3中的一個,統計結果如下:

動物:99識別正確、共識別100個
國家:97識別正確、共識別97個
人物:98識別正確、共識別98個
景區:99識別正確、共識別105個

 

 

其中以景區為例,識別出的label3數目為105,同時正確識別出的個體數=99,總共測試樣本共100個,故:
        準確率=99/105=0.9429
        召回率=99/100=0.9900
        F值=(2*0.9429*0.9900)/(0.9429+0.9900)=0.9659

最終輸出結果如下所示:

 

 

同時可以計算巨集平均聚類準確率(Macro-Prec)和巨集平均召回率(Macro-Rec)。

希望基礎性文章對您有所幫助,如果文章中有錯誤或不足之處還請海涵。
最後推薦作者的最新出版書籍:

 


 

本書主要包括上下兩冊:

    《Python網路資料爬取及分析從入門到精通(爬取篇)》
    《Python網路資料爬取及分析從入門到精通(分析篇)》


(By:Eastmount 2018-07-06 深夜8點  http://blog.csdn.net/eastmount/ )

 

 

相關文章