
取名10行代碼看懂紅樓夢,是將介紹使用python代碼來讀紅樓夢獲取其主要人物。這里的思想就是詞頻統計,通過分析紅樓夢小說文字中出現最多的詞語,來概括說明紅樓夢的核心人物和事情。實際上如果你能跟著往下看,就開始進入了自然語言處理的一些基礎知識。
在正式進入讀紅樓夢之前,需要先鋪墊一些詞頻統計相關知識。因此首先從英文的詞頻統計操作開始,其中的思想用到了大數據分布式處理里的mapreduce框架,在該框架中主要包括兩個任務:map(映射)和reduce(規約)。這里不具體討論MapReduce的思想和處理流程,我們來看一下在python中實現wordcount詞頻統計任務,進而來體驗一下其基本思路。后面再來實現中文的詞頻統計分析,進而看懂紅樓夢。
英文文章的詞頻統計
詞頻統計任務是一個非常常見的任務,也是相對較為簡單的程序。任務就是從一段文字中將單詞出現的次數統計出來,例如從ChinaDaily英文網站上關注一段新聞:
CHENGDU -- Rescuers have located 14 miners trapped underground in a flooded coal mine in Southwest China's Sichuan province, local authorities said Sunday.
The rescuers are clearing the shaft and drilling a deep hole to reach the trapped miners. They are also trying to pump and block water in a bid to prevent the rising of underground water levels and are sending more oxygen down the underground shaft.
The accident occurred at 3:26 pm Saturday at the Shanmushu coal mine owned by Sichuan Coal Industry Group in Gongxian County when 347 miners were working underground. A total of 329 escaped and four were killed.
Nearly 200 rescuers are racing against the clock to reach the trapped miners.
Due to communication interruptions in some mining areas, the workers were not immediately located. But through their consistent efforts, the rescue workers have finally located the remaining miners。
那這段話有多少個單詞呢?最笨的辦法就是一個個的數,但這顯然不是我們想要的方式。既然有python,我們可以嘗試使用程序來解決這個問題。
我們先來理清一下思路順序。
(1)首先需要將這段文字從網站上拷貝下來或者爬取下來保存成文本文件;
(2)然后在python中讀取該文件開始處理這個段落。因為是單詞統計,很明顯單詞與單詞之間主要分割標記就是空格,如果使用空格來分割段落文字,就可以將段落打散為一個個的單詞列表了。不過同時看到段落中除了空格外,還有標點符號以及數字,這些也都需要去除。在整理好單詞列表后,就可以使用map方式將所有單詞與其出現的次數構建成<單詞,次數>這種key-value結構對。
(3)然后使用reduce規約思想將這種結構對進一步處理,即將相同單詞的次數累加,獲得每個單詞出現的頻率。
根據思路我們來組織程序代碼:
第一步,簡單點,將新聞段落復制粘貼到記事本里,保存為news.txt文件。這個部分就不需要代碼了。
第二步,python讀這個文件,將段落讀出來。這里定義個函數為readPara,即讀取段落文字,函數參數為filename。定義完函數后就可以測試一下。
#定義一個讀取段落文字的函數
def getPara(filename):with open(filename,'r') as f:content=f.readlines()return content#給定文本文件的位置
file="news.txt"
print(getPara(file))
測試結果返回一個列表,具體如下:
["CHENGDU -- Rescuers have located 14 miners trapped underground in a flooded coal mine in Southwest China's Sichuan province, local authorities said Sunday.n", 'The rescuers are clearing the shaft and drilling a deep hole to reach the trapped miners. They are also trying to pump and block water in a bid to prevent the rising of underground water levels and are sending more oxygen down the underground shaft.n', 'The accident occurred at 3:26 pm Saturday at the Shanmushu coal mine owned by Sichuan Coal Industry Group in Gongxian County when 347 miners were working underground. A total of 329 escaped and four were killed.n', 'Nearly 200 rescuers are racing against the clock to reach the trapped miners.n', 'Due to communication interruptions in some mining areas, the workers were not immediately located. But through their consistent efforts, the rescue workers have finally located the remaining miners。']
第三步,開始分割段落為單詞。這里的任務包括去除其中的非單詞字符,如標點符號和數字。
這個段落稍微有點復雜,那就是最后一個單詞miners那有個中文的句號,需要先將其清除。清除的辦法采用分割方法split函數:split('。')。分割后獲得的為兩個列表,這里只需要取第一個列表即可,因為第二個列表為標點符號句號。
for para in content:paraText=para.split('。')paraText=para[0]
然后在剩下的段落文本paraText中采用英文的句號繼續分割:split('.'),形成多個句子的列表。
for para in content:paraText=para.split('。')paraText=para[0]paraList=paraText.split('.')
緊接著就可以將句子打散為單詞了。不過其中還有一些換行符號和非英文單詞字符,可以使用python自帶的isalpha函數來判斷,isalpha函數就是用于判斷整個單詞是否都是字母組成,如果判斷為真,說明就是單詞,如果不是,就說明不是單詞。這樣做問題都不大,不過在本次段落中出現了一個China's,被誤殺了。這種連接拼寫確實不是很好處理,這里也只能先舍棄掉。后面再想辦法來處理。
打散后,然后將單詞再一一的添加到一個新的列表中,這樣形成整個段落的英文單詞列表。所以可以先定義一個words_list空列表,然后后面使用append方法將打散的單詞添加進去。
同時在單詞處理的時候,大小寫還是需要注意的,這里將所有大寫都變成小寫即可。使用方法就是單詞作為字符串對象,使用其lower函數即可。
整個過程的代碼組織如下:
def mapper(filename):words_list=[] #定義一個空列表with open(filename,'r') as f: #打開段落所在的文本文件content=f.readlines() #從頭讀到尾并保存到content列表中。默認會按段落分割for para in content: #對每一個段落列表進行處理para=para.split('。') #如果存在中文句號,將整個段落按句號分割,形成兩個大的列表para0=para[0].split('.') #取第一個列表,并使用英文句號分割,形成多個不含句號的文本列表for item in para0: #對每一個文本列表進行處理words=item.split(' ') #采用空格分割方式將文本列表打散為單詞或者其他字符for word in words: #對每個單詞或其他字符組合進行判斷if word.isalpha()==False: #如果不是單詞時continue #就不執行下面的操作words_list.append(word.lower()) #將所有的英文單詞變成小寫后一個個添加到words_list列表中print(words_list) #打印測試查看最終獲得的單詞列表
運行后結果如下:
['chengdu', 'rescuers', 'have', 'located', 'miners', 'trapped', 'underground', 'in', 'a', 'flooded', 'coal', 'mine', 'in', 'southwest', 'sichuan', 'local', 'authorities', 'said', 'sunday', 'the', 'rescuers', 'are', 'clearing', 'the', 'shaft', 'and', 'drilling', 'a', 'deep', 'hole', 'to', 'reach', 'the', 'trapped', 'miners', 'they', 'are', 'also', 'trying', 'to', 'pump', 'and', 'block', 'water', 'in', 'a', 'bid', 'to', 'prevent', 'the', 'rising', 'of', 'underground', 'water', 'levels', 'and', 'are', 'sending', 'more', 'oxygen', 'down', 'the', 'underground', 'shaft', 'the', 'accident', 'occurred', 'at', 'pm', 'saturday', 'at', 'the', 'shanmushu', 'coal', 'mine', 'owned', 'by', 'sichuan', 'coal', 'industry', 'group', 'in', 'gongxian', 'county', 'when', 'miners', 'were', 'working', 'underground', 'a', 'total', 'of', 'escaped', 'and', 'four', 'were', 'killed', 'nearly', 'rescuers', 'are', 'racing', 'against', 'the', 'clock', 'to', 'reach', 'the', 'trapped', 'miners', 'due', 'to', 'communication', 'interruptions', 'in', 'some', 'mining', 'the', 'workers', 'were', 'not', 'immediately', 'located', 'but', 'through', 'their', 'consistent', 'the', 'rescue', 'workers', 'have', 'finally', 'located', 'the', 'remaining', 'miners']
除了上述說的China's外,其他的單詞都進入了列表。
第四步,開始統計單詞出現的次數,處理的時候可以先定義個空字典,然后讀取列表中的單詞,如果在字典中已存在,則將其出現次數累加,如果不存在,則將其次數設定為1:
for word in words_list:if word in words_dict:words_dict[word]+=1else:words_dict[word]=1
這里我們可以單獨定義一個函數如reduce,其輸入為第三步的單詞列表。
def reduce(words):words_dict={}for word in words:if word in words_dict:words_dict[word]+=1else:words_dict[word]=1return words_dict
將第三步的單詞列表傳入reduce函數,打印一下處理結果如下:
{'chengdu': 1, 'rescuers': 3, 'have': 2, 'located': 3, 'miners': 5, 'trapped': 3, 'underground': 4, 'in': 5, 'a': 4, 'flooded': 1, 'coal': 3, 'mine': 2, 'southwest': 1, 'sichuan': 2, 'local': 1, 'authorities': 1, 'said': 1, 'sunday': 1, 'the': 12, 'are': 4, 'clearing': 1, 'shaft': 2, 'and': 4, 'drilling': 1, 'deep': 1, 'hole': 1, 'to': 5, 'reach': 2, 'they': 1, 'also': 1, 'trying': 1, 'pump': 1, 'block': 1, 'water': 2, 'bid': 1, 'prevent': 1, 'rising': 1, 'of': 2, 'levels': 1, 'sending': 1, 'more': 1, 'oxygen': 1, 'down': 1, 'accident': 1, 'occurred': 1, 'at': 2, 'pm': 1, 'saturday': 1, 'shanmushu': 1, 'owned': 1, 'by': 1, 'industry': 1, 'group': 1, 'gongxian': 1, 'county': 1, 'when': 1, 'were': 3, 'working': 1, 'total': 1, 'escaped': 1, 'four': 1, 'killed': 1, 'nearly': 1, 'racing': 1, 'against': 1, 'clock': 1, 'due': 1, 'communication': 1, 'interruptions': 1, 'some': 1, 'mining': 1, 'workers': 2, 'not': 1, 'immediately': 1, 'but': 1, 'through': 1, 'their': 1, 'consistent': 1, 'rescue': 1, 'finally': 1, 'remaining': 1}
第五步,由此我們將每個單詞出現的次數都統計了一下,最后需要對該字典按value方式排序,出現次數多的排列在前面,少的排列在后面,代碼為:
def reduce(words):words_dict={}for word in words:if word in words_dict:words_dict[word]+=1else:words_dict[word]=1words_dict=sorted(words_dict.items(),key=lambda x:x[1],reverse=True) #字典按value排序return dict(words_dict) #排序后為元組列表,使用dict函數將其轉換為字典
再來測試結果就得到了:
{'the': 12, 'miners': 5, 'in': 5, 'to': 5, 'underground': 4, 'a': 4, 'are': 4, 'and': 4, 'rescuers': 3, 'located': 3, 'trapped': 3, 'coal': 3, 'were': 3, 'have': 2, 'mine': 2, 'sichuan': 2, 'shaft': 2, 'reach': 2, 'water': 2, 'of': 2, 'at': 2, 'workers': 2, 'chengdu': 1, 'flooded': 1, 'southwest': 1, 'local': 1, 'authorities': 1, 'said': 1, 'sunday': 1, 'clearing': 1, 'drilling': 1, 'deep': 1, 'hole': 1, 'they': 1, 'also': 1, 'trying': 1, 'pump': 1, 'block': 1, 'bid': 1, 'prevent': 1, 'rising': 1, 'levels': 1, 'sending': 1, 'more': 1, 'oxygen': 1, 'down': 1, 'accident': 1, 'occurred': 1, 'pm': 1, 'saturday': 1, 'shanmushu': 1, 'owned': 1, 'by': 1, 'industry': 1, 'group': 1, 'gongxian': 1, 'county': 1, 'when': 1, 'working': 1, 'total': 1, 'escaped': 1, 'four': 1, 'killed': 1, 'nearly': 1, 'racing': 1, 'against': 1, 'clock': 1, 'due': 1, 'communication': 1, 'interruptions': 1, 'some': 1, 'mining': 1, 'not': 1, 'immediately': 1, 'but': 1, 'through': 1, 'their': 1, 'consistent': 1, 'rescue': 1, 'finally': 1, 'remaining': 1}
上述五步整個代碼完整組織如下:
def mapper(filename):words_list=[]with open(filename,'r') as f:content=f.readlines()for para in content:para=para.split('。')para0=para[0].split('.')for item in para0:words=item.split(' ')for word in words:if word.isalpha()==False:continuewords_list.append(word.lower())return words_listdef reduce(words):words_dict={}for word in words:if word in words_dict:words_dict[word]+=1else:words_dict[word]=1words_dict=sorted(words_dict.items(),key=lambda x:x[1],reverse=True)return dict(words_dict)filename="new.txt" #文件資源位置
wordlist=mapper(filename) #獲取單詞列表
wordCount=reduce(wordlist) #對詞頻進行統計
print(wordCount) #打印結果
上述過程就是單文件詞頻統計,在段落中出現次數最多的是the單詞,這個對于段落內容理解沒有意義,第二多的是miners、in 和to,這個miners應該是有意義的,in和to也是沒有意義的單詞。第三多的是underground,a,are和and,這里underground也是有意義的,后兩個都沒有意義。所以從前面排序結果來看,大概可以知道這個段落里講的是underground miners,也就是地下挖礦的人,和他們有關。如果進一步往下看的話,出現兩次的單詞里基本上都把整個段落的內容概括了。
由此可以將沒有意義,但出現頻率很高的單詞過濾掉,這類詞在NLP自然語言處理里稱之為stop words終止詞,過濾的時候在上述代碼中循環處理添加進列表的時候就可以判斷,如果是終止詞,就不添加,即:
def mapper(filename):words_list=[]list_stopWords=['the','a','and','or','is', 'are','to','in','at','by','of','but']#常見終止詞列表with open(filename,'r') as f:content=f.readlines()for para in content:para=para.split('。')para0=para[0].split('.')for item in para0:words=item.split(' ')for word in words:if word.isalpha()==False:continueif word in list_stopWords: continue #過濾終止詞words_list.append(word.lower()) return words_list
再來看詞頻統計結果:
'miners': 5, 'underground': 4, 'rescuers': 3, 'located': 3, 'trapped': 3, 'coal': 3, 'were': 3, 'have': 2, 'mine': 2, 'sichuan': 2, 'the': 2, 'shaft': 2, 'reach': 2, 'water': 2, 'workers': 2, 'chengdu': 1, 'flooded': 1, 'southwest': 1, 'local': 1, 'authorities': 1, 'said': 1, 'sunday': 1, 'clearing': 1, 'drilling': 1, 'deep': 1, 'hole': 1, 'they': 1, 'also': 1, 'trying': 1, 'pump': 1, 'block': 1, 'bid': 1, 'prevent': 1, 'rising': 1, 'levels': 1, 'sending': 1, 'more': 1, 'oxygen': 1, 'down': 1, 'accident': 1, 'occurred': 1, 'pm': 1, 'saturday': 1, 'shanmushu': 1, 'owned': 1, 'industry':1
這樣再來看,就明白了這個新聞里說的就是,四川挖煤礦的工人被困在井下,救援者進行施救。所以詞頻統計對理解段落含義很有意義。
最后再來一個詞云展示,也就是將上述詞頻統計里出現的詞用一種圖來表示出來,比較直觀。具體實現的時候需要先安裝一個wordcloud詞云第三方庫,matplotlib繪圖庫,然后先設置繪圖背景,然后將詞頻統計結果放置到背景上呈現效果。整個代碼組織如下:
from wordcloud import WordCloud, STOPWORDS #生成詞云、通用詞
import matplotlib.pyplot as plt # 在線顯示def mapper(filename):words_list=[]list_stopWords=['the','a','and','or','is', 'are','to','in','at','by','of','but']#常見終止詞列表with open(filename,'r') as f:content=f.readlines()for para in content:para=para.split('。')para0=para[0].split('.')for item in para0:words=item.split(' ')for word in words:if word.isalpha()==False:continueif word in list_stopWords: continue #過濾終止詞words_list.append(word.lower()) return words_listdef reduce(words):words_dict={}for word in words:if word in words_dict:words_dict[word]+=1else:words_dict[word]=1words_dict=sorted(words_dict.items(),key=lambda x:x[1],reverse=True)return dict(words_dict)filename="new.txt" #文件資源位置
wordlist=mapper(filename) #獲取單詞列表
wordCount=reduce(wordlist) #對詞頻進行統計#準備繪制詞云圖
wc = WordCloud(background_color="white",width=600, height=400, margin=5) #準備一個背景
wc.generate_from_frequencies(wordCount) #根據詞頻統計結果產生詞云
plt.imshow(wc) #顯示出來
plt.axis("off")
plt.show()
執行后效果如下:

