[R語言專題] 運用R語言玩轉文字探勘 – 套件篇
0Last Updated on 2023-10-05
在R語言玩轉文字探勘,你不能錯過tidytext和quanteda。
背景
開發者們打造許多套件,替人們處理文字探勘。有些針對特定任務,例如在主題模型章節中我會介紹stm
、LDA
,在詞向量章節中則以gensim
、word2vec
為範例;也有些套件能夠貫穿整個文字探勘流程,例如這篇文章將要介紹的tidytext
和quanteda
。
雖然它們只是套件(library),但因爲開發者在設計套件時,已經預想到後續和專門任務的串接,同時也在套件中準備了泛用的文字探勘功能,因此,我特別以分析框架(framework)的規格,拉出一個章節說明。
資料介紹
在這篇介紹文中,會使用同一筆資料,這樣才能好好對比使用兩個框架時,流程和資料長相上的差異。我使用的資料是「歷年總統國慶大會演說」資料,資料來自維基百科文庫,從民國86年開始,一直到民國109年結束,共有24筆資料。
library(tidytext)
library(tidyverse)
df_speech_clean <- read_csv("data/df_speech_clean.csv")
df_speech_clean %>% glimpse()
#> Rows: 24
#> Columns: 5
#> $ id <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 1…
#> $ text <chr> "大會主席、各位貴賓、各位親愛的父老兄弟姐妹:\n今天是中華民…
#> $ title <chr> "總統蒞臨中華民國八十六年國慶大會致詞", "總統蒞臨中華民國八…
#> $ date <date> 1997-10-15, 1998-10-14, 1999-10-13, 2000-10-18, 2001-10-17,…
#> $ president <chr> "李登輝", "李登輝", "李登輝", "陳水扁", "陳水扁", "陳水扁", …
框架一: tidytext
tidytext
套件介紹
看到這個標題,一定會讓你想到R語言當中著名的tidyverse
宇宙。
沒錯,tidytext
其實和tidyverse
一樣,都遵守所謂的tidy data principle,在相同的世界觀裡之中處理資料、分析資料。
舉例來說,在tidytext
裡面利用函數unnest_tokens()
分詞的時候,預設的資料結構為dataframe/tibble,斷詞(tokenization)後的產出同樣也是dataframe/tibble,相比之下,quanteda
慣用的資料結構則是list,有很大對比。
在英文世界中,因為tidyverse
的普及,又因為tidytext
處理英文(專有名字?指英文中文拼字的區別)的方便,採用tidytext
框架的分析非常之多,R語言相關著作裡,也以Julia Silge和David Robinson合著的這本Text Mining with R最為知名。
後來Julia Silge和Emil Hvitfeldt有另外合著一本Supervised Machine Learning for Text Analysis in R,但無論是內容的實用程度,或者是名氣都比不上前一本著作。不過,因為R語言中與自然語言處理(natural language processing)相關的高品質書籍並不多,這兩本都已經是箇中翹楚。
選用tidytext
,你將可以預期自己的資料大部分時間都會是整齊的,即使要切換成其他資料格式例如語料(corpus),也有專門的函數幫助你;又因為還有tidymodels
和ggplot2
等tidyverse
生態系底下的套件支援,你可以輕鬆將資料餵到模型裡,接著跑回歸或者分類,又或者排序出文字重要性後,立刻以ggplot視覺化呈現。
不過,tidytext
在處理中文時,斷詞結果並不精準,尤其是對比專門為中文設計的jiebaR
,更是相形見絀。因此,你可以直接啟用完全不同的框架如quanteda
,或者是在其他套件中先處理好文字,接著再將成品重新導回tidyverse
的世界。
就我自己來說,因為熟悉tidyverse
,所以多半採用這個方法;但因為研究/工作需要,也有用quanteda
跑過一次不同的流程,就看個人習慣和應用場景的特性!
tidytext
實作
在我們用的這份資料中,每一列都代表一篇文章。不過,從文章中我們很難發掘意義。跟10年前相比,最近的總統致詞有什麼樣的特色?更常提到哪些詞彙?更少用到什麼用字?詞彙之間的關係是什麼?句子是變得更長還是更簡潔?這些,都是以文章為單位的資料中,我們無法清楚看出的。
因此,我們利用tidytext
中的unnest_tokens()
,把原先代表文章的text
欄位,以分詞函數unnest_tokens()
分成詞彙word
。
library(tidytext)
df_speech_token <- df_speech_clean %>%
unnest_tokens(word, text) %>%
select(word, everything())
df_speech_token %>% head(5)
#> # A tibble: 5 × 5
#> word id title date president
#> <chr> <dbl> <chr> <date> <chr>
#> 1 大會 1 總統蒞臨中華民國八十六年國慶大會致詞 1997-10-15 李登輝
#> 2 主席 1 總統蒞臨中華民國八十六年國慶大會致詞 1997-10-15 李登輝
#> 3 各位 1 總統蒞臨中華民國八十六年國慶大會致詞 1997-10-15 李登輝
#> 4 貴賓 1 總統蒞臨中華民國八十六年國慶大會致詞 1997-10-15 李登輝
#> 5 各位 1 總統蒞臨中華民國八十六年國慶大會致詞 1997-10-15 李登輝
我們可以發現,word
欄位就是原本「大會主席、各位貴賓、各位親愛的父老兄弟姐妹」分詞後的模樣,差別只在標點符號於分詞過程中消失了。從原始的文字資料,經過unnest_tokens()
分詞後,每筆資料都代表一個詞,這樣的資料在tidytext
框架中,就被稱為「tidy text」資料(整齊、整潔的文字),這也貼合tidyverse開發者之一Hadley Wickam提過「tidy data」的精神。
得到tidy text之後,我們可以進一步利用dplyr
彙整資料,得出有意義的指標,例如利用count()
查看最常出現的詞彙,或者接續利用ggplot2
視覺化。
library(tidyverse)
df_speech_token %>% count(word) %>%
arrange(desc(n))
#> # A tibble: 6,421 × 2
#> word n
#> <chr> <int>
#> 1 的 3163
#> 2 我們 755
#> 3 與 502
#> 4 在 459
#> 5 台灣 457
#> 6 臺灣 269
#> 7 國家 263
#> 8 經濟 257
#> 9 也 237
#> 10 民主 233
#> # ℹ 6,411 more rows
df_speech_token %>% count(word) %>%
arrange(desc(n)) %>%
head(10) %>%
mutate(word = fct_reorder(as_factor(word), n)) %>%
ggplot(aes(x = word, y = n)) +
geom_col() +
coord_flip() +
theme(text = element_text(family = "Noto Sans CJK TC Medium", size = 20))
不過,你也能從上圖發現,分詞後的結果有些問題,事實上,這能夠折射出文字探勘中部分流程的必要性。舉例來說,「的」出現次數最多,但它有意義嗎?若想要從統計結果中排除,除了用肉眼篩選以外,理應能利用詞性刪除它,但現有欄位中,我們其實沒有詞性,是不是可以在分詞中加入這個詞?
再來,排名第五和第六的分別是「台灣」和「臺灣」,是不是應該事先整併?在其他語料中,可能還有繁體與簡體,以及中文國字和阿拉伯數字的相似狀況,我們也能先以字串處理技巧處理。
還有,現在出現的詞彙都是單詞,但如果我們其實想看複合詞的頻率呢?舉例來說,有學者想特別查看「台灣」「人」共同出現的次數,或者「中國」「政府」共同出現的次數,在現有資料結構底下,我們也無法完成這類的統計。
從上述三個例子可以看出,雖然unnest_tokens()
分詞完,我們好像已經完成90%的任務,但實際上,沒有在事前做好字串處理,在分詞時沒有使用預寫的詞典,也沒有在分詞時貼上詞性,沒有針對結果清整,對文字探勘和後續資料分析的品質影響甚巨,後面的章節就會仔細分拆這些流程。
框架二: quanteda
quanteda
套件介紹
大約3年前,R User Group Taiwan曾經有幸邀請一位學者兼開發者Chung-hong Chan演講,當時我有出席,後來在學校上課時才知道,原來他是學校老師的好朋友與研究夥伴,又聽了一次他的分享。
在演講中,Chan介紹了全名為Quantitative Analysis of Textual Data的quanteda
,翻譯即為文字資料量化分析。
和tidytext相同,你同樣可以利用quanteda
走完一遍從前處理、斷詞、分析資料的流程,雖然使用的基本資料結構為list,但過程中並不會有任何不便,對中文的契合度也很高,是tidytext
以外的好選項。
Chan另外寫有一本線上作品Automated Content Analysis,若對學術研究中的自動內容分析有興趣,也可以參考一下。
quanteda
實作
更改套件與框架,不代表改變我們的提問,我們關注的同樣是總統致詞有何特色。差異體現在quanteda
不是在tidy priciples底下運行,這個體系中資料用不同方式儲存,例如coprus
、例如dfm
,後面解釋文件資料的時候,會討論不同儲存文章/文件格式有何特色。
因為資料df_speech_clean
原本是一個資料框,我們要先把它轉成quanteda
熟悉的格式。
library(quanteda)
# import article
corp <- corpus(df_speech_clean)
corp
#> Corpus consisting of 24 documents and 4 docvars.
#> text1 :
#> "大會主席、各位貴賓、各位親愛的父老兄弟姐妹: 今天是中華民國八十六年國慶日,海內外同胞用熱烈的活動,慶祝我們國家的生日;..."
#>
#> text2 :
#> "大會主席、各位貴賓、各位親愛的父老兄弟姊妹: 今天是中華民國八十七年國慶日,海內外同胞用最歡喜的心情和最熱烈的活動,慶祝..."
#>
#> text3 :
#> "各位女士、各位先生: 今天是中華民國八十八年國慶日。每年的這一天,我們都無比歡喜的慶祝國家的生日。但是,今天,我們卻要以..."
#>
#> text4 :
#> "各位貴賓、各位女士、各位先生: 今天是中華民國八十九年國慶,我們以欣慰又嚴肅的心情來慶祝國家的生日。今年的雙十國慶,較之..."
#>
#> text5 :
#> "各位貴賓、各位女士、各位先生: 今天是中華民國九十歲的生日,也是新世紀的第一個雙十國慶。一年多前,我們以實現歷史上首度的..."
#>
#> text6 :
#> "各位貴賓、各位伙伴、各位女士、先生:大家恭喜! 今天是中華民國九十一年國慶,二千三百萬台灣人民及海內外同胞謹以最莊嚴、最..."
#>
#> [ reached max_ndoc ... 18 more documents ]
docvars(corp)
#> id title date president
#> 1 1 總統蒞臨中華民國八十六年國慶大會致詞 1997-10-15 李登輝
#> 2 2 總統蒞臨中華民國八十七年國慶大會致詞 1998-10-14 李登輝
#> 3 3 總統蒞臨中華民國八十八年國慶大會致詞 1999-10-13 李登輝
#> 4 4 總統蒞臨中華民國八十九年國慶大會致詞 2000-10-18 陳水扁
#> 5 5 總統蒞臨中華民國九十年國慶大會致詞 2001-10-17 陳水扁
#> 6 6 總統蒞臨中華民國九十一年國慶大會致詞 2002-10-16 陳水扁
#> 7 7 總統蒞臨中華民國九十二年國慶大會致詞 2003-10-15 陳水扁
#> 8 8 總統蒞臨中華民國九十三年國慶大會致詞 2004-10-13 陳水扁
#> 9 9 總統蒞臨中華民國九十四年國慶大會致詞 2005-10-12 陳水扁
#> 10 10 總統蒞臨中華民國九十五年國慶大會致詞 2006-10-11 陳水扁
#> 11 11 總統蒞臨中華民國九十六年國慶大會致詞 2007-10-17 陳水扁
#> 12 12 總統蒞臨中華民國九十七年國慶大會致詞 2008-10-15 馬英九
#> 13 13 總統蒞臨中華民國九十八年國慶大會致詞 2009-10-14 馬英九
#> 14 14 總統蒞臨中華民國九十九年國慶大會致詞 2010-10-13 馬英九
#> 15 15 總統蒞臨中華民國一百年國慶大會致詞 2011-10-12 馬英九
#> 16 16 總統蒞臨中華民國一○一年國慶大會致詞 2012-10-17 馬英九
#> 17 17 總統蒞臨中華民國一○二年國慶大會致詞 2013-10-16 馬英九
#> 18 18 總統蒞臨中華民國一○三年國慶大會致詞 2014-10-15 馬英九
#> 19 19 總統蒞臨中華民國一○四年國慶大會致詞 2015-10-14 馬英九
#> 20 20 總統蒞臨中華民國一○五年國慶大會致詞 2016-10-12 蔡英文
#> 21 21 總統蒞臨中華民國一○六年國慶大會致詞 2017-10-12 蔡英文
#> 22 22 總統蒞臨中華民國一○七年國慶大會致詞 2018-10-17 蔡英文
#> 23 23 總統蒞臨中華民國一○八年國慶大會致詞 2019-10-16 蔡英文
#> 24 24 總統蒞臨中華民國一○九年國慶大會致詞 2020-10-14 蔡英文
你可以發現corpus
,或者稱其為語料、文集儲存資料的方式。「Corpus consisting of 24 documents and 4 docvars」告訴我們,corpus
由文件(documents)和文件變數(docvars, document variables)組成。
不同框架,可以完成相同任務。舉例來說,你若只想看2009年以後的演說資料,我們處理資料框時,可以利用dplyr
中的filter()
篩選出2009年以後的演說;對應語料格式,同樣可以從docvars下手。就斷詞來說,我們也可以用quanteda
達成。
# tokenize
ch_toks <- corp %>%
tokens(remove_punct = TRUE)
# construct a dfm
ch_dfm <- dfm(ch_toks)
topfeatures(ch_dfm)
#> 的 我們 與 在 台灣 臺灣 國家 經濟 也 民主
#> 3163 755 502 459 457 269 263 257 237 233
library("quanteda.textstats")
library("quanteda.textplots")
result_keyness <- textstat_keyness(ch_dfm)
textplot_keyness(result_keyness, margin = 0.2, n = 15, show_reference = F)
利用quanteda
完全可以做到和tidytext
相同的事情。事實上,它也有針對中文語料提供專門入門文章。你可以在文章中看到,quanteda
可以分詞、建構建構文件特徵矩陣、計算文件相似度、訓練主題模型等任務。
不過,我必須老實承認,因為我實在太習慣跟資料框打交道,即使是使用quanteda
時,我也會忍不住在途中,將成品適度轉換成資料框架確認品質,這樣做的理由在於quanteda
確實提供方便的文字探勘流程。
另外我也會習慣客製化輸出的圖表,畢竟我們必須承認,上面那張圖並不是非常好看。整體來說,quanteda
確實好用,只是沒那麼喜歡處理其他格式的資料,它非常值得倚重。
No Comments