R包之tm:文本挖掘包
本文參考文檔:
- tm的使用指南 : http://mirror.bjtu.edu.cn/cran/web/packages/tm/vignettes/tm.pdf
- tm手冊 : http://mirror.bjtu.edu.cn/cran/web/packages/tm/tm.pdf
簡介
tm 即text mining
,是用來做文本挖掘的一個R包,是一個進行自然語言處理的基礎包。它提供了一些做文本挖掘的基礎設施,比如數據輸入,文集處理,預處理,元數據管理,創建單詞-文本矩陣。
安裝
該包的安裝方法是install.packages("tm")
數據輸入—文集(corpus)
文集代表一個文檔集,通常一個文件就是一個文檔。多個文檔構成一個文集。文集是一個抽象的概念,具體的實現方式有幾種,一個是VCorpus(Volatile Corpus),這種文集完全存儲在內存中。故而不能很大。這個文集的創建方法爲 VCorpus(x,readerControl)
。
另一個實現是PCorpus1 (Permanent
Corpus),這種實現方式下,內存中只是存儲文檔的指針,真正的文檔存儲在磁盤上(文件或者數據庫)。
文集創建的第一個參數x
必須是一個Source
對象,tm提供了一些預定義的source。比如DirSrouce
,VectorSource
,DataframeSource
等,它們分別用來處理一個目錄,一個向量(每個元素爲一個文檔),和數據框結構(比如csv)的文檔。可以利用getSources()
來得到所有可用的source,用戶也可以建立自己的source。
文集創建的第二個參數是readerControl
,這個參數必需是一個列表,其中包含組件reader
和language
。其中:
-
reader
負責創建一個文檔。而文檔的來源是source
傳遞過來的每個元素。下面是簡單的處理流程source --> elements ---> reader ---> document
其中,tm包中有幾種reader,比如readPlain(),readPDF(),readDOC()等等,可以利用getReaders()來獲得所有可以的
reader
。每個source都對應有一個缺省的reader,比如DirSource的reader就是讀入文件,把文件的內容作爲字符串。這個reader是可以被替換的。
-
language
指明文本的語言
因此,對於包tm目錄下的texts/txt目錄下的文本文件可以這樣做成一個文集
library(tm)
#> Loading required package: NLP
txt <- system.file("texts","txt",package="tm")
vid = VCorpus(DirSource(txt, encoding = "UTF-8"), readerControl = list(language="lat"))
vid
#> <<VCorpus>>
#> Metadata: corpus specific: 0, document level (indexed): 0
#> Content: documents: 5
最簡單的VectorSource,可以用來最簡單的測試,比如
docs <- c("hello doc1", "hello doc2")
VCorpus(VectorSource(docs))
#> <<VCorpus>>
#> Metadata: corpus specific: 0, document level (indexed): 0
#> Content: documents: 2
最後可以看一個指定外部reader的例子
reut21578 <- system.file("texts", "crude", package = "tm")
reuters <- VCorpus(DirSource(reut21578),
readerControl = list(reader = readReut21578XMLasPlain))
數據輸出
文集中的數據可以直接輸出,比如前面讀入的文集,可以這樣寫到磁盤上去writeCorpus(vid)
,
每個文檔會生成一個文件
查看語料庫(corpora)
print(vid)只是給出了最簡單的信息,要查看更多的信息,可使用inspect()
查看某幾條信息
inspect(vid[1:2])
#> <<VCorpus>>
#> Metadata: corpus specific: 0, document level (indexed): 0
#> Content: documents: 2
#>
#> [[1]]
#> <<PlainTextDocument>>
#> Metadata: 7
#> Content: chars: 676
#>
#> [[2]]
#> <<PlainTextDocument>>
#> Metadata: 7
#> Content: chars: 700
查看單個文檔元數據
meta(vid[[2]])
#> author : character(0)
#> datetimestamp: 2016-03-09 01:45:14
#> description : character(0)
#> heading : character(0)
#> id : ovid_2.txt
#> language : lat
#> origin : character(0)
查看單個文檔內容
as.character(vid[[2]])
#> [1] " quas Hector sensurus erat, poscente magistro"
#> [2] " verberibus iussas praebuit ille manus."
#> [3] " Aeacidae Chiron, ego sum praeceptor Amoris:"
#> [4] " saevus uterque puer, natus uterque dea."
#> [5] " sed tamen et tauri cervix oneratur aratro,"
#> [6] ""
#> [7] " frenaque magnanimi dente teruntur equi;"
#> [8] " et mihi cedet Amor, quamvis mea vulneret arcu"
#> [9] " pectora, iactatas excutiatque faces."
#> [10] " quo me fixit Amor, quo me violentius ussit,"
#> [11] " hoc melior facti vulneris ultor ero:"
#> [12] ""
#> [13] " non ego, Phoebe, datas a te mihi mentiar artes,"
#> [14] " nec nos aëriae voce monemur avis,"
#> [15] " nec mihi sunt visae Clio Cliusque sorores"
#> [16] " servanti pecudes vallibus, Ascra, tuis:"
#> [17] " usus opus movet hoc: vati parete perito;"
查看多個文檔內容
lapply(vid[1:2],as.character)
#> $ovid_1.txt
#> [1] " Si quis in hoc artem populo non novit amandi,"
#> [2] " hoc legat et lecto carmine doctus amet."
#> [3] " arte citae veloque rates remoque moventur,"
#> [4] " arte leves currus: arte regendus amor."
#> [5] ""
#> [6] " curribus Automedon lentisque erat aptus habenis,"
#> [7] " Tiphys in Haemonia puppe magister erat:"
#> [8] " me Venus artificem tenero praefecit Amori;"
#> [9] " Tiphys et Automedon dicar Amoris ego."
#> [10] " ille quidem ferus est et qui mihi saepe repugnet:"
#> [11] ""
#> [12] " sed puer est, aetas mollis et apta regi."
#> [13] " Phillyrides puerum cithara perfecit Achillem,"
#> [14] " atque animos placida contudit arte feros."
#> [15] " qui totiens socios, totiens exterruit hostes,"
#> [16] " creditur annosum pertimuisse senem."
#>
#> $ovid_2.txt
#> [1] " quas Hector sensurus erat, poscente magistro"
#> [2] " verberibus iussas praebuit ille manus."
#> [3] " Aeacidae Chiron, ego sum praeceptor Amoris:"
#> [4] " saevus uterque puer, natus uterque dea."
#> [5] " sed tamen et tauri cervix oneratur aratro,"
#> [6] ""
#> [7] " frenaque magnanimi dente teruntur equi;"
#> [8] " et mihi cedet Amor, quamvis mea vulneret arcu"
#> [9] " pectora, iactatas excutiatque faces."
#> [10] " quo me fixit Amor, quo me violentius ussit,"
#> [11] " hoc melior facti vulneris ultor ero:"
#> [12] ""
#> [13] " non ego, Phoebe, datas a te mihi mentiar artes,"
#> [14] " nec nos aëriae voce monemur avis,"
#> [15] " nec mihi sunt visae Clio Cliusque sorores"
#> [16] " servanti pecudes vallibus, Ascra, tuis:"
#> [17] " usus opus movet hoc: vati parete perito;"
變換(transformation)
變換指通過tm_map函數來對文集中所有的文檔作用一個函數的過程。比如停用詞剔除等。每個變換隻是作用在一個文檔 上,tm_map來把它作用到所有的文檔。
比如
- 剔除多餘的空白
tm_map(vid,stripWhitespace)
-
轉換爲小寫
tm_map(vid,content_transformer(tolower))
其中的
content_transformer
是一個修改文檔內容的方便的函數,tolower可以是任何其他的字符串修改函數 -
移除停用詞
reuters <- tm_map(reuters, removeWords, stopwords("english"))
過濾器
過濾器可以移除不感興趣的(或者感興趣的)文檔。tm提供了tm_filter函數,這個函數的原型爲
tm_filter(x, FUN,...)
tm_index(x,FUN,...)
其中的FUN函數出入爲一片文檔,輸出爲一個bool值。表示是否接受該文檔。第二個版本tm_index
只是返回滿足條件的index,前者返回一個文集,下面是一個使用示例:
data("crude")
# Full-text search
tm_filter(crude, FUN = function(x) any(grep("co[m]?pany", content(x))))
元數據管理
元數據分爲兩個層次,一個是文集級別的元數據,一個是文檔級別的元數據。要獲得元數據,最簡單的是 使用meta()
函數。每篇文檔,有些預定義的元數據(比如author
),但是每篇文檔也可以添加自定義的
元數據標籤。如下所示,分別用DublinCore
和meta
函數來修改一個文檔的元數據
data(crude)
DublinCore(crude[[1]], "Creator") <- "Ano Nymous"
meta(crude[[1]])
#> author : Ano Nymous
#> datetimestamp: 1987-02-26 17:00:56
#> description :
#> heading : DIAMOND SHAMROCK (DIA) CUTS CRUDE PRICES
#> id : 127
#> language : en
#> origin : Reuters-21578 XML
#> topics : YES
#> lewissplit : TRAIN
#> cgisplit : TRAINING-SET
#> oldid : 5670
#> places : usa
#> people : character(0)
#> orgs : character(0)
#> exchanges : character(0)
meta(crude[[1]], "author") <- "Jiang Hang"
meta(crude[[1]])
#> author : Jiang Hang
#> datetimestamp: 1987-02-26 17:00:56
#> description :
#> heading : DIAMOND SHAMROCK (DIA) CUTS CRUDE PRICES
#> id : 127
#> language : en
#> origin : Reuters-21578 XML
#> topics : YES
#> lewissplit : TRAIN
#> cgisplit : TRAINING-SET
#> oldid : 5670
#> places : usa
#> people : character(0)
#> orgs : character(0)
#> exchanges : character(0)
下面是修改文集級別元數據的列子
meta(crude, tag = "url", type = "corpus") <- "http://www.bagualu.net"
meta(crude, type = "corpus")
#> $url
#> [1] "http://www.bagualu.net"
#>
#> attr(,"class")
#> [1] "CorpusMeta"
每個元數據的數據可以是dataframe結構的。
標準操作和函數
標準的操作符[
,[[
,[<-
,[[<-
,c()
,lapply()
可以直接作用在corpora(語料庫)上
創建文檔-單詞矩陣
tm中,函數TermDocumentMatrix
和DocumentTermMatrix
可直接創建文檔-單詞矩陣,這二者的卻別
在於矩陣的行是文檔還是單詞
dtm <- DocumentTermMatrix(reuters)
inspect(dtm[5:10, 740:743])
#> <<DocumentTermMatrix (documents: 6, terms: 4)>>
#> Non-/sparse entries: 2/22
#> Sparsity : 92%
#> Maximal term length: 9
#> Weighting : term frequency (tf)
#>
#> Terms
#> Docs needs. negative negotiate neither
#> 211 0 0 0 0
#> 236 0 0 0 0
#> 237 1 0 0 0
#> 242 0 0 0 0
#> 246 0 0 0 0
#> 248 0 1 0 0
文檔-單詞矩陣的操作
有了矩陣以後,可以有很多R函數可以作用於它,但是tm包提供了一些常用的函數,比如你想找到那些至少 出現了10次的單詞,使用findFreqTerms()
函數
findFreqTerms(dtm,10)
#> [1] "about" "and" "are" "bpd" "but"
#> [6] "crude" "dlrs" "for" "from" "government"
#> [11] "has" "its" "kuwait" "last" "market"
#> [16] "mln" "new" "not" "official" "oil"
#> [21] "one" "opec" "pct" "price" "prices"
#> [26] "reuter" "said" "said." "saudi" "sheikh"
#> [31] "that" "the" "they" "u.s." "was"
#> [36] "were" "will" "with" "would"
又比如,要找到與單詞opec
有0.8以上相關性的單詞,使用findAssocs()
findAssocs(dtm,"opec",0.8)
#> $opec
#> meeting emergency oil 15.8 analysts buyers above
#> 0.88 0.87 0.87 0.85 0.85 0.83 0.82
#> said ability
#> 0.82 0.80
文檔-單詞矩陣通常很大。tm提供了移除稀疏元素的函數
inspect(removeSparseTerms(dtm,0.4))
#> <<DocumentTermMatrix (documents: 20, terms: 8)>>
#> Non-/sparse entries: 140/20
#> Sparsity : 12%
#> Maximal term length: 6
#> Weighting : term frequency (tf)
#>
#> Terms
#> Docs and for its oil reuter said the was
#> 127 1 2 3 5 1 1 5 1
#> 144 9 5 6 11 1 9 17 1
#> 191 0 2 1 2 1 1 4 0
#> 194 1 2 1 1 1 1 4 1
#> 211 2 2 1 1 1 3 8 0
#> 236 7 4 8 7 1 6 15 7
#> 237 11 3 3 3 1 0 30 2
#> 242 3 1 0 3 1 3 6 1
#> 246 9 6 3 4 1 4 18 2
#> 248 6 2 2 9 1 5 27 4
#> 273 5 4 0 5 1 5 21 1
#> 349 2 0 0 3 1 1 5 0
#> 352 3 0 2 5 1 1 7 1
#> 353 1 2 2 4 1 1 4 3
#> 368 1 0 1 3 1 2 11 2
#> 489 5 4 2 4 1 2 8 0
#> 502 6 5 2 4 1 2 13 0
#> 543 0 3 2 2 1 2 5 1
#> 704 5 3 1 3 1 3 21 0
#> 708 0 0 0 1 1 0 0 1
字典
字典是字符串的集合,通常用一個字符串向量表示,可以在DocumentTermMatrix
函數中指定一個
字典,這樣生成的矩陣中,就只有字典中出現的詞語,不在字典中的詞語不會出現在文檔單詞矩陣中 如下所示
inspect(DocumentTermMatrix(reuters,
list(dictionary = c("prices", "crude", "oil"))))
#> <<DocumentTermMatrix (documents: 20, terms: 3)>>
#> Non-/sparse entries: 41/19
#> Sparsity : 32%
#> Maximal term length: 6
#> Weighting : term frequency (tf)
#>
#> Terms
#> Docs crude oil prices
#> 127 2 5 3
#> 144 0 11 3
#> 191 2 2 0
#> 194 3 1 0
#> 211 0 1 0
#> 236 1 7 2
#> 237 0 3 0
#> 242 0 3 1
#> 246 0 4 0
#> 248 0 9 7
#> 273 5 5 4
#> 349 2 3 0
#> 352 0 5 4
#> 353 2 4 1
#> 368 0 3 0
#> 489 0 4 2
#> 502 0 4 2
#> 543 2 2 2
#> 704 0 3 2
#> 708 1 1 0
關於中文支持
利用缺省的reader讀入文檔時,如果文檔爲中文,tm還是會以空格作爲單詞的分割符。這樣基本對中文不適用。爲了能夠處理中文,需要 圖換掉缺省的reader。新的reader應該讀入文章,並進行分詞,然後將分詞的結果保存爲一個新的文件,該文件中,各中文單詞以空格隔開 。然後再利用tm的缺省reader進行處理就可以了。關於自定義reader的格式,詳見這篇博客
rmmseg4j 以前在cran中的,後來被移除了,原因是不符合java的源碼政策 ( Archived on 2014-08-30 as does not comply with policy on Java sources. )
-
對於PCorpus而言,第三個參數
dbControl
必須是一個列表,它具有組件dbName
,dbType
(這個dbType必須是filehash
包支持的數據庫類型)。這裏不做詳細討論。↩