詞頻統計思路就是如上分步驟所述,但具體到每篇文章,由于文章的格式、標準等都不一樣,所以還需要具體問題去分析,需要哪些步驟來實現。
上述的詞頻統計是自然語言處理的一個最基本階段,即Tokenization標識化。這部分任務在nltk自然語言處理庫里調用其word_tokenize方法就可以完成。用法為:
import nltk
text="I love China and I was born in Hubei Province"
tokens=nltk.word_tokenize(text)
print(tokens)
另外還可以使用python自帶的collections庫里的counter函數,直接獲得詞頻統計結果。我們上述的代碼實際上就是將這個詞頻統計Counter函數進行了詳細解析。例如:
import collections
text = "I love China and I was born in Hubei Province"
words=collections.Counter(text.split(' '))
print(words)
打印結果為:
Counter({'I': 2, 'love': 1, 'China': 1, 'and': 1, 'was': 1, 'born': 1, 'in': 1, 'Hubei': 1, 'Province': 1})
10行代碼讀懂紅樓夢
中文文章詞頻統計任務相對英文要稍微復雜一些,因為英文單詞與單詞之間天然就用空格空隔開,所以很容易處理;但中文就不一樣了,一段中文話里每個字與每個字之間沒有天然的分割標記,而且還有含義的理解。比如“我看他們在跳舞”這句話,“我”是一個詞,“看”是一個詞,“他們”是一個詞,“在”是一個詞,“跳舞”是一個詞。也就是一個詞可能是一個字,也有可能是多個字構成。這樣在處理的時候就麻煩了。如何確定是一個詞語呢?
這里就需要引入前人所做的工作,把所有詞語都統計好了,形成一個字典庫,名稱叫結巴分詞,直接從cmd窗口使用pip install jieba命令就可以下載到本地:
pip install jieba
為了了解結巴分詞模塊的用法,可以去python安裝目錄下找到site-packages里的jieba文件夾,如下:

