文中所涉及的文本及圖片資源可通過下列鏈接獲取:
鏈接:https://pan.baidu.com/s/1Kf_bC5mRq3qBs7Oi3QNdQQ
提取碼:rf5h
詞雲製作
相信大家近些年一定解除了很多形形色色的詞雲,那麼那些亮眼的詞雲是如何製作的呢?我們是不是也可以用 Python 製作一個呢?
俗話說,知其所往方可往(我瞎編的ヽ(・ω・´メ))。我們首先要明白其原理,知曉其流程,才能達到我們的目的。
顯然,詞雲是以文本里的某一詞彙的出現頻率爲依據,將詞彙用不同大小的字體展現出來的一個文本處理過程,最後以一定的形態輸出,我們稱其爲圖像處理過程。那麼,我們就知道了其分爲兩步:
- 文本分割,按照詞彙出現頻率高低進行統計排序
- 圖像顯示,將詞彙以不同的形態輸出
按照語言類型的不同,我們將其分類爲拼音類文本(以英文文本爲代表)和象形類文本(以中文文本爲代表)。兩者的文本分割處理略有不同,在此分類討論。
英文文本
對於英文文本來說,其文本的分割大致可分爲以下幾個步驟:
- 首先,我們要統一英文的大小寫。雖然我們知道某一單詞無論其大寫還是小寫都表示相同語義,但是由於Python對大小寫敏感,所以我們首先要統一單詞的大小寫。
- 然後,我們要去掉特殊的符號。這裏所說的符號在英文中指那些特殊字符和標點符號。
- 最後,對每個單詞進行詞頻統計。我們常用字典類型進行統計,鍵可以表示爲單詞,而值表示爲頻數。
中文文本
由於中文文本中的單詞不是通過空格或者標點符號進行分割,因此中文及其類似語言存在一個重要的分詞問題,即如何正確地劃分一段文本所出現的詞彙。
例如最出名的一段話下雨天留客天留我不留,你會如何劃分呢???
jieba庫
概述
- jieba 是 Python 中一個常用的第三方中文分詞函數庫,能夠將一段中文文本分割成中文詞語的序列。
- jieba 的分詞原理是利用一箇中文詞庫,將待分詞的內容與分詞詞庫進行對比,通過圖結構和動態規劃方法找到最大概率的詞組。除此之外,jieba 還提供增加自定義中文單詞的功能。
- jieba 支持三種分詞模式:精確模式—將句子最精確地切開,適合文本分析;全模式—把句子中所有可以成詞的詞語都掃描出來,速度快但不能解決歧義;搜索引擎模式—在精確模式基礎上,對長詞再次切分,提高召回率,適合用於搜索引擎分詞。
安裝
在系統命令行輸入pip install jieba
即可
使用
jieba 庫包含的主要函數如下:
jieba.lcut(s)
:精確模式,返回一個列表類型jieba.lcut(s,cut_all=True)
:全模式,返回一個列表類型jieba.lcut_for_search(s)
:搜索引擎模式,返回一個列表類型jieba.add_word(w)
:向分詞詞典中添加新詞 w
既然中文分詞存在三種方法,那麼我們該用哪種方法呢?
(我有選擇困難症唉,怎麼po[・_・?])
答案是——看需求:- 明確模式:希望對文本準確分詞,不產生冗餘
- 全模式:希望對文本分詞更準確,不漏掉任何可能的分詞結果
- 搜索引擎模式:沒想好怎麼用,想看看文本大致構造
詞雲製作
數據展示方式多樣,傳統的統計圖表雖然科學但略顯古板。尤其對於文本來說,直觀簡單又充滿藝術感的設計效果給人帶來的不僅是文本的分析結果,更是美的享受。
詞雲以詞語爲基本單元,根據其在文本中出現的頻率設計不同大小以形成視覺上的不同效果,形成“關鍵詞雲層”,從而使讀者對文本主旨一目瞭然。
wordcloud庫
概述
wordcloud 庫是專門用於根據文本生成詞雲的 Python 第三方庫,常用且有趣。
安裝
在系統命令行中輸入pip install wordcloud
即可
使用
- 在生成詞雲時,wordcloud 默認會以空格或標點爲分隔符對目標文本進行分詞處理。
- 對於中文文本,分詞處理需要由用戶來完成:一般是先將文本分詞處理,然後以空格連接,再調用 wordcloud 庫函數。此外,還需指定中文字體,將該字體文件與代碼存放在同一目錄下或在字體文件名前增加完整路徑。
wordcloud 庫的核心是 WordCloud 類,所有的功能都封裝在其中。
使用時需要實例化一個 WordCloud 類的對象,並調用其 generate(text) 方法將 text 文本轉化爲詞雲。WordCloud 類在創建時有一系列可選參數,用於配置詞雲圖。其常用參數如下所示:
WordCloud 對象創建常用參數
font_path
:指定字體文件及其路徑,默認爲系統字體庫 C:\Windows\Fontswidth
:圖片寬度,默認400像素height
:圖片高度,默認200像素mask
:詞雲形狀,默認爲方形圖min_font_size
:詞雲中最小字體號,默認4號font_step
:字號步進間隔,默認爲5max_font_size
:詞雲中最大字體號,默認自動調節max_words
:詞雲圖中最大詞數,默認200stopwords
:被排除詞列表,詞雲不予顯示background_color
:圖片背景顏色,默認黑色
WordCloud 類常用方法
generate(txt)
:由 txt 文本生成詞雲to_file(filename)
:將詞雲圖保存爲名爲 filename 的圖片,路徑可自行設置
示例
英文文本:以 hamlet 爲例
wordcloud 庫 默認以空格或標點爲分隔符對目標文件進行處理,因此特別適合直接處理英文文本。代碼如下:
# -*-coding:utf-8-*-
from wordcloud import WordCloud
txt = open("C:/Users/SHOHOKU/Desktop/hamlet.txt","r").read()
wordcloud = WordCloud(font_path="AdobeKaitiStd-Regular.otf",width=911,height=305,max_words=36).generate(txt)
wordcloud.to_file("C:/Users/SHOHOKU/Desktop/hamlet.png")
效果如圖所示:
當然,你既然讀到這裏了,我這麼一個好心的人肯定會送你一份禮物。想了想,送你一份英文文本分割的程序吧,相信以後會用的到:
# -*-coding:utf-8-*-
# 文本預處理
def gettext():
txt = open("C:/Users/SHOHOKU/Desktop/hamlet.txt","r").read() #讀取文本
txt = txt.lower() #統一大小寫
symbol = [",",".","?","/","<",">",'''""''',"''",\
":",";","]","[]","{}","","+","=","-","_","()",\
"*","&","^","%","$","#","@","!","~","`"]
for s in symbol:
txt = txt.replace(s,"") #以空格代替特殊符號
return txt
# 文本處理
text = gettext() #加載文本
words = text.split() #以空格切分文章
# 詞頻統計
counts = {} #創建字典類型用來統計詞彙及其頻數
for word in words:
counts[word] = counts.get(word,0) + 1 #對於已有詞彙,返回其序數並+1;對於新詞彙,返回0並+1
# 結果展示
items = list(counts.items()) #將字典類型轉換爲列表類型,便於展示結果
items.sort(key=lambda x:x[1],reverse = True) #按照第二列對結果進行降序排序
for i in range(10): #展示出現次數最高的前10個單詞
word,count = items[i]
print("{:<10}{}".format(word,count))
中文文本:以《三國演義》爲例
需求:統計三國演義中出現頻率前10的人物,並生成相應詞雲。
第一步:人物出場頻率統計
# -*-coding:utf-8-*-
# 文本預處理
def gettext():
txt = open("C:/Users/SHOHOKU/Desktop/threekingdoms.txt","r",encoding="utf-8").read()
return txt
# 文本分割
import jieba
text = gettext()
words = jieba.lcut(text)
# 詞頻統計
counts = {}
for word in words:
if len(word)==1: #姓名通常不爲一字,故做簡化處理
continue
else:
counts[word] = counts.get(word,0) + 1
# 結果展示
items = list(counts.items())
items.sort(key = lambda x:x[1],reverse=True)
for i in range(25):
word,count = items[i]
print("{:<10}{}".format(word,count))
結果如圖所示:
我們發現,結果中存在着一些問題:有的和人名並無關係,將軍這類詞很模糊,而且像玄德曰,玄德,劉備其實是同一人物等。這是中文所特有的多語義性造成的。所以我們進行中文文本詞頻統計時,要適當地擴大探索範圍,觀察其展示結果的不足並加以改進,像DNA分子一樣螺旋上升,纔可以到達希望的頂峯。
我們試着改進其文本,以便能更好地滿足我們的需求,經過不斷嘗試,我們可以獲得一個大致結果:
# -*-coding:utf-8-*-
# 文本預處理
def gettext():
txt = open("C:/Users/SHOHOKU/Desktop/threekingdoms.txt","r",encoding="utf-8").read()
return txt
excludes = ["將軍","荊州","卻說","二人","不可","不能","如此",\
"商議","如何","軍士","主公","軍馬","左右","次日","大喜","引兵","天下","於是","東吳","今日","魏兵",\
"不敢","陛下","一人","人馬","不知","都督"]
# 文本分割
import jieba
text = gettext()
words = jieba.lcut(text)
# 詞頻統計
counts = {}
for word in words:
if len(word) == 1: #姓名不可能爲一個字,捨棄之
continue
if word=="丞相" or word=="孟德":
rword="曹操"
if word=="諸葛亮" or word=="孔明曰":
rword = "孔明"
if word=="關公" or word=="雲長":
rword = "關羽"
if word=="玄德" or word=="玄德曰":
rword = "劉備"
if word=="翼德":
rword = "張飛"
if word=="子龍":
rword = "趙雲"
else:
rword = word
counts[rword] = counts.get(rword,0)+1
for word in excludes:
del counts[word]
# 結果展示
items = list(counts.items())
items.sort(key = lambda x:x[1],reverse=True)
for i in range(25):
word,count = items[i]
print("{:<10}{}".format(word,count))
結果如圖所示:
我們發現改進過後的代碼已經可以大致達到我們的目的,但通過去除詞來重新分割文本的方法耗時費力,並不怎麼提倡。如果有更好的方法,歡迎與筆者交流。
第二步:在文本分割的基礎上利用空格分割文本,然後利用 wordcloud 庫輸出即可
# -*-coding:utf-8-*-
import jieba
from wordcloud import WordCloud
from scipy.misc import imread
img = imread("c:/Users/SHOHOKU/Desktop/chinamap.jpg")
excludes = ["將軍","荊州","卻說","二人","不可","不能","如此",\
"商議","如何","軍士","主公","軍馬","左右","次日","大喜","引兵","天下","於是","東吳","今日","魏兵",\
"不敢","陛下","一人","人馬","不知"]
def gettext():
f = open("C:/Users/SHOHOKU/Desktop/threekingdoms.txt","r",encoding="utf-8").read()
words = jieba.lcut(f)
ls = []
for i in words:
if len(i)==1 or i in excludes:
continue
elif i in ["丞相","孟德"]:
ls.append("曹操")
elif i in ["孔明曰","孔明"]:
ls.append("諸葛亮")
elif i in ["玄德曰","玄德"]:
ls.append("劉備")
elif i in ["關公","雲長"]:
ls.append("關羽")
elif i in ["翼德"]:
ls.append("張飛")
elif i in ["子龍"]:
ls.append("趙雲")
elif i in ["都督"]:
ls.append("周瑜")
else:
ls.append(i)
return " ".join(ls)
txt = gettext()
w = WordCloud(font_path="STXINGKA.TTF",width=911,height=305,mask=img,max_words=10,background_color="white").generate(txt)
w.to_file("c:/Users/SHOHOKU/Desktop/April.png")
效果如圖所示: