Kmeans文檔聚類算法實現之python


實現文檔聚類的總體思想:

  1. 將每個文檔的關鍵詞提取,形成一個關鍵詞集合N;
  2. 將每個文檔向量化,可以參看計算余弦相似度那一章;
  3. 給定K個聚類中心,使用Kmeans算法處理向量;
  4. 分析每個聚類中心的相關文檔,可以得出最大的類或者最小的類等;

將已經分好詞的文檔提取關鍵詞,統計詞頻:

# 計算每個文檔的關鍵詞和詞頻
# 關鍵詞統計和詞頻統計,以列表形式返回
def Count(resfile):
    t = {}
    infile = open(resfile, 'r', encoding='utf-8')
    i = 0
    f = infile.readlines()
    count = len(f)
    # print(count)
    infile.close()
    s = open(resfile, 'r', encoding='utf-8')
    while i < count:
        line = s.readline()
        line = line.rstrip('\n')
        # print(line)
        words = line.split(" ")
        #   print(words)
        for word in words:
            if word != "" and t.__contains__(word):
                num = t[word]
                t[word] = num + 1
            elif word != "":
                t[word] = 1
        i = i + 1
    # 按鍵值降序
    dic = sorted(t.items(), key=lambda t: t[1], reverse=True)
    s.close()
    # 返回的是一篇文檔的詞項統計表,形式為[(word:出現次數)]
    return dic

  上面的count函數統計的一篇文檔的詞頻,如果每篇文檔都需要統計則需要調用這個count函數,每調用一次就返回一個dict,給一個文檔集統計詞頻的參考代碼如下(假設有500篇文檔):

def readfile():
    f = open("res.txt", "w", encoding="utf-8")
    # mergeword 用來記錄所有文檔的詞項集合,不重復,其長度是用來作為文檔向量維度
    mergeword = []
    everyDocumentDict = []
    for i in range(500):
       filedir
= "D:/PythonCodingLover/PythonPro/DailyStudy/互聯網項目二/CNS/"+ "CNS" +str(i)+"_C.txt" # 將每個文檔的字典寫入res.txt中 dict = Count(filedir) # everyDocumentDict記錄的是每篇文檔的詞項統計 everyDocumentDict.append(dict) # print(type(dict)) for j in range(len(dict)): if dict[j][0] not in mergeword: mergeword.append(dict[j][0]) f.close() # 返回文檔集的詞項集 return mergeword,everyDocumentDict

  上面兩部分可以實現將文檔集里的關鍵詞,擔心是否正確可以使用簡單的測試代碼,如下:

# 測試文檔集關鍵詞是否正確
mergeword ,eveKeywordOfCount= readfile()
print(type(eveKeywordOfCount))
print(len(eveKeywordOfCount))
print(len(mergeword))

將每篇文檔向量化,便於后面的文檔聚類:

  下面這個函數將所有的文檔向量一起返回,而不是一篇文檔向量;

# 現在有了500個文檔的總關鍵詞和每篇文檔的詞項統計,所以我們現在要做的是將每篇文檔向量化,維度是len(mergeword)
# 注意EveDocCount的結構是[[(),()],[(),()]],里面記錄的列表是每個文檔的詞項統計,而括號里面的是keyword:詞頻

print("-------------------文檔向量化開始操作-----------------") def VectorEveryDoc(EveDocCount,mergeword): # vecOfDoc列表記錄的是每篇文檔向量化后的向量列表,共有500個元素 vecOfDoc = [] # vecDoc列表記錄的是一篇文檔的向量模型,向量化后添加到vecOfDoc vectorLenth = len(mergeword) # 下面開始將500文檔向量化 i = 0 while i < 500: # EveDocCount[i]記錄的是第幾篇文檔的詞項統計 vecDoc = [0] * vectorLenth # 測試是正確的 # print(EveDocCount[i]) for ch in range(len(EveDocCount[i])): # termFrequence 是詞項對應的頻數 termFrequence = EveDocCount[i][ch][1] # keyword是詞項 keyword = EveDocCount[i][ch][0] # 下面開始具體的向量化 j = 0 while j < vectorLenth: if keyword == mergeword[j]: # 這里是J 而不是 I ,寫錯了就很容易出錯了 vecDoc[j] = termFrequence break else: j = j + 1 vecOfDoc.append(vecDoc) i = i+ 1 # 返回500個文檔的文檔向量 return vecOfDoc print("-------------------文檔向量化操作結束-----------------")

