如何繪製高大上的詞雲圖?
作者 | 蘇克
責編 | 郭芮
當我們手中有一篇文件,比如書籍、小說、電影劇本,若想快速瞭解其主要內容是什麼,則可以採用繪製 WordCloud 詞雲圖,顯示主要的關鍵詞(高頻詞)這種方式,非常方便。
本文將介紹常見的英文和中文文字的詞雲圖繪製,以及 Frequency 頻詞雲圖。接下來,將詳細說明各種形式的詞雲圖繪製步驟。
英文詞雲
我們先繪製英文文字的詞雲圖,因為它相對簡單一些。這裡以《海上鋼琴師》這部電影的劇本為例。
首先,準備好電影劇本的文字檔案(如下圖):
接下來,我們繪製一個最簡單的矩形詞雲圖,程式碼如下:
1import os
2from os import path
3from wordcloud import WordCloud
4from matplotlib import pyplot as plt
5# 獲取當前檔案路徑
6d = path.dirname(__file__) if "__file__" in locals() else os.getcwd()
7# 獲取文字text
8text = open(path.join(d,'legend1900.txt')).read()
9# 生成詞雲
10wc = WordCloud(scale=2,max_font_size = 100)
11wc.generate_from_text(text)
12# 顯示影象
13plt.imshow(wc,interpolation='bilinear')
14plt.axis('off')
15plt.tight_layout()
16#儲存影象
17wc.to_file('1900_basic.png')
18# or
19# plt.savefig('1900_basic.png',dpi=200)
20plt.show()
這裡,通過 open() 方法讀取文字檔案,然後在 WordCloud 方法中設定了詞雲引數,再利用 generate_from_text() 方法生成該電影劇本的詞雲,最後顯示和儲存詞雲圖。十幾行程式碼就可以生成最簡單的詞雲圖:
通過上面的詞雲圖,你可能會發現有幾點問題:
可不可以更換背景,比如白色?
詞雲圖能不能換成其他形狀或者圖片?
詞雲中最顯眼的詞彙 「ONE」,並沒有實際含義,能不能去掉?
以上這些都是可以更改的,如果你想實現以上想法,那麼需要先了解一下 WordCloud 的API 引數及它的一些方法。
這裡,我們列出它的各項引數,並註釋重要的幾項:
1wordcloud.WordCloud(
2 font_path=None, # 字型路徑,英文不用設定路徑,中文需要,否則無法正確顯示圖形
3 width=400, # 預設寬度
4 height=200, # 預設高度
5 margin=2, # 邊緣
6 ranks_only=None,
7 prefer_horizontal=0.9,
8 mask=None, # 背景圖形,如果想根據圖片繪製,則需要設定
9 scale=1,
10 color_func=None,
11 max_words=200, # 最多顯示的詞彙量
12 min_font_size=4, # 最小字號
13 stopwords=None, # 停止詞設定,修正詞雲圖時需要設定
14 random_state=None,
15 background_color='black', # 背景顏色設定,可以為具體顏色,比如white或者16進位制數值
16 max_font_size=None, # 最大字號
17 font_step=1,
18 mode='RGB',
19 relative_scaling='auto',
20 regexp=None,
21 collocations=True,
22 colormap='viridis', # matplotlib 色圖,可更改名稱進而更改整體風格
23 normalize_plurals=True,
24 contour_width=0,
25 contour_color='black',
26 repeat=False)
關於更詳細的用法,你需要到官網瞭解。
瞭解了各項引數後,我們就可以自定義想要的詞雲圖了。比如更換一下背景顏色和整體風格,就可以通過修改以下幾項引數實現:
1wc = WordCloud(
2 scale=2,# 縮放2倍
3 max_font_size = 100,
4 background_color = '#383838',# 灰色
5 colormap = 'Blues')
6# colormap名稱 https://matplotlib.org/examples/color/colormaps_reference.html
結果如下:
接下來,我們提升一點難度,通過設定 StopWords 去掉沒有實際意義的「ONE」字元,然後將詞雲圖繪製在我們自定義的一張圖片上。
程式碼實現如下:
1import os
2from os import path
3import numpy as np
4from wordcloud import WordCloud,STOPWORDS,ImageColorGenerator
5from PIL import Image
6from matplotlib import pyplot as plt
7from scipy.misc import imread
8import random
9
10def wc_english():
11 # 獲取當前檔案路徑
12 d = path.dirname(__file__) if "__file__" in locals() else os.getcwd()
13 # 獲取文字text
14 text = open(path.join(d,'legend1900.txt')).read()
15 # 讀取背景圖片
16 background_Image = np.array(Image.open(path.join(d, "mask1900.jpg")))
17 # or
18 # background_Image = imread(path.join(d, "mask1900.jpg"))
19 # 提取背景圖片顏色
20 img_colors = ImageColorGenerator(background_Image)
21 # 設定英文停止詞
22 stopwords = set(STOPWORDS)
23 wc = WordCloud(
24 margin = 2, # 設定頁面邊緣
25 mask = background_Image,
26 scale = 2,
27 max_words = 200, # 最多詞個數
28 min_font_size = 4, # 最小字型大小
29 stopwords = stopwords,
30 random_state = 42,
31 background_color = 'white', # 背景顏色
32 max_font_size = 150, # 最大字型大小
33 )
34 # 生成詞雲
35 wc.generate_from_text(text)
36 # 等價於
37 # wc.generate(text)
38 # 根據圖片色設定背景色
39 wc.recolor(color_func=img_colors)
40 #儲存影象
41 wc.to_file('1900pro1.png')
42 # 顯示影象
43 plt.imshow(wc,interpolation='bilinear')
44 plt.axis('off')
45 plt.tight_layout()
46 plt.show()
這裡,首先通過 open() 方法讀取文字檔案,Image.open() 方法讀取了背景圖片,np.array 方法將圖片轉換為矩陣。
接著設定了詞雲自帶的英文 StopWords 停止詞,用來分割篩除文字中不需要的詞彙,比如:a、an、the 這些。
然後,在 WordCloud 方法中,設定詞雲的具體引數。generate_from_text() 方法生成該詞雲,recolor() 則是根據圖片色彩繪製詞雲文字顏色。最終的詞雲繪製效果如下:
現在,我們還是看到了顯眼的「ONE」,下面我們將它去除掉,方法也很簡單,幾行程式碼就可以實現:
1# 獲取文字詞排序,可調整 stopwords
2process_word = WordCloud.process_text(wc,text)
3sort = sorted(process_word.items(),key=lambda e:e[1],reverse=True)
4print(sort[:50]) # 獲取文字詞頻最高的前50個詞
5# 結果
6[('one', 60), ('ship', 47), ('Nineteen Hundred', 43), ('know', 38), ('music', 36), ...]
7
8stopwords = set(STOPWORDS)
9stopwords.add('one')
首先,我們對文字詞頻進行排序,可以看到 「ONE」詞頻最高,就將它新增進 stopwords 中,這樣就可以遮蔽該詞從而不在詞雲中顯示。
需要注意的是,這種手動新增停止詞的方法適用於詞數量比較少的情況。
另外,我們還可以將詞雲圖顏色顯示為黑白漸變色,也只需修改幾行程式碼即可:
1def grey_color_func(word, font_size, position, orientation, random_state=None,
2 **kwargs):
3 return "hsl(0, 0%%, %d%%)" % random.randint(50, 100)
4 # 隨機設定hsl色值
5wc.recolor(color_func=grey_color_func)
效果如下:
以上,就是英文詞雲圖繪製的幾種方法,下面我們介紹中文詞雲圖的繪製。
中文詞雲
相比於英文詞雲,中文在繪製詞雲圖前,需要先切割詞彙,這裡推薦使用 jieba 包來切割分詞。因為它可以說是最好的中文分詞包了,GitHub 上擁有 160 K 的 Star 數。安裝好 jieba 包後,我們就可以對文字進行分詞然後生成詞雲。
這裡,選取吳軍老師的著作《浪潮之巔》作為中文文字的案例,仍然採用圖片形式的詞雲圖。素材準備好後,接下來就可以開始中文詞雲圖繪製。
首先,需要讀取文字檔案,相比於英文,這裡要新增文字編碼格式,否則會報錯,新增幾行程式碼就可以識別文字的編碼格式:
1text = open(path.join(d,'langchao.txt'),'rb').read()
2text_charInfo = chardet.detect(text)
3print(text_charInfo)
4# 結果
5{'encoding': 'UTF-8-SIG', 'confidence': 1.0, 'language': ''}
6text = open(path.join(d,r'langchao.txt'),encoding='UTF-8-SIG').read()
接著,對文字進行分詞。jieba 分詞有 3 種方式:精確模式、全模式和搜尋引擎模式,它們之間的差別,可以用一個例子來體現。
比如,有這樣的一句話:「"我來到北京清華大學"」,用 3 種模式進行分詞,結果分別如下:
全模式:我/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學
精確模式:我/ 來到/ 北京/ 清華大學
搜尋引擎模式:我/ 來/ 來到/ 北京/ 清華/ 大學/ 清華大學/
根據結果可知,我們應該選擇「精確模式」來分詞。關於 jieba 包的詳細用法,可以參考 GitHub 倉庫連結:https://github.com/fxsjy/jieba。
分詞完成後,還需要設定 stopwords 停止詞,由於 WordCloud 沒有中文停止詞,所以需要自行構造。這裡可以採取兩種方式來構造:
通過 stopwords.update() 方法手動新增;
根據已有 stopwords 詞庫遍歷文字篩除停止詞。
2.1. stopwords.update() 手動新增
這種方法和前面的英文停止詞構造的方法是一樣的,目的是在詞雲圖中不顯示 stopwords 就行了 ,即先不設定 stopwords,而是先對文字詞頻進行排序,然後將不需要的詞語新增為 stopwords 即可,程式碼實現如下:
1# 獲取文字詞排序,可調整 stopwords
2process_word = WordCloud.process_text(wc,text)
3sort = sorted(process_word.items(),key=lambda e:e[1],reverse=True)
4print(sort[:50]) # # 獲取文字詞頻最高的前50個詞
5
6[('公司', 1273), ('但是', 769), ('IBM', 668), ('一個', 616), ('Google', 429), ('自己', 396), ('因此', 363), ('微軟', 358), ('美國', 344), ('沒有', 334)...]
可以看到,我們先輸出文字詞頻最高的一些詞彙後,發現:「但是」、「一個」、「因此」這些詞都是不需要顯示在詞雲圖中的。因此,可以把這些詞用列表的形式新增到 stopwords 中,然後再次繪製詞雲圖就能得出比較理想的效果,完整程式碼如下:
1import chardet
2import jieba
3text+=' '.join(jieba.cut(text,cut_all=False)) # cut_all=False 表示採用精確模式
4# 設定中文字型
5font_path = 'C:WindowsFontsSourceHanSansCN-Regular.otf' # 思源黑體
6# 讀取背景圖片
7background_Image = np.array(Image.open(path.join(d, "wave.png")))
8# 提取背景圖片顏色
9img_colors = ImageColorGenerator(background_Image)
10# 設定中文停止詞
11stopwords = set('')
12stopwords.update(['但是','一個','自己','因此','沒有','很多','可以','這個','雖然','因為','這樣','已經','現在','一些','比如','不是','當然','可能','如果','就是','同時','比如','這些','必須','由於','而且','並且','他們'])
13
14wc = WordCloud(
15 font_path = font_path, # 中文需設定路徑
16 margin = 2, # 頁面邊緣
17 mask = background_Image,
18 scale = 2,
19 max_words = 200, # 最多詞個數
20 min_font_size = 4, #
21 stopwords = stopwords,
22 random_state = 42,
23 background_color = 'white', # 背景顏色
24 # background_color = '#C3481A', # 背景顏色
25 max_font_size = 100,
26 )
27wc.generate(text)
28# 獲取文字詞排序,可調整 stopwords
29process_word = WordCloud.process_text(wc,text)
30sort = sorted(process_word.items(),key=lambda e:e[1],reverse=True)
31print(sort[:50]) # 獲取文字詞頻最高的前50個詞
32# 設定為背景色,若不想要背景圖片顏色,就註釋掉
33wc.recolor(color_func=img_colors)
34# 儲存影象
35wc.to_file('浪潮之巔basic.png')
36# 顯示影象
37plt.imshow(wc,interpolation='bilinear')
38plt.axis('off')
39plt.tight_layout()
40plt.show()
stopwords 新增之前:
stopwords 新增之後:
可以看到,stopwords.update() 這種方法需要手動去新增,比較麻煩一些,而且如果 stopwords 過多的話,新增就比較費時了。下面介紹第 2 種自動去除 stopwords 的方法。
2.2. stopwords 庫自動遍歷刪除
這種方法的思路也比較簡單,主要分為 2 個步驟:
利用已有的中文 stopwords 詞庫,對原文字進行分詞後,遍歷詞庫去除停止詞,然後生成新的文字檔案。
根據新的檔案繪製詞雲圖,便不會再出現 stopwords,如果發現 stopwords 詞庫不全可以進行補充,然後再次生成詞雲圖即可。
程式碼實現如下:
1# 對原文字分詞
2def cut_words():
3 # 獲取當前檔案路徑
4 d = path.dirname(__file__) if "__file__" in locals() else os.getcwd()
5 text = open(path.join(d,r'langchao.txt'),encoding='UTF-8-SIG').read()
6 text = jieba.cut(text,cut_all=False)
7 content = ''
8 for i in text:
9 content += i
10 content += " "
11 return content
12
13# 載入stopwords
14def load_stopwords():
15 filepath = path.join(d,r'stopwords_cn.txt')
16 stopwords = [line.strip() for line in open(filepath,encoding='utf-8').readlines()]
17 # print(stopwords) # ok
18 return stopwords
19
20# 去除原文stopwords,並生成新的文字
21def move_stopwwords(content,stopwords):
22 content_after = ''
23 for word in content:
24 if word not in stopwords:
25 if word != ' 'and'
':
26 content_after += word
27
28 content_after = content_after.replace(" ", " ").replace(" ", " ")
29 # print(content_after)
30 # 寫入去停止詞後生成的新文字
31 with open('langchao2.txt','w',encoding='UTF-8-SIG') as f:
32 f.write(content_after)
網上有很多中文 stopwords 詞庫資料,這裡選取了一套包含近 2000 個詞彙和標點符號的詞庫:stopwords_cn.txt,結構形式如下:
遍歷該 stopwords 詞庫,刪除停止詞獲得新的文字,然後利用第一種方法繪製詞雲圖即可。
首先輸出一下文字詞頻最高的部分詞彙,可以看到常見的停止詞已經沒有了:
1[('公司', 1462), ('美國', 366), ('IBM', 322), ('微軟', 320), ('市場', 287), ('投資', 263), ('世界', 236), ('矽谷', 235), ('技術', 234), ('發展', 225), ('計算機', 218), ('摩托羅拉', 203)...]
詞雲圖最終效果如下:
Frenquency 詞雲圖
上面兩種中英文詞雲圖都是通過文字繪製的,而除了直接讀入文字生成詞雲以外,比較常見的還有通過「詞頻」繪製詞雲圖。這種詞雲圖,則可以使用 DataFrame 或者字典格式來繪製。
下面,以一篇「近十年世界大學排名 TOP500 強」的資料(http://www.shanghairanking.com/ARWU2018.html)為例,介紹如何繪製詞頻詞雲圖。該份資料大小為 5001行 x 6 列,我們想根據各國 TOP 500 強大學的數量,來視覺化地展示各國之間的大學數量差異。
1world_rank university score quantity year country
21 哈佛大學 100 500 2009 USA
32 斯坦福大學 73.1 499 2009 USA
43 加州大學-伯克利 71 498 2009 USA
54 劍橋大學 70.2 497 2009 UK
65 &nbs