1. 程式人生 > >如何繪製高大上的詞雲圖?

如何繪製高大上的詞雲圖?

640?wx_fmt=gif

640?wx_fmt=jpeg

作者 | 蘇克

責編 | 郭芮

當我們手中有一篇文件,比如書籍、小說、電影劇本,若想快速瞭解其主要內容是什麼,則可以採用繪製 WordCloud 詞雲圖,顯示主要的關鍵詞(高頻詞)這種方式,非常方便。

本文將介紹常見的英文和中文文字的詞雲圖繪製,以及 Frequency 頻詞雲圖。接下來,將詳細說明各種形式的詞雲圖繪製步驟。


640?wx_fmt=png

英文詞雲


我們先繪製英文文字的詞雲圖,因為它相對簡單一些。這裡以《海上鋼琴師》這部電影的劇本為例。

首先,準備好電影劇本的文字檔案(如下圖):

640?wx_fmt=png

接下來,我們繪製一個最簡單的矩形詞雲圖,程式碼如下:

 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() 方法生成該電影劇本的詞雲,最後顯示和儲存詞雲圖。十幾行程式碼就可以生成最簡單的詞雲圖:

640?wx_fmt=png

通過上面的詞雲圖,你可能會發現有幾點問題:

  • 可不可以更換背景,比如白色?

  • 詞雲圖能不能換成其他形狀或者圖片?

  • 詞雲中最顯眼的詞彙 「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


結果如下:

640?wx_fmt=png

接下來,我們提升一點難度,通過設定 StopWords 去掉沒有實際意義的「ONE」字元,然後將詞雲圖繪製在我們自定義的一張圖片上。

640?wx_fmt=jpeg

程式碼實現如下:

 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() 則是根據圖片色彩繪製詞雲文字顏色。最終的詞雲繪製效果如下:

640?wx_fmt=png

現在,我們還是看到了顯眼的「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 中,這樣就可以遮蔽該詞從而不在詞雲中顯示。

需要注意的是,這種手動新增停止詞的方法適用於詞數量比較少的情況

640?wx_fmt=png

另外,我們還可以將詞雲圖顏色顯示為黑白漸變色,也只需修改幾行程式碼即可:

1def grey_color_func(word, font_size, position, orientation, random_state=None,
2                    **kwargs)
:

3        return "hsl(0, 0%%, %d%%)" % random.randint(50100)
4        # 隨機設定hsl色值
5wc.recolor(color_func=grey_color_func) 

效果如下:

640?wx_fmt=png

以上,就是英文詞雲圖繪製的幾種方法,下面我們介紹中文詞雲圖的繪製。


640?wx_fmt=png

中文詞雲


相比於英文詞雲,中文在繪製詞雲圖前,需要先切割詞彙,這裡推薦使用 jieba 包來切割分詞。因為它可以說是最好的中文分詞包了,GitHub 上擁有 160 K 的 Star 數。安裝好 jieba 包後,我們就可以對文字進行分詞然後生成詞雲。

這裡,選取吳軍老師的著作《浪潮之巔》作為中文文字的案例,仍然採用圖片形式的詞雲圖。素材準備好後,接下來就可以開始中文詞雲圖繪製。

640?wx_fmt=png

首先,需要讀取文字檔案,相比於英文,這裡要新增文字編碼格式,否則會報錯,新增幾行程式碼就可以識別文字的編碼格式:

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 新增之前:

640?wx_fmt=png

stopwords 新增之後:

640?wx_fmt=png

可以看到,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,結構形式如下:

640?wx_fmt=png

遍歷該 stopwords 詞庫,刪除停止詞獲得新的文字,然後利用第一種方法繪製詞雲圖即可。

首先輸出一下文字詞頻最高的部分詞彙,可以看到常見的停止詞已經沒有了:

1[('公司'1462), ('美國'366), ('IBM'322), ('微軟'320), ('市場'287), ('投資'263), ('世界'236), ('矽谷'235), ('技術'234), ('發展'225), ('計算機'218), ('摩托羅拉'203)...]

詞雲圖最終效果如下:

640?wx_fmt=png


640?wx_fmt=png

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