使用文本編輯器打開其中的_init_.py文件,查看其源代碼,定位其中常用的cut方法,即分詞方法。
def cut(self, sentence, cut_all=False, HMM=True):'''The main function that segments an entire sentence that containsChinese characters into seperated words.Parameter:- sentence: The str(unicode) to be segmented.- cut_all: Model type. True for full pattern, False for accurate pattern.- HMM: Whether to use the Hidden Markov Model.'''sentence = strdecode(sentence)if cut_all:re_han = re_han_cut_allre_skip = re_skip_cut_allelse:re_han = re_han_defaultre_skip = re_skip_defaultif cut_all:cut_block = self.__cut_allelif HMM:cut_block = self.__cut_DAGelse:cut_block = self.__cut_DAG_NO_HMMblocks = re_han.split(sentence)for blk in blocks:if not blk:continueif re_han.match(blk):for word in cut_block(blk):yield wordelse:tmp = re_skip.split(blk)for x in tmp:if re_skip.match(x):yield xelif not cut_all:for xx in x:yield xxelse:yield xdef cut_for_search(self, sentence, HMM=True):"""Finer segmentation for search engines."""words = self.cut(sentence, HMM=HMM)for w in words:if len(w) > 2:for i in xrange(len(w) - 1):gram2 = w[i:i + 2]if self.FREQ.get(gram2):yield gram2if len(w) > 3:for i in xrange(len(w) - 2):gram3 = w[i:i + 3]if self.FREQ.get(gram3):yield gram3yield w
代碼中有關cut方法的參數說明:cut_all: 如果設置為true則為全模式分詞,如果為false,就為精確分詞,如果使用HMM就使用隱層馬爾科夫模型。具體效果如何,使用代碼來實踐看看:
import jieba
text="我在看他們跳舞,我心里高興得不得了。有時候我也想也許我也可以這樣隨便跳起舞來"
#設置參數cut_all=True,即全模式分詞
word_sep1=list(jieba.cut(text,cut_all=True))
print("全模式分詞效果為:",word_sep1)
#設置參數cut_all=False,即精確模式分詞
word_sep2=list(jieba.cut(text,cut_all=False))
print("精確模式分詞效果為:",word_sep2)
運行后結果返回列表結果如下:
全模式分詞效果為 :['我', '在', '看', '他們', '跳舞', '', '', '我心', '心里', '高興', '得', '不得', '不得了', '', '', '有時', '有時候', '時候', '我', '也', '想', '也許', '我', '也', '可以', '這樣', '隨便', '跳起', '起舞', '來']
精確模式分詞效果為: ['我', '在', '看', '他們', '跳舞', ',', '我', '心里', '高興', '得', '不得了', '。', '有時候', '我', '也', '想', '也許', '我', '也', '可以', '這樣', '隨便', '跳', '起舞', '來']
對比而言,精確分詞更為準確,全模式分詞還有詞的聯想效果。
有了這個非常好用的分詞方法,那對于大段中文文章的詞頻統計過程就與上述的英文文章類似了。也是分:首先分詞處理,獲得詞語的列表,然后進行map操作,構建字典,每個詞語出現過,就給次數1,最后再進行reduce操作,將相同詞語出現的次數相加,獲得出現頻率結果。
下面我對紅樓夢相關章節進行分詞處理。這個難度還是比較大的,主要是紅樓夢屬于古典小說,有許多文言文表達,而不是白話文。所以分詞效果并不是非常準確。不過對于練習已經足夠有吸引力了。
第一步,從網上下載紅樓夢小說txt文本,保存成一個文本文件。

第二步,開始在python中編寫代碼,首先讀取這個文本文件,獲得紅樓夢中文文本。并使用分詞結果構建一個字典,字典內容為<詞語,次數1>。由于全部回數的文本很長,在練習時可以選擇其中一部分來進行測試。代碼參考如下:
import jieba#定義函數mapper,用于構建字典
def mapper(file):word_sep=[]word_map={}punctuation=['?','!',',','。',';',':','“','”','n','u3000','(',')']stopwords=["之","的","一","他","她","我","我們","可以","你","里","去","來","那","在","上","下"]with open(file,'r') as f:text=f.readlines() for i in range(50):words=list(jieba.cut(text[i],cut_all=False))for word in words:if word in punctuation:continue #去除標點符號if word in stopwords:continue #去除終止詞word_sep.append(word)for word in word_sep:word_map[word]=1return word_map#主函數
if __name__=="__main__":file='紅樓夢.txt'print(mapper(file))
在這一步中使用了標點符號和終止詞,當分詞后的字符是標點符號和終止詞時,就不加入字典。不過這里終止詞是我自己構建的列表,內容相對較少。這塊可以自行增加。
運行上述代碼后就可以獲得如下結果(這里受限篇幅僅顯示部分結果):
{'甄士隱': 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, '塊': 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, '后': 1, '便': 1, '說': 1, '到': 1, '紅塵': 1, '中': 1, '榮華富貴': 1, '聽': 1, '不覺': 1, '打動': 1, '凡心': 1, '也': 1, '想要': 1, '人間': 1, '享一享': 1, '這': 1, '但': 1, '自恨': 1, '粗蠢': 1, '不得已': 1}
第三步,有了上述的詞語字典后,就可以進行詞頻統計了。此時增加一個reducer函數,專門用于處理統計。
def reducer(word_dict):word_freq={}for key in word_dict: if key in word_freq:word_freq[key]+=1 else:word_freq[key]=1word_freq=sorted(word_freq.items(),key=lambda x:x[1],reverse=True)return dict(word_freq)
輸出詞頻統計結果,如下示例:
文字出現的頻率為: {'道': 8, '弟子': 6, '一塊': 4, '便': 4, '到': 4, '紅塵': 4, '聽': 4, '不知': 4, '補天': 3, '說': 3, '不能': 3, '卻': 3, '如此': 3, '自然': 3, '則': 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, '甄士隱': 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, '石俱得': 1, '獨': 1, '自己': 1, '不堪': 1, '入選': 1, '遂': 1, '自怨': 1, '自嘆': 1, '日夜': 1, '悲號': 1, '慚愧':1}
細看統計結果,里面出現許多一個字的詞語,一般情況下中文單個文字表達的意思還是很有限的,多以詞組的形式來表示含義。因此需要將單個文字從詞語統計中剔除。
另外為了統計結果更直觀,代碼中也增加詞云庫,使用詞云來顯示詞頻統計的結果。詞云顯示的時候由于是漢字,所以需要增加漢字字庫。即給定font_path,代碼中直接調用windows系統的字體庫中的宋體。
wc = WordCloud(background_color="white",width=600, height=400, margin=5,font_path="C:/Windows/Fonts/simsun.ttc")
將上述三步合起來,代碼整體組織如下:
import jieba
from wordcloud import WordCloud, STOPWORDS #生成詞云、通用詞
import matplotlib.pyplot as plt # 在線顯示#定義函數mapper,用于構建詞語列表
def mapper(file):word_sep=[]word_map={}punctuation=['?','!',',','。',';',':','“','”','’','‘','n','u3000','(',')','、']stopwords=["之","的","一","他","她","我","我們","可以","你","里","去","來","那","在","上","下","了","又","是","這","著","也","人",'不','有']with open(file,'r') as f:text=f.readlines() for i in range(50): #取前50列表測試words=list(jieba.cut(text[i],cut_all=False)) #結巴分詞for word in words:if word in punctuation:continue #去除標點符號if word in stopwords:continue #去除終止詞if len(word)<2:continue #去除單個字word_sep.append(word) #將分好的詞語添加到空列表return word_sep#定義函數,用于詞頻統計
def reducer(word_dict):word_freq={}for key in word_dict: if key in word_freq:word_freq[key]+=1 else:word_freq[key]=1word_freq=sorted(word_freq.items(),key=lambda x:x[1],reverse=True)return dict(word_freq) #主函數
if __name__=="__main__":file='紅樓夢.txt'word_dict=mapper(file) word_freq=reducer(word_dict)#print("文字出現的頻率為:",word_freq)wc = WordCloud(background_color="white",width=600, height=400, margin=5,font_path="C:/Windows/Fonts/simsun.ttc")wc.generate_from_frequencies(word_freq)plt.imshow(wc)plt.axis("off")plt.show()
運行程序,獲得如下詞云圖:

這個圖云是對前幾回的文本進行的統計,所以里面出現了雨村、士隱、道人、封肅、丫鬟、那僧、世人、弟子等較高出現的詞語,基本上能夠概括前幾回的主要人物和事情。
如果把全部紅樓夢的文字都拿進來,最后的詞云圖如下(效果與選擇終止詞有關):

再來對比整個紅樓夢,這張詞云圖上出現的就是寶玉、賈母、王夫人、鳳姐、姑娘、奶奶、黛玉、襲人、寶釵等紅樓夢核心人物。