向量化結束之后,便需要計算余弦距離(也可以使用其他距離,例如歐幾里得距離):

  說明:一個文檔集的關鍵詞可能有很多,為了方便后面的計算,引入科學計算包numpy,示例代碼如下:

  

# 導入科學計算包
import numpy as np

  而后將500個文檔向量傳給numpy的數組,構造矩陣,示例代碼如下:

resultVec = VectorEveryDoc(eveKeywordOfCount,mergeword)
vecDate = np.array(resultVec)

  之后便計算余弦相似度,這里和前面寫的余弦距離相似度計算類似,不同的是使用了nmupy數組,注意其中的矩陣乘法,示例代碼如下:

# 計算余弦距離
def CalConDis(v1,v2):
    lengthVector = len(v1)
    # 計算出兩個向量的乘積
    # 將v2變換,轉置矩陣v2
    v2s =v2.T
    B = np.dot(v1,v2s)
    # 計算兩個向量的模的乘積
    v1s = v1.T
    A1 = np.dot(v1,v1s)
    A2 = np.dot(v2,v2s)
    A = math.sqrt(A1) * math.sqrt(A2)
    # print('相似度 = ' + str(float(B) / A))
    resdis = format(float(B) / A,".3f")
    return float(resdis)

Kmeans聚類算法實現文檔聚類:

隨機產生K個聚類中心點:

# 隨機選取中心點,dateSet是m * n矩陣,K是要指定的聚類的個數
def createRandomCent(dateSet,k):
    # 返回整個矩陣的列的列數
    n = np.shape(dateSet)[1]
    # 創建一個k * n 的零矩陣
    centroids = np.mat(np.zeros((k, n)))
    # 隨機產生k個中心點
    for j in range(n):
        minJ = min(dateSet[:, j])
        rangeJ = float(max(dateSet[:, j]) - minJ)
        centroids[:, j] = np.mat(minJ + rangeJ * np.random.rand(k, 1))
    # 返回隨機產生的k個中心點
    return centroids

Kmeans算法按照隨機產生的聚類中心開始聚類,返回的一個矩陣:

countclu = 1
# 具體的Kmeans算法實現
# dateset是指500個文檔的向量集合(500 * length),dis用的是余弦距離,k是給定的k個聚類中心,createCent是隨機生成的K個初始中心
def dfdocKmeansCluster(dateset,k,discos = CalConDis,createCent = createRandomCent):
    # docCount 記錄的總共有多少個樣本,既矩陣的行數
    docCount = np.shape(dateset)[0]
    # 在構建一個500 * 2的0矩陣,用來存放聚類信息
    docCluster = np.mat(np.zeros((docCount,2)))

    # 初始化K個聚類中心
    centerOfCluster = createCent(dateset,k)
    # clusterFlag用來判定聚類是否結束
    clusterFlag = True
    while clusterFlag:
        clusterFlag = False
        for each in range(docCount):
            # 將最大余弦距離初始化成一個負數
            maxCosDis = -100
            # 文檔索引
            minIndex = -1
            # 找到每篇文檔距離最近的中心
            for i in range(k):
                # 計算每個文檔到中心點的余弦相似度,
                global countclu
                countclu = countclu+ 1
                print("已經聚類第" + str(countclu) + "")
                distcosOfDocToDoccenter = discos(centerOfCluster[i, :], dateset[each, :])
                # 選擇余弦距離最大的一個中心
                if distcosOfDocToDoccenter > maxCosDis:

                    maxCosDis = distcosOfDocToDoccenter
                    minIndex = i
            if docCluster[each, 0] != minIndex:
                # 如果沒到最優方案則繼續聚類
                clusterFlag = True
            # 第1列為所屬中心,第2列為余弦距離
            docCluster[each, :] = minIndex, maxCosDis
        # 打印隨機產生的中心點
        print(centerOfCluster)

        # 更改聚類中心點
        for cent in range(k):
            ptsInClust = dateset[np.nonzero(docCluster[:, 0].A == cent)[0]]
            centerOfCluster[cent, :] = np.mean(ptsInClust, axis=0)
    # 返回K個中心點,
    return centerOfCluster,docCluster

  這里返回的一個500*2的矩陣,第一列是聚類中心,第二列是和中心的余弦距離,索引就是文檔編號

  如果需要得出具體的類有幾篇文檔等問題,則需要對返回的矩陣進行分析(注意是numpy矩陣);

  程序到了這里。就基本上結束了;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM