
這是一個不快樂的十月,我們看到了太多離别。
昨天下午,香港媒體公布了又一則令國人震驚的消息,一代文學巨匠金庸先生逝世,享年94歲。
影響幾代人的金庸大俠,就這樣離開了我們。
今天小編翻出一篇舊文,當時我們用Python算法對金庸大師筆下的人物關系進行了一番分析,權當是學習之餘的一種演練。如今再看此文,希望能以此,對大師進行一番緬懷。大師仙逝,傳世作品和俠肝義膽長存。
正文
對于預處理得到的資料,我們會進行一些資料統計,分析資料的統計情況。對于文本資料,一個比較有用的統計資料是詞頻。所謂詞頻(Word Frequncy),指的是單詞在文本中出現的次數。
詞頻的統計—誰是金庸大師筆下的主角?
具體到小說分析問題,我們可以統計小說中的各個角色的出場次數。顯然,對于角色來說,主要人物的出場次數多,詞頻也較高,是以可以通過詞頻統計,大緻看出小說中誰的主角光環比較強。
假定所有的小說檔案的名稱都是“小說名.txt”,編碼都是utf8。
仍然以《天龍八部》為例,首先用codecs子產品,讀取小說的内容:
with codecs.open("天龍八部.txt", encoding="utf8") as f:
content = f.read()
利用人物字典character_info得到《天龍八部》中所有人物的清單chars,并用字元串的.count()方法對chars中單個人物c在content出現的次數進行計數:
chars = character_info["天龍八部"]
counts = [content.count(c) for c in chars]
為了友善對詞頻進行排序,我們将這兩個清單變成NumPy數組:
import numpy as np
chars = np.array(character_info["天龍八部"])
counts = np.array([content.count(c) for c in chars])
利用數組的.argsort()方法對單詞出現的次數counts進行排序,得到相應的下标:
idx = counts.argsort()
預設情況下,數組的排序是按照從小到大進行的。是以,可以使用idx[-10:]得到《天龍八部》中出現次數最多的10個角色的索引并利用索引i得到人名及其出現的次數:
for i in idx[-10:]:
print chars[i], counts[i]
木婉清 688
段正淳 706
王語嫣 814
慕容複 861
阿朱 961
喬峰 1086
阿紫 1108
虛竹 1584
蕭峰 1724
段譽 3245
從詞頻統計來看,段譽榮登主角寶座
詞頻的繪圖—誰是金庸大師筆下的主角?
我們還可以對詞頻進行繪圖,進而得到更直覺的結果。
在繪制詞頻圖像之前,需要解決在Matplotlib中顯示漢字的問題。
預設情況下,Matplotlib是不能直接顯示漢字的。例如,直接添加漢字标題會得到如圖1所示的結果:
import matplotlib.pyplot as plt
plt.title("中文")
plt.show()
圖1 不能正常顯示的中文标題
标題中應該顯示“中文”兩個字的地方被兩個方塊替代了,原因是Matplotlib找不到合适的中文字型去顯示中文。
為此,我們需要找到一些支援中文的字型。
Windows 7 及以上的系統中,字型庫的位置為 C:/Windows/Fonts,如:
l 宋體:C:/Windows/Fonts/simsun.ttc
Linux 系統可以通過 fc-list 指令檢視已有的字型和相應的位置,如:
l 宋體:/usr/share/fonts/truetype/osx-font-family/Songti.ttc
也可以從網上下載下傳字型:
l Yahei Consolas 字型:YaHei.Consolas.1.11b.ttf;
字型可以使用matplotlib.font_manager 中的FontProperties類導入:
from matplotlib.font_manager import FontProperties
font_song = FontProperties(fname="C:/Windows/Fonts/simsun.ttc")
其中,fname參數表示字型檔案的位置。
這樣,我們就将宋體導入了對象font_song中。
導入後,為了顯示漢字我們在需要寫入中文文字的函數中,加入fontProperties參數,指定顯示文字的字型為font_song。例如,對于第一小節中的例子,加入參數指定宋體後,标題可以正常顯示中文,得到如圖2所示的結果:
import matplotlib.pyplot as plt
plt.title("中文", fontproperties=font_song)
plt.show()
圖2 正常顯示的中文标題
解決圖像中文顯示的問題後,我們回到文本分析的案例中。
為了讓詞頻的顯示更直覺,可以使用plt.barh()函數繪制一個縱向的條形圖,來表示《天龍八部》中出現次數前10位的人物的詞頻統計結果,如圖3所示:
plt.barh(range(10), counts[idx[-10:]])
plt.title("天龍八部", fontproperties=font_song)
plt.yticks(range(10), chars[idx[-10:]], fontproperties=font_song)
plt.show()
其中,橫軸是詞出現的次數,縱軸是對應的人物名。
從圖3中可以看到,詞頻圖像的顯示要比文字更為直覺,不難得出“段譽是貫穿全書的主角”的猜想。
為了統計其他小說的詞頻,我們定義一個函數find_main_chars(),實作詞頻統計和繪圖的過程。該函數接受兩個參數:novel和num,其中novel是小說的名稱,num是顯示的主角個數(預設為10):
圖10-3 《天龍八部》主角詞頻統計
def find_main_chars(novel, num=10):
# 讀取檔案
with codecs.open("{}.txt".format(novel), encoding="utf8") as f:
content = f.read()
# 詞頻統計
chars = np.array(character_info[novel])
counts = np.array([content.count(c) for c in chars])
idx = counts.argsort()
# 繪圖
plt.barh(range(num), counts[idx[-num:]])
plt.title(novel, fontproperties=font_song)
plt.yticks(range(num), chars[idx[-num:]], fontproperties=font_song)
plt.show()
可以利用該函數檢視其他小說中的詞頻統計。例如,《射雕英雄傳》和《倚天屠龍記》的詞頻統計如圖4和5所示:
圖4 《射雕英雄傳》主角詞頻統計
圖5 《倚天屠龍記》主角詞頻統計
find_main_charecters("射雕英雄傳")
find_main_charecters("倚天屠龍記")
從圖4和圖5中,不難得出《射雕英雄傳》的主角是郭靖以及《倚天屠龍記》的主角是張無忌的結論。
以上結果說明,詞頻統計能夠較好地找出小說中的主角人物。
關系分析
找出主角之後,我們再來運用關系分析的方法,找出下金庸先生筆下,各路英雄豪傑的微妙關系。
之前有人研究過,用Word2Vec生成的詞向量存在這樣的現象:
vec("北京")-vec("中國")=vec("巴黎")-vec("法國")
這種現象通常說明了一種對應關系:
“北京”之于“中國”,正如“巴黎”之如“法國”.
在gensim中,對應關系也可以使用模型的.most_similar()方法來找到。利用該方法,可以定義函數find_relationship()來尋找這樣的對應關系,該函數接受a,b,c作為參數,以a和b的關系作為參考,傳回與c對應具有類似關系的d。
在函數中,我們将.most_similar()方法中的postive參數設為b和c組成的清單,并加入negative參數,将其設為隻有a一個元素組成的清單。.most_similar()方法傳回的結果仍然是前10個相關詞及其對應的相似度,這裡取傳回結果的第一個,作為最終找到的d。
是以,函數find_relationship()的定義為:
def find_relationship(a, b, c):
d, _ = model.most_similar(positive=[c, b], negative=[a])[0]
print "{}之于{},正如{}之于{}".format(a, b, c, d)
利用find_relationship()函數,我們可以找出人物之間存在的一些微妙關系:
find_relationship("郭靖", "黃蓉", "楊過")
find_relationship("郭靖", "華筝", "楊過")
find_relationship("段譽", "公子", "韋小寶")
find_relationship("郭靖", "降龍十八掌", "黃蓉")
郭靖之于黃蓉,正如楊過之于小龍女
郭靖之于華筝,正如楊過之于郭芙
段譽之于公子,正如韋小寶之于大人
郭靖之于降龍十八掌,正如黃蓉之于打狗棒法
最鐵的關系大概是這對:
find_relationship("楊過", "小龍女", "韋小寶")
find_relationship("令狐沖", "盈盈", "韋小寶")
find_relationship("郭靖", "黃蓉", "韋小寶")
楊過之于小龍女,正如韋小寶之于康熙
令狐沖之于盈盈,正如韋小寶之于康熙
郭靖之于黃蓉,正如韋小寶之于康熙
這些關系也能佐證使用詞向量模型的合理性。
以上内容節選自
《自學Python—程式設計基礎、科學計算及資料分析》