TFIDF Example

library(tm)
library(tmcn)
library(Matrix)
library(wordcloud)

Something before reading

  1. 這份 code 使用的 coding style 是遵照 Google R Coding Style
  2. 看到不懂的 function 多用 ?<function name> 去查詢
  3. 直接把每行 code 複製貼上是最沒有效率的學習方法,同學可以試著想想每行 code 背後代表的意義

Segmentation, Term Document Matrix

  1. 讀檔
  2. 把 [1] [2] … 這種維基百科的註解去掉(文本是從 wiki 複製下來的)
  3. 把 docs 轉成 corpus
  4. 斷詞
  5. 把斷詞結果轉換成 term-document matrix
  6. 偷看一下轉換後的結果
docs <- readLines("structuralism.txt")
docs <- gsub("\\[[0-9]+\\]", "", docs)
docs.corpus <- Corpus(VectorSource(docs))
docs.seg <- tm_map(docs.corpus, segmentCN)
docs.tdm <- TermDocumentMatrix(docs.seg, control = list())
inspect(docs.tdm)
## <<TermDocumentMatrix (terms: 435, documents: 14)>>
## Non-/sparse entries: 619/5471
## Sparsity           : 90%
## Maximal term length: 13
## Weighting          : term frequency (tf)
## Sample             :
##           Docs
## Terms      10 11 12 13 14 3 4 6 7 8
##   心理學    0  0  7  3  0 0 0 0 0 0
##   文化      0  0  0  0  0 4 1 0 0 0
##   文學      0  4  0  0  1 1 0 3 0 0
##   以及      1  0  3  0  1 2 0 0 0 0
##   故事      0  0  0  0  8 0 0 0 0 0
##   研究      1  2  2  3  0 1 1 1 1 1
##   科學      0  1  0  0  0 0 0 1 1 0
##   結構      6  1  0  0  6 4 2 0 1 1
##   結構主義  3  3  0  0  0 2 5 1 1 0
##   語言      0  0  0  0  0 0 0 0 3 1

TFIDF Counting

  1. 計算每個詞的 term frequency
  2. 定義計算 idf 的 function
  3. 計算 idf
  4. tfidf = tf * idf
  5. 看一下結果
docs.tf <- apply(as.matrix(docs.tdm), 2, function(doc) {doc / sum(doc)})
idf.function <- function(word_doc) { log2( (length(word_doc)+1) / nnzero(word_doc) ) }
docs.idf <- apply(docs.tdm, 1, idf.function)
docs.tfidf <- docs.tf * docs.idf
head(docs.tfidf)
##                Docs
## Terms                    1 2 3         4 5 6 7 8 9 10         11
##   structuralism 0.09767226 0 0 0.0000000 0 0 0 0 0  0 0.00000000
##   了解          0.09767226 0 0 0.0000000 0 0 0 0 0  0 0.00000000
##   二十<a5>      0.09767226 0 0 0.0000000 0 0 0 0 0  0 0.00000000
##   下半          0.09767226 0 0 0.0000000 0 0 0 0 0  0 0.00000000
##   大前提        0.09767226 0 0 0.0000000 0 0 0 0 0  0 0.00000000
##   之間          0.04767226 0 0 0.0312605 0 0 0 0 0  0 0.04434629
##                Docs
## Terms                   12 13 14
##   structuralism 0.00000000  0  0
##   了解          0.00000000  0  0
##   二十<a5>      0.00000000  0  0
##   下半          0.00000000  0  0
##   大前提        0.00000000  0  0
##   之間          0.02166921  0  0

Query of Words

  1. 定義查詢函數
  2. 查詢 “結構”, “人類學”, “整體” 三個詞在各篇文章的 tfidf 值
query.tfidf <- function(q){
  q.position <- which(rownames(docs.tfidf) %in% q)
  q.tfidf <- docs.tfidf[q.position, ]
  return (q.tfidf)
}
query.tfidf(c("結構", "人類學", "整體"))
##         Docs
## Terms             1 2          3          4 5         6          7
##   結構   0.04534453 0 0.04969264 0.02973412 0 0.0000000 0.01813781
##   整體   0.09767226 0 0.00000000 0.00000000 0 0.0000000 0.00000000
##   人類學 0.00000000 0 0.03180723 0.00000000 0 0.0504767 0.00000000
##         Docs
## Terms             8 9         10         11 12 13         14
##   結構   0.01929554 0 0.07663864 0.02109048  0  0 0.04572558
##   整體   0.00000000 0 0.00000000 0.00000000  0  0 0.00000000
##   人類學 0.00000000 0 0.06540643 0.00000000  0  0 0.00000000

Cosine Similiarity

  1. 定義「計算 x, y 兩向量 cosine 值」函數
  2. 計算 “各篇文章的 tfidf 向量”“第一篇文章 tfidf 向量” 的 cosine 值
cos <- function(x, y){
  return (x %*% y / sqrt(x %*% x * y %*% y))[1, 1]
}
# compare with first doc
docs.cos.sim <- apply(docs.tfidf, 2, cos, y = docs.tfidf[, 1])
docs.cos.sim
##           1           2           3           4           5           6 
## 1.000000000 0.031479857 0.071134636 0.032901470 0.144244508 0.022938287 
##           7           8           9          10          11          12 
## 0.073859797 0.012212186 0.029992818 0.031548444 0.052831432 0.010399877 
##          13          14 
## 0.005784062 0.032081762

Wordcloud

  1. 計算個詞彙的詞頻總和
  2. 單詞-詞頻對應存入 data frame
  3. 畫出文字雲
f <- sort(rowSums(docs.tfidf), decreasing = T)
docs.df <- data.frame(
  word = names(f),
  freq = f
)
wordcloud(docs.df$word, docs.df$freq, scale=c(5,0.1), colors=brewer.pal(8, "Dark2"))