文字探勘 - 創造專屬字典
一般來說,我們在處理文字斷詞時可以直接使用像是jieba
這類型的套件,因他已收集足夠豐富的字詞,所以斷字基本上不會有太大誤差。儘管是一些比較特殊的情境,像假如你要分析哈利波特小說的文字內容,或是魔戒的影評分析,網路上大多都有相對應的字典供你載入。但若你想分析的文章你苦苦找尋就是沒有找著字典,又或者是你想分析公司資料但特有的專業術語太多,這時候就得嘗試自己創造一個專有的字典。
你當然可以選擇將專有名詞一個一個填上去,但身為一個數據分析師,當然應該嘗試讓程式來幫我們完成囉!我參考了陳嘉葳寫的推廣PPT提供的方法,依步驟來動手完成這一隻程式。
前置作業
分析資料
我拿之前寫的一篇文章淺談文字探勘 - 轉換非結構化文字來做原始資料,當中包含文字探勘的一些專有名詞。同樣方法可應用在其他的文章或評論中,只是怕牽涉版權只好先拿自己的文章來玩玩。
環境及套件
操作的環境如下所示:
- 作業系統:macOS High Sierra
- 程式語言:R version 3.4.3
- IDE:RStudio version 1.1.414
過程中所需用到的套件如下:
1 | library(tidyverse) |
因為需要做文字上的處理,stringr
可以幫助我們在文字上做任意的轉換。tm
主要是用來做文字探勘的一個套件,但他對中文的支援度很差,一般來說建議使用tidytext
。不過在清理資料中的標點符號或是英文數字等,它有不錯的函數可以用,因此這裡也把它載入。RWeka
是用來個別斷詞用的,下方會說明它的用途。slam
則是因為文字的運算量相當龐大,過程中我們會轉換成matrix來做運算,這是可以使用這個套件會比較方便。snowfall
也是運算龐大的原因,因此用平行運算的方式來加速結果產出。
創建字典
資料處理
這裡我直接將文章的內容儲存成.txt檔,以方便直接讀取。現在的目的是製作專屬字典,因此標點符號及數字不是我們需考量的東西(當然如果需要也可以將它們納進來),在資料處理時就將它們移除。
1 | dat <- read_lines("./創建字典.txt") |
在將它們處理成一整個文字塊後,我們要將它們分開斷詞,期望處理成以下的形式:
“我們在分” “們在分析” “在分析時” “分析時都” “析時都習” “時都習慣” …
“我們在” “們在分” “在分析” “分析時” “析時都” “時都習” “都習慣” …
“我們” “們在” “在分” “分析” “析時” “時都” “都習” “習慣” …
這樣就可以將有可能形成字詞的字斷開,再用一些方法將專有的字詞跳出來。這裡要注意的是,NGramTokenizer
這個函式原來是為了拉丁語句做個別斷詞使用的,所以他對每一個字的斷詞判斷是以空格為主,因此我們用來處理中文時,就先將字與字之間加入空格,等斷完字再將空格移除。作法如下:
1 | full <- dat %>% str_split_fixed("", n = Inf) %>% |
接下來我們將出現較少的字詞移除,一方面是字詞太少時我們很難判斷它是否為專有名詞,另一方面是我們需將無關緊要的雜訊給移除。至於移除的標準就見仁見智,也有人不是用次數而是用比例當門檻。
1 | seg_base <- full %>% filter(n >= 2) # 將出現次數較少的詞彙濾除 |
獨立字詞
對於專有名詞來說,我們當然希望它能被完整的切出來,但跟專有名詞高關聯度的字詞很常會一起被切出來。像下面這樣:
“做文字探勘” “在文字探勘”
”文字探勘時“ “文字探勘的”
我們要如何將「做」、「在」、「時」、「的」等字眼去除呢?這裡可以用一些機率論的原理,舉例來說,「文字探勘」跟「做」應該是要被切分開的詞,那麼與就應該彼此獨立,也就是說,
我們做個簡單的換位,
當近似於1時,代表「做文字探勘」不是一個獨立詞彙,那麼它就極有可能非專有名詞。仔細觀察可以發現,越大作為一個字詞的可能性就越高,因此我們可以設置一個門檻,來濾除非我們想要的字詞。
因為母體的詞彙總數是相同的,所以這裡可以用次數來取代比率,會比較好計算。另外我們這裡只擷取四個字內的專有名詞,但切割需切割前後各一個字詞,這也是上面NGramTokenizer
取5個字的原因。
1 | ## 計算字詞的獨立關係 |
去除不完整的詞彙
除了多餘的字詞來添亂,因為我們是個別斷詞的關係,零碎的字也會被抓進來。舉例來說:「文字探勘」會被斷出來,但是「文字探」或「字探勘」這類型的字一樣也會被抓出。為了解決這樣的問題,就引入了訊息量—「熵」這個概念。一個專有名詞周遭通常伴隨較豐富的訊息量,但像「文字探」後面通常只會加「勘」,所以訊息量就很低。所以只要抓住這個概念,只取兩側有豐富訊息量的字詞,就很有可能為專屬名詞。
1 | freq_all <- matrix(full$proportion, dimnames = list(full$name)) |
結果產出
廢話不多說,先來看看這樣產出後的結果:
1 | 一向量 |
可以看到雖然分出的字詞有些道理,但卻不那麼漂亮。這個原因在所準備的文字資料不夠大量所致,像「個詞彙在」這個詞,隨著「個」及「在」的比例增加,這個詞在獨立字詞階段就會被去除。所幸的是我們還是抓出了想要做成字典的字詞:
1 | 向量指標 |