天天看點

一篇舊文,以此緬懷金庸先生

一篇舊文,以此緬懷金庸先生

這是一個不快樂的十月,我們看到了太多離别。

昨天下午,香港媒體公布了又一則令國人震驚的消息,一代文學巨匠金庸先生逝世,享年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—程式設計基礎、科學計算及資料分析》