本文涉及,使用R语言读取文本(从txt文件、http链接或是https链接),构建TermDocumentMatrix(统计每个term(单词或词组)在每个document中出现的频次),绘制wordcloud,并进行情感分析(Sentiment Analysis)。
关键字: R语言,文本挖掘,WordCloud,Sentiment Analysis
数据来源:
要挖掘的数据来自于:
美国布什总统出兵伊拉克前的演讲:
https://www.historyplace.com/speeches/bush-war.htm
positive words和negative words来自于:
https://www.cs.uic.edu/~liub/FBS/sentiment-analysis.html
1 文本读取
1.1 从文件中读取文本
使用如下方法读取文本数据(scan或者readLines)
setwd("E:/Project/tripping/R_Plot/Data")
textfile <- "GB.txt"
# 一行一行地读取文本数据
# 方法1:
text <- scan(textfile, character(0), sep='\n')
#方法2: text <- readLines(textfile)
就我实验结果来看,读取的结果是一样的,都是把一行字符串作为列表中的一个元素.
1.2 从网页中读取文本
使用如下代码从一个HTTP链接中读取文本,读取的结果与1.1相同(这里我把一个模块用if语句括起来了,是为了方便注释)
# --- 从网页读取文本
library(XML) # 应注意XML不支持HTTPS协议,HTTPS应先用RCurl中的getURL载入数据,或直接把HTTPS改为HTTP
# 处理HTTP
if (FALSE) {
textLocation <- URLencode("http://www.historyplace.com/speeches/bush-war.htm")
doc.html <- htmlTreeParse(textLocation, useInternal=TRUE)
text <- unlist(xpathApply(doc.html, '//p', xmlValue))
head(text, 3)
}
但要注意,HTTPS链接不适用于上面的xml方法,对于HTTPS,要么把链接里的HTTPS改成HTTP,然后用上面的xml方法,或者采用下面的RCurl的方法:
# 如果处理的是HTTPS,可如下处理:
if (TRUE) {
library(RCurl)
url <- "https://www.jianshu.com/p/48d758ce62b4"
web <- getURL(url)
doc<-htmlTreeParse(web,encoding="UTF-8", error=function(...){}, useInternalNodes = TRUE,trim=TRUE)
text <- unlist(xpathApply(doc, '//p', xmlValue))
head(text, 3) # 在text中,每个元素是一行字符串
}
2 绘制WordCloud
刚刚读取的文本数据,形成的是一个列表,列表里每个元素是一个字符串,代表文本中的一行。
接下来我们要绘制WordCloud,需要统计在整个文本中每个单词出现的次数,然后对于高频词的单词画的更大。
我们采用的方式是,先构建一个矩阵,其中第i行第j列的元素表示第i个单词在文本的第j行中出现的次数。
library("tm")
words.vec <- VectorSource(text)
words.corpus <- Corpus(words.vec)
# 第二个参数里的content_transformer貌似加不加都一样的,这里对数据进行预处理
words.corpus <- tm_map(words.corpus, content_transformer(tolower))
words.corpus <- tm_map(words.corpus, content_transformer(removePunctuation))
words.corpus <- tm_map(words.corpus, removeNumbers)
words.corpus <- tm_map(words.corpus, removeWords, stopwords("english")) # 删除stop words
tdm <- TermDocumentMatrix(words.corpus)
tdm
最后的tdm就是我们想要的矩阵,我们在输入tdm后,可以看到关于它的统计信息:
# 输出解读:
<<TermDocumentMatrix (terms: 578, documents: 37)>> #一共有578个term,37个documents(37行)
Non-/sparse entries: 934/20452 #在term-documents构成的578*37矩阵中,非空的只有934个元素
Sparsity : 96% #有96%的元素是空值
Maximal term length: 16 #一个term并非是一个单词,可能是一个词组,这里是最大term长度(16个字母)
Weighting : term frequency (tf)
接下来,我们只需要对每一行求和,即可得到每个单词在整个文本中出现的频次:
m <- as.matrix(tdm)
wordCounts <- rowSums(m)
wordCounts <- sort(wordCounts, decreasing = TRUE)
head(wordCounts)
这里的wordCounts即为所求,接下来,我们把wordCounts转化成DataFrame的格式,便于绘制WordCloud
cloudFrame <- data.frame(word=names(wordCounts), freq=wordCounts)
# -- 绘制wordcloud
library("wordcloud")
wordcloud(cloudFrame$word, cloudFrame$freq)
绘制的WordCloud如下图所示:
3 情感分析(Sentiment Analysis)
在情感分析中,我们的目标是分析这个文本是积极的(positive)还是消极的(negative)
在之前已经有一些研究收集了英语中的积极词汇和消极词汇:
https://www.cs.uic.edu/~liub/FBS/sentiment-analysis.html
我们只需要统计我们的目标文本中积极词汇所占的比例和消极词汇所占的比例,即可得到关于文本的积极评分和消极评分。
首先,读取积极词汇文件和消极词汇文件,和刚刚一样,这里我们也是按行读取的,不过在这两个文件里,每个单词一行,所以这样也没什么问题。要注意,文本的开头有些别的信息,要删掉。
# -- Sentiment Analysis
setwd("../Data/opinion-lexicon-English")
pos <- "positive-words.txt"
neg <- "negative-words.txt"
p <- scan(pos, character(0), sep="\n")
n <- scan(neg, character(0), sep="\n")
p <- p[-1:-29] # 文件的开头有些别的东西,要去掉
n <- n[-1:-30]
head(p, 10)
head(n, 10)
读取了积极单词和消极单词后,我们首先统计文本中积极单词的个数:
totalWords <- sum(wordCounts) # 总的单词数目
words <- names(wordCounts) # 都有哪些单词
matched <- match(words, p, nomatch=0) # 返回一个矩阵,表示words中的每一个单词是p中的第几个单词
mCounts <- wordCounts[which(matched != 0)]
length(mCounts) # 这篇文章中出现的positive的单词的种类个数(there were 40 unique positive words)
nPos <- sum(mCounts)
nPos # 共有58个positive words
然后用同样的方法统计消极单词的个数:
# -- 下面对negative words做同样的操作,统计unique negative words的个数和negative words的个数
matched <- match(words, n, nomatch = 0)
nCounts <- wordCounts[which(matched != 0)]
nNeg <- sum(nCounts)
length(nCounts)
nNeg
最后分别计算积极单词和消极单词所占的比率:
totalWords <- length(words)
ratioPos <- nPos / totalWords
ratioNeg <- nNeg / totalWords
ratioPos
ratioNeg
最后结果:
> ratioPos
0.100346
> ratioNeg
0.1020761