Re0誰是真女主?讓詞雲來告訴你

zhazha_hui發表於2020-09-30

1.前言

關於Re0誰是女主角一直是個爭論不休的話題,作為一個堅定的艾米莉亞黨,為了支援自己買的股票,我決定交給Re0原著小說(1-19章)來判斷。根據原著中誰的出場頻率最高,來看看誰才是真正的女主角。

2.最簡單的詞雲

首先讓我們有請我們的工具人1號——WordCloud庫。它可以統計文字中各個詞語的頻率,並且將它們通過不同大小的詞雲來展示。話不多說,上程式碼。

import matplotlib.pyplot as plt
from wordcloud import WordCloud

txt = open('Re0.txt', encoding='ANSI').read()
#讀入Re0小說,注意小說的格式,儲存在字串txt中
wc = WordCloud(font_path='Hiragino.ttf',width=800,
height=600,mode='RGBA',background_color=None).generate(txt)
#WordCloud新建的是一個wordcloud物件,裡面的引數可以自行控制,
#width和heigth分別是寬度和高度
#font_path代指字型路徑,中文字型不設定路徑就會出現亂碼
#而mode代表讀入的模式,這裡採用RBGA格式,表示在RGB的基礎上加了一
#個alpha,也就是透明度backgroud_color為背景顏色,預設是黑色,
#這裡設定為None和RGBA模式配合可以得到無背景的透明詞雲

plt.imshow(wc)
plt.axis('off')
#去除座標軸
plt.show()
wc.to_file('Re0_untokenized.png')
#輸出圖片,注意在RGBA模式下只能輸出為png格式的圖片
Re0誰是真女主?讓詞雲來告訴你
圖1 最簡單的詞雲
# 分詞後的詞雲 這裡我們看到一堆沒有用的詞,‘因為’,‘所以’,‘不過’……。使用頻率最高的是‘不過’,‘可是’,‘但是’這些轉折詞。而且出現了'開什麼玩笑','怎麼了',‘不僅如此’,‘即使如此’這些分詞沒有分開的情況。那麼這個時候有請我們的二號工具人——jieba分詞和stopwords停頓詞。jieba是目前特別流行的中文分詞工具,可以恰到好處地對中文句子進行分詞。而從stopwords中可以得到目前中文中出現頻率極高但是卻對分析文字沒有意義的詞。例如:‘因為’,‘所以’,‘不過’這些關聯詞等等。這裡博主下載的是百度的停頓詞庫。連結見文末參考資料。
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import jieba

txt = open('Re0.txt', encoding='ANSI').read()
txt = ' '.join(jieba.cut(txt))
#用jieba進行分詞,再將分詞用空格連線起來。這裡分詞會消耗很長的時間
stop_words = open('stopwords/baidu_stopwords.txt', 
encoding='utf8').read().split()
#開啟停頓詞庫,將其用空白符分割成列表
wc = WordCloud(font_path='Hiragino.ttf',width=800, height=600, 
stopwords=stop_words,mode='RGBA',
background_color=None).generate(txt)
#把停頓詞列表輸入wordcloud物件中,這樣wordcloud計數時便會忽略這些詞

plt.imshow(wc)
plt.axis('off')
plt.show()

wc.to_file('Re0_tokenized_stopwords.png')
Re0誰是真女主?讓詞雲來告訴你
圖2 分詞和處理停頓詞之後的詞雲

看到艾米莉亞這幾個大字沒有,這是EMT黨的大勝利!!!但是我有覺得好像和雷姆差不多。那麼讓我們用數來說話吧。下面我們選取幾個比較大的名字將他們在詞雲統計中的頻率畫出來。

#接續上一節程式碼
print(wc.words_)
# wc.words_是詞雲物件的一個成員,它是由詞語:頻率組成的字典,
# 我們將它列印觀察一下
plt.rcParams['font.sans-serif']=['KaiTi'] #用來正常顯示中文標籤
plt.rcParams['font.serif'] = ['KaiTi']
plt.rcParams['axes.unicode_minus']=False #用來正常顯示負號
#這一段是用來處理中文亂碼的程式碼,原本是打算按照網上改成SimHei字型,
#發現還是亂碼結果改成KaiTi才解決了這個問題。

new_keys = [u'愛蜜 莉雅', u'雷姆', u'拉姆', u'羅茲 瓦爾',
 u'威爾 海姆', u'嘉飛爾', u'碧翠絲', u'裡烏斯']
#這裡每個中文詞前面加u表示採用unicode編碼

dic1 = {key : wc.words_[key] for key in new_keys}

# 這裡我們根據選擇的keys形成新的詞典
plt.bar(*zip(*dic1.items()))
#用dic1.items()獲得字典裡面的全部內容,得到用二元組組成的列表
#zip(*)將其解壓,得到一堆二元組。bar(*)將二元組一個個畫出來

plt.show()

