banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban
tg_channel

pkuseg分詞統計並生成詞雲

本文動力來源

1.https://mp.weixin.qq.com/s/Pim3yYbDswob8CkSGIZ49g 2.https://mp.weixin.qq.com/s/XJ-CP0wiXaJciRgEWKzATg

pkuseg 是啥?#

pkuseg 是由北京大學語言計算與機器學習研究組研製推出的一套全新的中文分詞工具包。

pkuseg 具有如下幾個特點:

1. 高分詞準確率。相比於其他的分詞工具包,我們的工具包在不同領域的數據上都大幅提高了分詞的準確度。根據我們的測試結果,pkuseg 分別在示例數據集(MSRA 和 CTB8)上降低了 79.33% 和 63.67% 的分詞錯誤率。 2. 多領域分詞。我們訓練了多種不同領域的分詞模型。根據待分詞的領域特點,用戶可以自由地選擇不同的模型。 3. 支持用戶自訓練模型。支持用戶使用全新的標註數據進行訓練。

Github 地址:https://github.com/lancopku/PKUSeg-python

安裝:pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pkuseg

我的 Anaconda 環境實測在第一次運行之後會報 numpy 這個庫的錯,可以用 pip3 uninstall numpy 之後重新 pip3 install numpy

更新:pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -U pkuseg

詞雲是啥?#

wordcloud 詞雲的安裝需要 Microsoft Visual C++ 14.0 運行庫的支持

不建議單獨去安裝一個包,建議直接安裝常用運行庫,網上很多,這裡給一個 RPK 睿派克論壇上打包的微軟常用運行庫合集:https://www.lanzous.com/b143614/ (RPK 論壇的人看到請記得打廣告費謝謝)

另外直接pip3 install wordcloud的話是從國外的鏡像下載,速度較慢,建議下載 whl 文件下來自行安裝:點我去下載

image

搜索 wordcloud,下載自己對應的版本,然後 pip3 install wordcloud‑1.5.0‑cp37‑cp37m‑win_amd64.whl 即可

詞雲代碼實現#

本文所有代碼及示例文件都打包了,下載地址在文末

我們先來一個簡單的純英文文本的詞雲生成例子

詞雲demo.py

from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt

with open("Alice.txt") as f:
    wordcloud = WordCloud(
        background_color="white",  # 背景顏色為白色
        width=1000,  # 畫布寬度為1000,設置背景圖片之後設置寬高不起作用
        height=600,  # 畫布高度為600
        margin=2  # 邊框設置為2
    ).generate_from_text(f.read())  # 根據文本內容生成詞雲
# wordcloud.recolor(color_func=ImageColorGenerator(bak_pic)) # 設置詞雲顏色為圖片的顏色
# 將生成的詞雲圖保存在當前用戶桌面,如重複生成會將之前的覆蓋
plt.imshow(wordcloud)  # imshow()作用:將一個image顯示在二維坐標軸上
plt.axis("off")  # 不顯示坐標軸
plt.show()  # 將image顯示出來

效果展示:

image

可以看到我們就已經生成了一個英文單詞文本的詞雲圖,但有如下幾個新的需求:

  • 需要自定義背景圖片,最好能使用背景圖片的顏色
  • 需要對中文生成圖雲
  • 看起來圖片不咋清晰,需要清晰一點
  • 需要保存每次生成的圖雲

第二版代碼如下:

wordcloud_gen.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
@author: soapffz
@fucntion: 使用wordcloud讀取單詞文本並生成詞雲打印出來
@time: 2019-01-31
'''

from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
from os import path

# 導出的詞雲圖片的位置設置為當前用戶的桌面
pic_path = path.join(path.expanduser("~")+"\\"+"Desktop"+"\\")
bak_pic = plt.imread("totoro.jpg")
with open("Alice.txt") as f:
    wordcloud = WordCloud(
        font_path="C:\Windows\Fonts\simsun.ttc",  # 設置字體,這是win10的,win7字體後綴為ttf請自行查找
        mask=bak_pic,  # 設置背景圖片,設置背景圖片之後設置寬高不起作用
        background_color="white",  # 背景顏色為白色
        prefer_horizontal=0.7,  # 詞語水平方向排版出現的頻率設置為0.7(默認為0.9)
        scale=15,  # 畫布放大倍數設置為15(默認為1)
        margin=2  # 邊框設置為2
    ).generate_from_text(f.read())  # 根據文本內容生成詞雲
wordcloud.recolor(color_func=ImageColorGenerator(bak_pic))  # 設置詞雲顏色為圖片的顏色
# 將生成的詞雲圖保存在當前用戶桌面,如重複生成會將之前的覆蓋
wordcloud.to_file(path.join(pic_path+"wordcloud.jpg"))
plt.imshow(wordcloud)  # imshow()作用:將一個image顯示在二維坐標軸上
plt.axis("off")  # 不顯示坐標軸
plt.show()  # 將image顯示出來

此處有坑 注意,上面的代碼中都是直接打開文本,沒有注明編碼的,然後我看了一下我沒報錯的文本是 ANSI 編碼的,也就是 win10 默認的 txt 的格式,然後我在打開一個 utf-8 編碼的文本時候報了這個錯誤:

UnicodeDecodeError: 'gbk' codec can't decode byte 0x9d in position 809: illegal multibyte sequence

但是在with open("content.txt")時指定編碼:with open("content.txt",encoding="utf-8")則可以打開 utf-8 編碼的 txt,但是由於 經常創建 txt,建議這裡都使用 ANSI 編碼,就可以不用修改代碼了,如果報錯的建議將 txt 另存為 ANSI 編碼覆蓋掉就行:

image

在圖中右下角修改編碼,並確認覆蓋就行了,另外,剛打開時的右下角顯示的編碼即是此文本當前的編碼

有這麼幾個參數會比較影響詞雲的效果:

  • font_path 文本的字體,可以去網上自行下載,也可以使用 C:\Windows\Fonts\ 中的自帶字體,注意 win10 字體後綴為 ttc,而 win7 的為 ttf
  • background_color:背景色,默認黑。如果遮罩圖像顏色過淺、背景設置白色,可能導致字看起來 “不清晰”。對一個淺色遮罩圖分別用白、黑兩種背景色後發現,黑色背景的強烈對比之下會有若干很淺也很小的詞浮現出來,而之前因背景色、字色過於相近而幾乎無法用肉眼看出這些詞。
  • prefer_horizontal 這個參數是用來設置詞語水平方向排版出現的頻率的,設置的越小則在水平方向的詞越多,在豎直方向上的詞越少,我個人覺得默認的 0.9 太大了,建議 0.7,比較有逼格 *max_font_size:最大字號。圖的生成會依據最大字號等因素去自動判斷詞的佈局。經測試,哪怕同一個圖像,只要圖本身尺寸不一樣(比如我把一個 300×300 的圖拉大到 600×600 再去當遮罩),那麼同樣的字號也是會有不同的效果。原理想想也很自然,字號決定了字的尺寸,而圖的尺寸變了以後,最大字相對於圖的尺寸比例自然就變了。所以,需要根據期望顯示的效果,去調整最大字號參數值。
  • scale 這個是畫布放大倍數,數值越大,比較小的詞就顯示得越清楚,所以建議把這個設置為十幾左右,默認的 1 會不太清晰,設置得越大耗費時間越長
  • relative_scaling:表示詞頻和雲詞圖中字大小的關係參數,默認 0.5。為 0 時,表示只考慮詞排序,而不考慮詞頻數;為 1 時,表示兩倍詞頻的詞也會用兩倍字號顯示。
  • 另外說一下random_state 這個參數,這個是用來設置樣式數量的,但是我試了一下,設置了這個參數之後不僅沒有增加樣式,連圖片的隨機樣式都沒有了,這個大家可以自己試一下,可能是我這的問題

其他參數可以自己添加一下試一下:

mask : nd-array or None (default=None) #如果參數為空,則使用二維遮罩繪製詞雲。如果 mask 非空,設置的寬高值將被忽略,遮罩形狀被 mask 取代。除全白(#FFFFFF)的部分將不會繪製,其餘部分會用於繪製詞雲。如:bg_pic = imread('讀取一張圖片.png'),背景圖片的畫布一定要設置為白色(#FFFFFF),然後顯示的形狀為不是白色的其他顏色。可以用ps工具將自己要顯示的形狀複製到一個純白色的畫布上再保存,就ok了。
font_path : string  #字體路徑,需要展現什麼字體就把該字體路徑+後綴名寫上,如:font_path = '黑體.ttf'
background_color : color value (default=”black”) #背景顏色,如background_color='white',背景顏色為白色
width : int (default=400) #輸出的畫布寬度,默認為400像素
height : int (default=200) #輸出的畫布高度,默認為200像素
prefer_horizontal : float (default=0.90) #詞語水平方向排版出現的頻率,默認 0.9 (所以詞語豎直方向排版出現頻率為 0.1 )
min_font_size : int (default=4) #顯示的最小的字體大小
max_font_size : int or None (default=None) #顯示的最大的字體大小
scale : float (default=1) #按照比例進行放大畫布,默認值1,如設置為1.5,則長和寬都是原來畫布的1.5倍,值越大,圖像密度越大越清晰
font_step : int (default=1) #字體步長,如果步長大於1,會加快運算但是可能導致結果出現較大的誤差
max_words : number (default=200) #要顯示的詞的最大個數
mode : string (default=”RGB”) #當參數為“RGBA”並且background_color不為空時,背景為透明
relative_scaling : float (default=.5) #詞頻和字體大小的關聯性
stopwords : set of strings or None #設置需要屏蔽的詞,如果為空,則使用內置的STOPWORDS
color_func : callable, default=None #生成新顏色的函數,如果為空,則使用 self.color_func
regexp : string or None (optional) #使用正則表達式分隔輸入的文本
collocations : bool, default=True #是否包括兩個詞的搭配
colormap : string or matplotlib colormap, default=”viridis” #給每個單詞隨機分配顏色,若指定color_func,則忽略該方法
random_state : int or None  #為每個單詞返回一個PIL顏色

fit_words(frequencies)  #根據詞頻生成詞雲
generate(text)  #根據文本生成詞雲
generate_from_frequencies(frequencies[, ...])   #根據詞頻生成詞雲
generate_from_text(text)    #根據文本生成詞雲
process_text(text)  #將長文本分詞並去除屏蔽詞(此處指英語,中文分詞還是需要自己用別的庫先行實現,使用上面的 fit_words(frequencies) )
recolor([random_state, color_func, colormap])   #對現有輸出重新著色。重新上色會比重新生成整個詞雲快很多
to_array()  #轉化為 numpy array
to_file(filename)   #輸出到文件

我的背景圖片是龍貓:

image

設置與背景圖片顏色相同,效果如下:

image

emmmm,可以看到圖中的話都是一大段一大段的,這就需要靠分詞來解決了

並且感覺不太好看,把設置詞雲顏色為圖片的顏色這一行註釋掉,再看看:

image

emmm,看来背景圖片的選取很重要,最好是層次分明,且含有大片白色 ,且顏色要好看的,詞雲先到這裡,我們來看分詞

pkuseg 分詞代碼實現#

使用默認模型及默認詞典分詞

import pkuseg
seg = pkuseg.pkuseg()    # 以默認配置加載模型
text = seg.cut('我愛北京天安門')   # 進行分詞
print(text)

輸出結果:['我', '愛', '北京', '天安門']

其他的使用方式詳見 GitHub 地址,我們用張小龍的 3 萬字演講做統計,步驟如下:

1. 將演講內容下載下來,保存到一個 txt 文件中,然後將內容加載到內存 2. 用 pkuseg 對內容進行分詞處理,並統計出現頻率最高的前 20 個詞語是哪些

import pkuseg
from collections import Counter
import pprint

content = []
with open("yanjiang.txt", encoding="utf-8") as f:
    content = f.read()

seg = pkuseg.pkuseg()
text = seg.cut(content)
counter = Counter(text)
pprint.pprint(counter.most_common(20))

輸出結果:

[(',', 1441),
 ('的', 1278),
 ('。', 754),
 ('一個', 465),
 ('是', 399),
 ('我', 336),
 ('我們', 335),
 ('你', 229),
 ('了', 205),
 ('在', 189),
 ('會', 179),
 ('它', 170),
 ('微信', 164),
 ('有', 150),
 ('人', 147),
 ('做', 144),
 ('裡面', 115),
 ('這個', 111),
 ('自己', 110),
 ('用戶', 110)]

什麼鬼,這都是些啥玩意,別急,其實啊,分詞領域還有一個概念叫做停用詞,所謂停用詞就是在語境中沒有具體含義的文字,例如這個、那個,你我他,的得地,以及標點符合等等。因為沒人在搜索的時候去用這些沒意義的停用詞搜索,為了使得分詞效果更好,我們就要把這些停用詞過去掉,我們去網上找個停用詞庫。

第二版代碼:

import pkuseg
from collections import Counter
import pprint

content = []
stopwords = []
new_text = []
with open("yanjiang.txt", encoding="utf-8") as f:
    content = f.read()
seg = pkuseg.pkuseg()
text = seg.cut(content)
with open("stopword.txt", encoding="utf-8") as f:
    stopwords = f.read()
for w in text:
    if w not in stopwords:
        new_text.append(w)
counter = Counter(new_text)
pprint.pprint(counter.most_common(20))

輸出:

[('微信', 164),
 ('用戶', 110),
 ('產品', 87),
 ('一種', 74),
 ('朋友圈', 72),
 ('程序', 55),
 ('社交', 55),
 ('這是', 43),
 ('視頻', 41),
 ('希望', 39),
 ('遊戲', 36),
 ('時間', 34),
 ('閱讀', 33),
 ('內容', 32),
 ('平台', 31),
 ('文章', 30),
 ('AI', 30),
 ('信息', 29),
 ('朋友', 28),
 ('就像', 28)]

看起來比第一次好多了,因為停用詞都過濾掉了。但是我們選出來的前 20 個高頻詞還是很不準確,有些不應該分詞的也被拆分了,例如朋友圈,公众号,小程序等詞,我們認為這是一個整體。

對於這些專有名詞,我們只需要指定一個用戶詞典, 分詞時用戶詞典中的詞固定不分開,重新進行分詞:

好多公众号的教程是使用一個列表:

lexicon = ['小程序', '朋友圈', '公众号']
seg = pkuseg.pkuseg(user_dict=lexicon)
text = seg.cut(content)

我這樣使用會報如下錯誤:

image

所以我這裡使用官方的用法,使用自定義詞典:

seg = pkuseg.pkuseg(user_dict='dict.txt') # 加載模型,給定用戶詞典
text = seg.cut(content)

dict.txt 中的內容為:


小程序
朋友圈
公众号

注意前面留一行,第一行的內容是讀取不到的,最後出來的結果前 50 個高頻詞是這樣的:

對張小龍演講稿分詞統計.py 輸出結果:

[('微信', 164),
 ('用戶', 110),
 ('產品', 89),
 ('朋友圈', 72),
 ('社交', 55),
 ('小程序', 53),
 ('視頻', 41),
 ('希望', 39),
 ('時間', 38),
 ('遊戲', 36),
 ('閱讀', 33),
 ('朋友', 32),
 ('內容', 32),
 ('平台', 31),
 ('文章', 30),
 ('AI', 30),
 ('信息', 29),
 ('團隊', 27),
 ('APP', 26),
 ('公众号', 25)]

可以看到張小龍講得最多的詞就是用戶、朋友、原動力、價值、分享、創意、發現等這些詞,這些詞正是互聯網的精神。

分詞的程序大概就到這裡了,接下來我們把分詞和詞雲放在一起。

框架整合#

我們把上面的 wordcloud 詞雲代碼和 pkuseg 代碼合在一起,並且改用 generate_from_frequencies () 即詞頻統計來生成詞雲,代碼如下:

pkuseg分詞統計並生成詞雲.py 代碼:

#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
@author: soapffz
@fucntion: pkuseg分詞統計並生成詞雲
@time: 2019-01-31
'''

