1. 程式人生 > 實用技巧 >詞雲wordcloud的實現

詞雲wordcloud的實現

什麼是雲詞
快速實現
分詞
視覺化
實現原理TOC

什麼是雲詞

詞雲,也稱為文字雲或標籤雲。在詞雲圖片製作中,一般我們規定特定文字詞在文字資料來源中出現的次數越多,說明該詞越重要,其在詞雲中所佔區域也就越大。詞雲可以利用常見的幾何圖形,或者其他不規則的圖片素材形狀來作為界限。詞雲不僅可以應用在企業資料分析上,還可以應用到媒體營銷或者平面設計當中。
大概長這樣:

快速實現

要實現詞雲,首先肯定是要獲取片語和權重,然後再根據資料進行視覺化的展示。

分詞

手動輸入比較麻煩,一般根據句子進行分詞,然後按照詞語出現的次數計算權重。
這裡推薦用【訊飛關鍵詞提取API】,有免費的額度,類似的雲平臺基本上都有這類API。

https://www.xfyun.cn/doc/nlp/keyword-extraction/API.html
如果不想用雲API,也可以用開源分詞庫,這裡推薦jieba
https://github.com/fxsjy/jieba

視覺化

直接用輪子:
https://github.com/timdream/wordcloud2.js

如果是基於echart,可以以擴充套件形式引入:
https://github.com/ecomfe/echarts-wordcloud

實現原理

這裡主要分析wordcloud2.js的原始碼,簡要說明。
假設我們要將一個不帶旋轉的詞放入canvas中,如下圖所示:

注:canvas座標系Y軸是向下的。
1、canvas其實被切成了許多小方塊(如例圖所示),預設方塊大小為16px,不小於4px,方格越大,間距越大。
2、初始化grid = [];

用來判斷方塊中能否放置元素。
3、getTextInfo方法中,建立了一個新的fcanvas,通過measureText測量出片語寬度,為了防止文字被切,實際的fcanvas是大於測量的寬度的,如例圖中黃色方框與綠色方框的關係。
fctx.fillText(word, fillTextOffsetX * mu, (fillTextOffsetY + fontSize * 0.5) * mu);
注意:這裡為什麼fillTextOffsetY要加上fontSize?
預設情況下fillText的字型是左下角在座標點的,所以需要加上fontSize使其滿足canvas座標系。例如:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");


ctx.font="20px Georgia";
ctx.fillText("Hello World!",50,50);


ctx.fillRect(50,50,20,20)


可以看到文字是整個高出了方塊的。
4、fcanvas填充完文字之後,通過getImageData獲取圖片畫素資訊,然後迴圈遍歷每隔格子的每個畫素,判斷畫素alpha是否有值,有則判斷文字bounds邊界(例圖綠色方框),繼續迴圈下一個格子。
其中,bounds的值為【小y,大x,大y,小x】,通過這4個值,就可以得出邊界座標和寬高。
左上:【小x,小y】
右上:【大x,小y】
左下:【小x,大y】
右下:【大x,大y】
寬:大x-小x
高:大y-小y
注意:imageData[((gy * g + y) * width + (gx * g + x)) * 4 + 3]

getImageData() 方法返回 ImageData 物件,該物件拷貝了畫布指定矩形的畫素資料。
對於 ImageData 物件中的每個畫素,都存在著四方面的資訊,即 RGBA 值:
R - 紅色 (0-255)
G - 綠色 (0-255)
B - 藍色 (0-255)
A - alpha 通道 (0-255; 0 是透明的,255 是完全可見的)
color/alpha 以陣列形式存在,並存儲於 ImageData 物件的 data 屬性中。

這裡乘以4是因為每個畫素佔4個下標,加3是為了獲取alpha值。
5、從中心點位置到半徑範圍內遍歷,結合計算出的文字資訊判斷格子是否被佔用,沒被佔用則填充文字,更新格子的值,直到放不下或放完為止。

紅色部分即為被“佔領”的方格。
6、圖案遮罩如何實現的?
我們可以通過上傳圖片,實現圖片遮罩,白色部分將會被忽略,文字將被填充到黑色部分。


還記得grid[]嗎?其實我們在上傳圖片之後,通過getImageData獲取圖片資料,將白色的部分都標記成了已被佔用的狀態,所以後續的遍歷,只會找到黑色的部分進行填充。