Re0誰是真女主?讓詞雲來告訴你
圖2 分詞和處理停頓詞之後的詞雲
wordcloud輸出是各個統計詞的頻率的關係。這裡詞雲似乎預設將出場最多的詞頻率設定為1,在此基礎上其他詞再與其比較。
#列印的結果
{'愛蜜 莉雅': 1.0, '雷姆': 0.5444051387078757, '拉姆': 
0.41817166263265687, '羅茲 瓦爾': 0.39136101284676966, '威爾 海姆': 
0.3528207037795569, '嘉飛爾': 0.31297709923664124, '碧翠絲': 
0.28542170917892384, '聲音': 0.2619623906162726, '少女': 
0.26084528020852726, '魔女': 0.25991435486873954, '就算': 
0.25544591323775834, '身體': 0.23589648110221562, '世界': 
0.23422081549059764, '真的': 0.2329175200148948, '感覺': 
0.23273133494693726, '一個': 0.22938000372370135, '裡烏斯': 
0.2280767082479985, 

我們可以看到艾米莉亞詞頻最高,EMT的大勝利。而雷姆從第二卷登場,第十卷沉睡,只在一半的章節登場,相對出場頻率卻佔了艾米莉亞的一半還多,tql。而從詞頻上看,486根本沒有上榜,可能是分詞後‘菜月昴’無了,所以我們的男主是’羅茲瓦爾’,不接受反駁,謝謝。牛頭人黨的大勝利。另外,讓人感到意外的是,Re0裡面妹子這麼多,沒想到威爾海姆老爺子,加菲貓,尤里烏斯這幾個大老爺們也上榜了。真就是無論男的女的我全都要唄。

3.將詞雲變成艾米莉亞的形狀

這樣就完事也太無聊了,為了慶祝EMT黨的大勝利,不如我們把詞雲改成艾米莉亞的形狀。這裡就要介紹我們的第3個工具人了——cv2庫。它能夠按照多維陣列格式讀入圖片。wordcloud物件有一個mask引數,讀入多維陣列。然後wordcloud會將白色的畫素視為背景刪去,在剩餘的畫素點上繪圖。因此要求我們選作蒙版的圖片要麼是白色背景的jpg,要麼是無背景的PNG,並且儘量大一點且前景集中在一塊,否則會出現字看不清和字分佈得很離散的情況。
這裡由於艾米莉亞穿的是白色的衣服,所以經常被剔除,得到殘破不全的詞雲。無奈之下博主只能用PS P了一張黑白的。這是原圖和P圖

Re0誰是真女主?讓詞雲來告訴你圖3.a 原圖
Re0誰是真女主?讓詞雲來告訴你圖3.b PS處理後的圖
import matplotlib.pyplot as plt
import jieba
import cv2
from wordcloud import WordCloud

txt = open('Re0.txt', encoding='ANSI').read()
txt = ' '.join(jieba.cut(txt))
stop_words = open('stopwords/baidu_stopwords.txt', 
                  encoding='utf8').read().split()

img = cv2.imread('aimi4.png', cv2.IMREAD_GRAYSCALE)
#用cv2庫讀入圖影像,並且採用灰度方式讀入。
#這裡推薦使用600x800以上的圖片,而且背景和角色最好分開,角色最好比較大,
#圖片背景必須是透明或者全白,不能有其它雜色

wc = WordCloud(mask=img, font_path='Hiragino.ttf', 
stopwords=stop_words,contour_width=1,contour_color='black',
background_color='white').generate(txt)
#設定Img為mask蒙版,控制詞雲形狀
#為了將圖片的輪廓畫出來,所以指定contour_width就是輪廓寬度,預設等於
#0,就#是不畫輪廓,contour_color指的是輪廓顏色。
#注意這裡不能用RGBA模式否則報錯
#operands could not be broadcast together 
#with shapes (868,726,4) (868,726,3)猜測是因為RGBA是4維度的
#而畫輪廓用的卻是三維顏色

plt.imshow(wc)
plt.axis('off')
plt.show()

wc.to_file('Re0_black_shape.png')
Re0誰是真女主?讓詞雲來告訴你
圖4 艾米莉亞形狀的雲圖

4.給詞雲染上艾米莉亞的顏色

這還不夠,博主希望控制雲圖的文字安裝艾米莉亞的顏色來上色。也就是說根據原圖中艾米莉亞的顏色分佈控制雲圖文字顏色分佈。這裡我們可以借用WordCloud中的ImageColorGenerator函式得到原圖的顏色分佈,在利用wordcloud物件的recolor函式給雲圖重新上色。

# -*- coding: utf-8 -*-

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

txt = open('Re0.txt', encoding='ANSI').read()
txt = ' '.join(jieba.cut(txt))

stop_words = open('stopwords/baidu_stopwords.txt', 
                  encoding='utf8').read().split()

mask = cv2.imread('aimi4.png', cv2.IMREAD_UNCHANGED)
#這裡假如後面的詞雲要採用RGBA模式的話,讀入png格式需要指定讀入格式為
#cv2.IMREAD_UNCHANGED,否則alpha元素資訊丟失,後面會報錯。

wc = WordCloud(mask=mask, font_path='Hiragino.ttf', 
stopwords=stop_words, 
               contour_width=1,contour_color='black',
               background_color='gray').generate(txt)
#這裡為了便於觀看將背景色設為灰色
color = cv2.imread('aimi3.png', cv2.IMREAD_UNCHANGED)
img_color =  ImageColorGenerator(color)
#從圖片中獲得原圖的顏色分佈
wc.recolor(color_func=img_color)
#根據圖片顏色分佈給詞雲上色
plt.imshow(wc)
plt.axis('off')
plt.show()

wc.to_file('Re0_color_mask.png')
Re0誰是真女主?讓詞雲來告訴你圖5.a 原圖
Re0誰是真女主?讓詞雲來告訴你圖5.b 染上艾米莉亞顏色的詞雲

5.用雷姆藍給詞雲上色

但是博主作為一個博愛的人,對於雷姆也特別喜歡,所以我希望將詞雲染上雷姆色。並且希望將詞雲用雷姆色表示。(希望各位EMT黨放下你們手裡的刀)。這裡就要說到wordcloud物件的又一個引數了——color_func。它讀入一個返回顏色的函式名,形參為word, font_size, position, orientation, font_path, random_state。例如random_color(word, font_size, position, orientation, font_path, random_state)。這裡為了控制詞雲的顏色。根據官網的說法,可以這麼寫color_func=lambda *args, **kwargs: (255,0,0)便可以將字型顏色設定為紅色。

# -*- coding: utf-8 -*-
from wordcloud import WordCloud
import matplotlib.pyplot as plt
import cv2
import jieba

# 開啟文字
text = open('Re0.txt', encoding='ANSI').read()

# 中文分詞
text = ' '.join(jieba.cut(text))
#print(text[:100])

# 生成物件
mask = cv2.imread('aimi4.png', cv2.IMREAD_UNCHANGED)
stop_words = open('stopwords/baidu_stopwords.txt', 
                  encoding='utf8').read().split()

wc = WordCloud(color_func=lambda *args, **kwargs: (0,0,255), 
mask=mask, font_path='Hiragino.ttf',stopwords=stop_words,
               contour_width=1,contour_color='black',
               background_color='white').generate(text)
#這裡設定詞雲的顏色為藍色

# 顯示詞雲
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
plt.show()

# 儲存到檔案
wc.to_file('RE0_color_blue.png')
Re0誰是真女主?讓詞雲來告訴你
圖6 雷姆色的詞雲

6.用頻率說話

這裡詞雲可以通過wordcloud物件的generate(文字字串)生成,實際上,也可以用wordcloud物件的generate_from_frequencies(頻率字典)函式形成。也就是說,該函式可以讀入一個形如{詞:詞頻}的字典,然後輸出雲圖。我們看看利用這個函式會不會有什麼區別。

# -*- coding: utf-8 -*-

from wordcloud import WordCloud
import matplotlib.pyplot as plt
import cv2
import jieba.analyse
# In[1]:
# 開啟文字
text = open('Re0.txt', encoding='ANSI').read()

# 提取關鍵詞和權重
#freq = jieba.analyse.extract_tags(text, topK=200, 
#                      withWeight=True, allowPOS=('ns','n'))
#這裡allowPos是指允許提取的詞性,這裡選取的是ns--地名,n--名詞,
#但是耗時長達2min,並且人名無法分析出來,很奇怪,於是果斷放棄

freq = jieba.analyse.extract_tags(text, topK=200, withWeight=True)
print(freq[:20])

#這裡由於jieba分析後得到的結果返回的是二元組構成的列表,
#因此需要將其修改為字典
freq = {i[0]: i[1] for i in freq}

mask = cv2.imread('aimi4.png')
stop_words = open('stopwords/baidu_stopwords.txt', 
                  encoding='utf8').read().split() 
wc = WordCloud(color_func=lambda *args, **kwargs: (0,0,255),
mask=mask, font_path='Hiragino.ttf', stopwords=stop_words,
contour_width=1,contour_color='black', 
background_color='white').generate_from_frequencies(freq)

# 顯示詞雲
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
plt.show()

wc.to_file('RE0_Freq.png')
Re0誰是真女主?讓詞雲來告訴你圖7.a 文字生成的詞雲
Re0誰是真女主?讓詞雲來告訴你圖7.b 頻率生成的詞雲

我們可以看到還是有些區別的,艾米莉亞被分成了兩個詞艾米和莉亞。羅茲瓦爾也是類似。而且還有不少的停頓詞如‘只是’,‘只有’,‘什麼’等等。看樣子還是老老實實用generate吧。
圖片均來自於百度,如若侵權請通知博主刪除。剛剛入門wordcloud。如果有錯誤希望各位大佬不吝指教。

參考資料:

中文停頓詞庫
官網
深度有趣課程

相關文章