【大数据】《红楼梦》作者分析(QDU)
- 【大數據】蔬菜價格分析(QDU)
- 【大數據】美國新冠肺炎疫情分析——錯誤版(QDU)
- 【大數據】美國新冠肺炎疫情分析——正確版(QDU)
- 【大數據】乳腺癌預測——老師給的鏈接(QDU)
- 由于kaggle上“貓狗大戰”的測試集標簽是錯的,所以沒做出來,用的github上的代碼
- 【大數據】《紅樓夢》作者分析(QDU)
問題分析
《紅樓夢》是我國著名的四大名著之一,一般的認為《紅樓夢》的前八十回為曹雪芹撰寫,后四十回為高鶚續寫,但也有學者對此并不認可。
一般來說,不同的作者往往會具有不同的寫作風格,這些風格可以通過在文中的虛詞的頻率進行衡量,因此,可以考慮統計各章中虛詞出現頻率,并以此作為基礎數據來聚類分析,對《紅樓夢》章節進行劃分,從而分析章節與作者之間的關系。
解決思路
對比三種方式“K-Means聚類”、“層次聚類”和“DBSCAN”實現聚類效果上的區別,選擇最佳的聚類方式判斷《紅樓夢》的作者數。
數據處理
按行讀入txt文本,劃分章節,對于每個章節統計總漢字個數和虛詞出現的個數,記錄下每個章節的起始行號。
計算每個章節每種虛詞出現的頻率,以頻率作為依據進行聚類分析。
總計120個章節,46個虛詞。
統計虛詞個數時應當注意,'罷咧’和’罷了’的出現會過多統計’罷‘的個數,因此對每個章節而言,統計完全部的行時,應當將’罷‘的數量減去’罷咧’和’罷了’的數量。
劃分章節時需要判斷該章節關鍵詞的出現是因為文中提及上回還是作為新的章節的標題,因為對上回的提及是不作為新的章節進行劃分的。
K-Means聚類
繪制分類數-DBI得分曲線,根據曲線變化確定最佳分類數。
戴維森堡丁指數(DBI),又稱為分類適確性指標,是由大衛L·戴維斯和唐納德·Bouldin提出的一種評估聚類算法優劣的指標。首先假設我們有m個時間序列,這些時間序列聚類為nnn個簇。mmm個時間序列設為輸入矩陣XXX,nnn個簇類設為NNN作為參數傳入算法。使用下列公式進行計算:
DBI=1N∑i=1Nmax?j≠i(Siˉ+Sjˉ∣∣wi?wj∣∣2)DBI=\frac{1}{N}\sum_{i=1}^N\max \limits_{j≠i}(\frac{\bar{S_i}+\bar{S_j}}{||w_i-w_j||_2}) DBI=N1?i=1∑N?j?=imax?(∣∣wi??wj?∣∣2?Si?ˉ?+Sj?ˉ??)
這個公式的含義是度量每個簇類最大相似度的均值。DBI的值最小是0,值越小,代表聚類效果越好。
繪制分類數-CH得分曲線,根據曲線變化確定最佳分類數。
CH分數也稱之為 Calinski-Harabaz Index,這個計算簡單直接,得到的Calinski-Harabasz分數值sss越大則聚類效果越好。
s(k)=Tr(Bk)Tr(Wk)×N?kk?1s(k)=\frac{Tr(B_k)}{Tr(W_k)}×\frac{N-k}{k-1} s(k)=Tr(Wk?)Tr(Bk?)?×k?1N?k?
其中
BkB_kBk?稱之為 between-clusters dispersion mean (簇間色散平均值)
WkW_kWk?稱之為 within-cluster dispersion (群內色散之間)
它們的計算公式如下:
Wk=∑q=1k∑x∈cq(x?cq)(x?cq)TBk=∑qnq(cq?c)(cq?c)TW_k=\sum_{q=1}^k\sum_{x∈c_q}(x-c_q)(x-c_q)^T \\\\ B_k=\sum_{q}n_q(c_q-c)(c_q-c)^T Wk?=q=1∑k?x∈cq?∑?(x?cq?)(x?cq?)TBk?=q∑?nq?(cq??c)(cq??c)T
類別內部數據的協方差越小越好,類別之間的協方差越大越好,這樣的Calinski-Harabasz分數會高。
層次聚類
層次聚類(Hierarchical Clustering)是聚類算法的一種,基于層次的聚類算法(Hierarchical Clustering)可以是凝聚的(Agglomerative)或者分裂的(Divisive),取決于層次的劃分是“自底向上”還是“自頂向下”。
凝聚層次聚類原理是:最初將每個對象看成一個簇,然后將這些簇根據某種規則被一步步合并,就這樣不斷合并直到達到預設的簇類個數。
計算聚類簇之間的距離的方法:
計算聚類簇間距離的方法有三種,分別為Single Linkage,Complete Linkage和Average Linkage。
- Single Linkage:方法是將兩個組合數據點中距離最近的兩個數據點間的距離作為這兩個組合數據點的距離。這種方法容易受到極端值的影響。兩個不相似的組合數據點可能由于其中的某個極端的數據點距離較近而組合在一起。
- Complete Linkage:Complete Linkage的計算方法與Single Linkage相反,將兩個組合數據點中距離最遠的兩個數據點間的距離作為這兩個組合數據點的距離。Complete Linkage的問題也與Single Linkage相反,兩個不相似的組合數據點可能由于其中的極端值距離較遠而無法組合在一起。
- Average Linkage:Average Linkage的計算方法是計算兩個組合數據點中的每個數據點與其他所有數據點的距離。將所有距離的均值作為兩個組合數據點間的距離。這種方法計算量比較大,但結果比前兩種方法更合理。
DBSCAN
DBSCAN算法:
從樣本空間中任意選擇一個樣本,以事先給定的半徑做圓,凡被該圓圈中的樣本都視為與該樣本處于相同的聚類,以這些被圈中的樣本為圓心繼續做圓,重復以上過程,不斷擴大被圈中樣本的規模,直到再也沒有新的樣本加入為止,至此即得到一個聚類。于剩余樣本中,重復以上過程,直到耗盡樣本空間中的所有樣本為止。
DBSCAN函數聚類效果主要取決于其參數eps和min_samples,因此在使用DBSCAN算法進行聚類時主要問題就是確定這兩個參數的值。
采用遍歷的方法計算得分,以得分最大的參數作為最終參數。
解決方案
K-Means聚類
不同的得分計算方式下不同分類數對應的效果
觀察兩條曲線,將DBI得分作為評判模型好壞的標準時,可以發現隨著分類數的增多,分類得分先增多后減少,即分類效果先降低后升高;而采用CH得分作為指標時,分類效果隨著分類數的增多持續下降。
綜合兩條曲線的變化趨勢,可以總結出當分類個數為2、3左右時分類效果最佳。
層次聚類
對比采用不同的linkage和不同的分類數對分類效果的影響,選取分類得分最高的作為分類算法
sklearn中的層次聚類函數:
class sklearn.cluster.AgglomerativeClustering(n_clusters=2, affinity=’euclidean’, memory=None, connectivity=None, compute_full_tree=’auto’, linkage=’ward’, pooling_func=<function mean>)linkage:一個字符串,用于指定鏈接算法
- ‘ward’:單鏈接single-linkage,采用dmindmin
- ‘complete’:全鏈接complete-linkage算法,采用dmaxdmax
- ‘average’:均連接average-linkage算法,采用davgdavg
三種不同參數下得分隨分類數的變化曲線:
觀察曲線可以發現,除平均值linkage方式外,另外兩種在分類數為2時都能使模型體現較佳的效果,因此選取ward作為linkage。
DBSCAN
首先,經過大量測試,發現本實驗中DBSCAN函數的min_samples參數值選3比較合適,因此只需要遍歷確定最佳的eps即可。
對于遍歷的每一種eps取值,都計算CH得分,選取CH得分最大時對應的模型和eps。
最大CH得分及對應模型聚類結果:
# 標簽,其中-1表示孤立點,0表示第一類 # 因此,可以看出DBSCAN只分得一類 [-1 0]# 每一章對應的標簽 [ 0 0 -1 0 0 0 0 0 0 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 0 0 -1 -1 -1 -1 0 -1 0 -1 0 00 0 0 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 0 -10 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 -1 0 0 0 -10 0 0 -1 -1 0 -1 -1 -1 -1 -1 -1 0 -1 -1 0 -1 -1 -1 -1 -1 0 -1 0]# eps 1.6363636363636362# 得分 61.796597508998026結果分析
對比三種方式下的CH得分可以發現,與K-Means聚類和層次聚類相比,DBSCAN得分過低,因此不采用DBSCAN作為聚類的方法。
K-Means聚類和層次聚類得到的結果相對合理,因此對比兩種方式的聚類結果:
# K-Means聚類 [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 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 1 0 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 0 0 11 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1 0 0 1 0 0 1 1 0 0 0 0 01 0 0 0 1 0 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 0 0 0 0 0 0 0 11 0 0 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 11 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 1 0 0 1 1 1 0 1 0 01 0 0 1 1 0 1 1 1]# 兩種聚類方式的相似度,即標簽相同的個數與全部章節數之比 K-Means聚類與層次聚類結果的相似度:0.8666666666666667這說明兩種聚類結果比較相似,而且具有比較好的參考性。
可以判斷是兩個人共同完成的《紅樓夢》,但是對于其中一人完成前80章和另一人完成后80章的區分并不是很明顯。
總結展望
優勢
局限性
附錄(代碼)
import re import numpy as np import matplotlib.pyplot as plt from sklearn import metrics # !!!不是keras的!!! from matplotlib.ticker import MaxNLocator# ------------------------ 數據處理 ------------------------def is_Chinese(ch):if '\u4e00' <= ch <= '\u9fff':return Truereturn Falsecell=['之','其','或','亦','方','于','即','皆','因','仍','故','尚','乃','呀','嗎','咧','罷咧','啊','罷','罷了','么','呢','了','的','著','一','不','把','讓','向','往','是','在','別','好','可','便','就','但','越','再','更','比','很','偏','兒']file = open('05.《紅樓夢》完整版.txt',encoding='utf-8') file_context=file.readlines() # print(len(file_context)) # 行數hui = 0 # 第幾回 row_num_start = [] # 每一回的開始段號 for i in range(0, len(file_context)):round = re.findall(u'第[一二三四五六七八九十百]+回', file_context[i])if (len(round) == 1) & (file_context[i][0] == '第') & (len(file_context[i]) < 30): # 唯一出現依次且位于段首,整行字數不超過30個字,則判斷為新回row_num_start.append(i) # 保存每回的開始段號,本回的結束段號就是下回的開始段號前一個hui += 1# print(hui) # 總共120回 # print(len(cell)) # 總共46個虛詞 row_num_start.append(len(file_context)) # 文末frequency = [[0]*len(cell) for _ in range(hui)] # 統計頻次 # 注意不可以使用 [[0]*5]*6 的形式,會出現對二維列表中的某個位置進行賦值,卻使得整一列都被賦值了的情況 frequency_rate = [[0]*len(cell) for _ in range(hui)] # 統計頻率 for i in range(0, hui):all_character_num = 0 # 某一回的全部漢字數for j in range(row_num_start[i]+1, row_num_start[i+1]): # 遍歷某一回的全部行row_contxt = np.array(file_context[j])bool_row_context = (is_Chinese(row_contxt)) # bool數組all_character_num += np.sum(bool_row_context != 0) # 本行的漢字個數for k in range(0, len(cell)):frequency[i][k] += len(re.findall(cell[k],file_context[j]))# 罷咧rownum:16 罷rownum:18 罷了rownum:19frequency[i][18] -= frequency[i][16] + frequency[i][19] # “罷”的數數量要減去“罷了”和“罷咧”的數量for j in range(0, len(cell)):frequency_rate[i][j] = frequency[i][j] / all_character_num# ------------------------ K-means聚類 ------------------------from sklearn.cluster import KMeanstest_classifiation_num = 50 # 繪制DBI曲線 preds = [] for i in np.arange(2,test_classifiation_num):y_pred = KMeans(n_clusters=i, random_state=9).fit_predict(frequency_rate)preds.append(metrics.davies_bouldin_score(frequency_rate, y_pred)) # 戴維森堡丁指數(DBI) # DBI的值最小是0,值越小,代表聚類效果越好。 plt.subplot(121) plt.plot(np.arange(2, test_classifiation_num), preds) plt.gca().xaxis.set_major_locator(MaxNLocator(integer=True)) # 坐標軸顯示為整數 plt.xlabel('classifiation_number', fontsize=14) plt.ylabel('davies_bouldin_score', fontsize=14)# 繪制CH得分曲線 preds = [] for i in np.arange(2,test_classifiation_num):y_pred = KMeans(n_clusters=i, random_state=9).fit_predict(frequency_rate)preds.append(metrics.calinski_harabasz_score(frequency_rate, y_pred)) # CH分數 # 值越大效果越好 plt.subplot(122) plt.plot(np.arange(2, test_classifiation_num), preds) plt.gca().xaxis.set_major_locator(MaxNLocator(integer=True)) # 坐標軸顯示為整數 plt.xlabel('classifiation_number', fontsize=14) plt.ylabel('calinski_harabasz_score', fontsize=14)plt.suptitle('K-Means', fontsize=15) plt.show()# 觀察曲線變化可以發現,選取二聚類和三聚類效果相對可能會好# ------------------------ 層次聚類 ------------------------ from sklearn import clusterfig=plt.figure() ax=fig.add_subplot(1,1,1) markers="+o*"linkages=['ward','complete','average'] nums=range(2, test_classifiation_num)for i, linkage in enumerate(linkages):preds = []for num in nums:clst = cluster.AgglomerativeClustering(n_clusters=num, linkage=linkage)predicted_labels = clst.fit_predict(frequency_rate)preds.append(metrics.calinski_harabasz_score(frequency_rate, predicted_labels))ax.plot(nums, preds, marker=markers[i], label="linkage:%s" % linkage)ax.set_xlabel("n_clusters", fontsize=14) ax.set_ylabel("calinski_harabasz_score", fontsize=14) ax.legend(loc="best") fig.suptitle("AgglomerativeClustering", fontsize=15) plt.show()# 觀察發現linkage選取ward比較平穩,且二聚類的得分最高,這與K-means的結果類似# ------------------------ DBSCAN ------------------------from sklearn.cluster import DBSCAN models, scores = [], [] epsilons = np.linspace(0.5, 3, 100)for eps in epsilons:model = DBSCAN(eps=eps, min_samples=3)model.fit(frequency_rate)if np.unique(model.labels_).shape[0] < 2: # 保證分類數至少為2,只分一組沒法使用calinski_harabasz_score,會報錯continuescore = metrics.calinski_harabasz_score(frequency_rate, model.labels_)models.append(model)scores.append(score)index = np.array(scores).argmax() # 最大得分對應的索引 best_model = models[index] best_eps = epsilons[index] best_score = scores[index]predicted_labels = np.unique(best_model.labels_) print(predicted_labels) # [-1 0] print(best_model.labels_) print(best_eps) # 1.6363636363636362 print(best_score) # 61.796597508998026# DBSCAN的得分太低了KMeans_predict = KMeans(n_clusters=2, random_state=9).fit_predict(frequency_rate) print(KMeans_predict) AgglomerativeClustering_predict = \(np.array(cluster.AgglomerativeClustering(n_clusters=2, linkage='ward').fit_predict(frequency_rate)) == 0).astype(int) print(AgglomerativeClustering_predict)print('K-Means聚類與層次聚類結果的相似度:', np.sum((KMeans_predict == AgglomerativeClustering_predict) == 1) / hui) # 0.8666666666666667總結
以上是生活随笔為你收集整理的【大数据】《红楼梦》作者分析(QDU)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Excel实现报表
- 下一篇: window7取消文件默认打开方式的方法