import pkuseg
from collections import Counter
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
from os import path

tmp_content = []
stopwords = []
with open("yanjiang.txt", encoding="utf-8") as f:
    seg = pkuseg.pkuseg(user_dict='dict.txt')
    text = seg.cut(f.read())
with open("stopword.txt", encoding="utf-8") as f:  # 將停用詞加載進來
    stopwords = f.read()
for w in text:
    if w not in stopwords:  # 如果分詞得到的詞語中沒有在停用詞中就記錄下來
        tmp_content.append(w)
counter = Counter(tmp_content).most_common(30)  # 得到出現頻率最高的30個詞語
content = {}
for i in range(len(counter)):  # 將出現頻率最高的30個詞語及出現次數轉換為字典
    content[counter[i][0]] = counter[i][9]
# print(content) # 打印出分詞得到的頻率最高的30個詞語及次數字典

# 導出的詞雲圖片的位置設置為當前用戶的桌面
pic_path = path.join(path.expanduser("~")+"\\"+"Desktop"+"\\")
bak_pic = plt.imread("totoro.jpg")
wordcloud = WordCloud(
    font_path="simsun.ttc",  # 設置字體,這是win10的,win7字體後綴為ttf請自行查找
    mask=bak_pic,  # 設置背景圖片,設置背景圖片之後設置寬高不起作用
    background_color="white",  # 背景顏色為白色
    prefer_horizontal=0.7,  # 詞語水平方向排版出現的頻率設置為0.7(默認為0.9)
    scale=5,  # 畫布放大倍數設置為15(默認為1)
    margin=2  # 邊框設置為2
).generate_from_frequencies(content)  # 根據詞頻生成詞雲
wordcloud.recolor(color_func=ImageColorGenerator(bak_pic))  # 設置詞雲顏色為圖片的顏色
# 將生成的詞雲圖保存在當前用戶桌面,如重複生成會將之前的覆蓋
wordcloud.to_file(path.join(pic_path+"wordcloud.jpg"))
plt.imshow(wordcloud)  # imshow()作用:將一個image顯示在二維坐標軸上
plt.axis("off")  # 不顯示坐標軸
plt.show()  # 將image顯示出來

效果如下:

image

當然這樣的代碼太醜了,後面有時間盡量用可視化 GUI 來創作

所有代碼及示例文件下載地址:https://www.lanzous.com/i31rsdc

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。