R, tidytext, jiebaR 中文斷詞文字探勘處理

tidytext 是 R 軟體 tm 套件之外新的一套文字探勘處理套件。網路上關於 tidytext 的文章,99% 是處理英文,僅有的一篇西遊記分析文章,裡面也僅是點到為止,連中文斷詞都省略。所以我決定自己測一下 tidytext 套件搭配 jiebaR 中文斷詞、以及跟原有 tm 套件 dtm / tdm 矩陣互相轉換的介紹

以下程式使用的文章資料,是網路上可以找到的 hotel 評價資料檔,隨機抽選 321 篇文章出來測試

參考文章: Text Mining with R (tidytext)

# 讀入 d:\data\hotel 資料夾中所有 *.txt 文章
xdir = "d:/data/hotel"
fnames = list.files(path=xdir,pattern="*.txt")

xtext = NULL
for (f in fnames) {
fpath = paste(xdir,f,sep="/")
t0 = readLines(fpath)
t1 = paste(t0,collapse=" ")
xtext = c(xtext,t1)
}

# 使用 jiebaR 作中文斷詞
library(jiebaR)

xseg = worker() # 一般斷詞
xtext2 = NULL
for (i in 1:length(xtext)){
t0 = xtext[i]
t1 = xseg <= t0
xtext2 = c(xtext2,paste0(t1,collapse=" "))
}

require(dplyr)

text_df = data_frame(doc_id = 1:length(xtext2), text = xtext2)
# head(text_df)

library(tidytext)

# 轉成 tidytext 格式

# unnest_tokens_(data, output_col, input_col, token = "words")
# 參數:
# output_col: output column, 新變數名稱是 word
# input_col: input column, 舊變數名稱是 text_df 中的 text
# token 預設是 “words”, 但也可以是 “ngrams”, “sentences”, “lines”,
# “paragraphs”,…

# unnest_tokens 函數遇到中文會不正常,偶而會把已經斷好的詞
# 再切開成單字
# 自己準備一個 tokenizer 函數 tok99
tok99 = function(t) strsplit(t,”[ ]{1,}”)

td1 = unnest_tokens(text_df,word, text, token=tok99)

td1

?View Code LANGUAGE
# A tibble: 20,418 × 2
#   doc_id word
#1      1  這個
#2      1    鬼
#3      1  地方
#4      1  居然
#5      1    是
#..............
# ... with 20,408 more rows

td_count = count(td1,word, sort = TRUE)

td_count

?View Code LANGUAGE
# A tibble: 3,125 × 2
#    word    n
#1     的  1219
#2   酒店   431
#3     了   361
#4     是   330
#5     很   298
#..........
# ... with 3,115 more rows

############################################
# 詞彙頻率圖
############################################

library(ggplot2)

td_count %>%
filter(n > 50) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n)) +
geom_col() +
xlab(NULL) +
coord_flip()

############################################
# 文字雲
############################################

library(wordcloud)

td_count %>%
with(wordcloud(word, n, min.freq = 20, scale=c(10,1),
random.order = F, ordered.colors = F, colors=rainbow(1000)) )

############################################
# 計算 TF-IDF
############################################

# 先計算個別文章各詞的 TF-IDF
td2 = td1 %>%
count(doc_id,word,sort=T) %>%
ungroup() %>%
bind_tf_idf(word,doc_id, n)

td2

?View Code LANGUAGE
# A tibble: 16,011 × 6
#    doc_id word    n    tf         idf     tf_idf
#1    301    的    25 0.09541985 0.222365 0.021218038
#2    129    的    23 0.09387755 0.222365 0.020875085
#3     88    的    19 0.06188925 0.222365 0.013762006
#4    163    的    19 0.05382436 0.222365 0.011968656
# .....................
# ... with 16,001 more rows

td_tfidf = arrange(td2,desc(tf_idf))

td_tfidf

?View Code LANGUAGE
# A tibble: 16,011 × 6
#    doc_id word    n     tf        idf     tf_idf
#1    190   花園     2 0.14285714 4.672829 0.6675470
#2    198   花園     2 0.14285714 4.672829 0.6675470
#3      1      0     3 0.11538462 5.771441 0.6659355
#4      2   搖頭     2 0.10526316 5.771441 0.6075201
#..............
# ... with 16,001 more rows

#####################################################
# 各篇文章的 TF-IDF 比較圖
#####################################################
plot_hotel = mutate(td_tfidf,word = factor(word, levels = rev(unique(word))))

plot_hotel %>%
top_n(30) %>%
ggplot(aes(word, tf_idf, fill = doc_id)) +
geom_col() +
labs(x = NULL, y = “tf-idf”) +
coord_flip()

############################################################
# 轉成 tm 套件的 dtm 矩陣
#
# cast_dtm(data, document_col, term_col, value_col,
# weighting = tm::weightTf, …)
############################################################
library(tm)

# 轉成一般 TF 為主的 dtm 矩陣
dtm_tf = cast_dtm(td_tfidf,doc_id, word, n)

dim(dtm_tf)
#[1] 321 3125

inspect(dtm_tf[1:10,1:10])

# 轉成 TF-IDF dtm 矩陣
dtm_tfidf = cast_dtm(td_tfidf, doc_id, word, tf_idf)

dim(dtm_tfidf)
#[1] 321 3125

inspect(dtm_tfidf[1:10,1:10])

############################################################
# 轉成 Matrix 套件的稀疏矩陣 (sparse matrix)
############################################################

library(Matrix)

# cast into a Matrix object
m = cast_sparse(td_tfidf, doc_id, word, tf_idf)

dim(m)
# [1] 321 3125