情感分析:从文本中提前作者情感意识的过程。
情感分析的难点:
- 受文化和人口统计学因素的影响;
- 情绪难以量化;
- 分析师或建模的偏见会破坏情感分析;
- 指定特征的情感分析更难。(如:对餐馆的评论——价格nice,但是食物一般。)
Plutchik 创建的情感分类系统,认为存在以下8种主要的情绪:
- anger 愤怒
- fear 恐惧
- sadness 悲伤
- disgust 厌恶
- surprise 惊讶
- anticipation 期待
- trust 信任
- joy 快乐
由于惊讶 ∑( 口 ||,可能是正面的“我中了五百万”,也可能是负面的“乔丹坠机去世了”。因而分析文本的极性polarity 比分析文本的情绪sentiment更容易。
4.2 情绪评分
假设您想在波士顿有一间公寓,希望通过http://Airbnb.com服务进行出租。您希望通过租用公寓赚一些额外的钱,但是要确保公寓具有良好的出租质量。
1)定义问题和特定目标。在正面或负面评论中列出了哪些公寓质量?
2)确定需要收集的文本。 入住之后,Airbnb的房客可以发表有关该公寓的评论。 这些评论是公开的,可以辅助新租户作决定。
3)整理文字。 语料库包含1000个随机选择的波士顿Airbnb列表。 您将清理并整理评论频率矩阵。
4)提取特征。 一旦组织好TDM,您将需要计算各种情绪和极性得分。
5)分析。 情感和极性得分将用于评论分类。
4.2.1 主观性词典
qdap软件包使用的主观性词典来自芝加哥伊利诺伊大学的刘冰进行的研究。该词典稍小,包含大约6800个带标签的单词,但它是基于经过严格审查的学术研究得出的。
例:
Sentiment analysis in R is good yet challenging.
Sentiment analysis in python is not good.
Sentiment analysis in R is very good.
❓为什么6800词的主观性词典有效?
Zip定律认为,文档中任何词出现的频率与其排名次序成反比。
最小努力原则,尽管存在数以万计的单词,但日常交流常用的只有几千个不同的词语。
4.2.2 qdap's正负面词的评分
1)polarity 函数可在主观性词典中扫描正面词汇和负面词汇。
2)一旦找到了极性词,该函数就会创建一组词汇,包括前四个词和后两个词。
3)在单词簇中,中性词被计为0。 构成聚类基础的正词和负词分别计为1和-1。 因此,其余的非中性和非极性词被视为程度副词。 这些程度副词被赋予权重,以放大或缩小原始的极性词。 默认值为0.8。 因此,正面词汇+0.8,而负面词汇-0.8。
4)将单词簇中的所有值相加,生成极性总和。
5)然后将总和除以段落中所有单词的平方根,以衡量关键字的密度。
1.8/sqrt(7)≈0.68
#Airbnb关于波士顿公寓或房子的评论
options(stringsAsFactors = FALSE)
Sys.setlocale(category = "LC_ALL", locale = "C")
library(tm)
library(qdap)
library(wordcloud)
library(ggplot2)
library(ggthemes)
setwd("E:/Rdata/text_mining-master/")
bos.airbnb<-read.csv('bos_airbnb_1k.csv')
bos.pol<-polarity(as.character(bos.airbnb$comments))
ggplot(bos.pol$all,aes(x=polarity,y=..density..))+
theme_gdocs()+
geom_histogram(binwidth=0.25,
fill="darkred",
colour="grey60",
size=0.2)+
geom_density(size=0.75)
以0.9为中心而非0,说明评论至少有一个积极的词
反应偏差会导致“分数膨胀”。
TF-IDF
TFIDF是词频(TF)和逆文档频率(IDF)的乘积。从常识的角度来看,如果一个词汇经常出现,则认为它很重要。但是,如果它出现在所有文档中,那么它可能就没有那么重要的信息了。
词频(TF)计算文档中某个词汇出现的次数。因为文档很长,所以单词出现的频率更高,所以我们可以通过除以文档长度来对频率进行标准化。
TF =(term occurrences in a document)/(total unique terms in the document)
逆文档频率(IDF)
IDF =log(total document in corpus/number of documents with term t in it)
TF-IDF=TF*IDF
极性词云
bos.airbnb$polarity<-scale(bos.pol$all$polarity)
pos.comments<-subset(bos.airbnb$comments,bos.airbnb$polarity>0)
neg.comments<-subset(bos.airbnb$comments,bos.airbnb$polarity<0)
pos.terms<-paste(pos.comments,collapse = " ")
neg.terms<-paste(neg.comments,collapse = " ")
all.corpus<-VCorpus(VectorSource(c(pos.terms,neg.terms)))
all.tdm<-TermDocumentMatrix(all.corpus,
control=list(weighting=weightTfIdf,
removePunctuation=TRUE,
stopwords=stopwords(kind="en")))
all.tdm.m<-as.matrix(all.tdm)
colnames(all.tdm.m)<-c("positive","negative")
comparison.cloud(all.tdm.m,max.words = 300,
colors = c("darkgreen","darkred"))
在Airbnb的好评中,有很多人的名字,比如Alex, Becky和Erik等等。可以推测,要想拥有一个积极的Airbnb体验,主人和客人之间必须有直接的互动。相比之下,自动化的帖子和肮脏的房间会导致负面的评论。
4.3 表情符号
4.3.1 基于R的表情符号
print('\U2764')
print('\U263B')
patterns<-c('\U2764','\U263B')
replacements<-c('love','happy')
mgsub(patterns,replacements,'I \U2764 you')
4.3.2 基于标点的表情符号
data("emoticon")
emoticon.df<-emoticon
#添加自定义表情
meaning<-c('troubled face','crying')
emoticon<-c('(>_<)','Q_Q')
new.emotes<-data.frame(meaning,emoticon)
emoticon.df<-rbind(emoticon.df,new.emotes)
tail(emoticon.df)
text<-'Text mining is so much fun :-D. Other tm books make me Q_Q because
they have academic examples!'
mgsub(emoticon.df[,2],emoticon.df[,1],text)
虽然替换后的句子的语法并不准确,但标点符号的情感意义用词语代替了,避免有价值的信息丢失。因此,在删除标点符号之前执行这种替换非常重要。
4.3.4 Emoji
tweet='Manually dealing with emoji is hard, makes me . Plus doing it one by one makes me . Luckily qdap’s mgsub can help '
print(tweet)
#手动创建emoji语义表
emoji.replacements<-c('Speak no evil monkey',
'Tears of joy face',
'Unamused face',
'Smiley with heart eyes',
'Smiley with smiling eyes',
'OK hand',
'Face blowing a kiss',
'Thumbs down',
'Loudly crying face',
'Grinning Face with Smiling Eyes',
'Flushed Face',
'Thumbs up',
'Clapping hands or person raising both hands in celebration',
'Person with folded hands or praying',
'Pile of "Poo"',
'Face with stuck out tongue and winking eye',
'Smiling face with open mouth and smiling eyes',
'Sleeping face',
'Hushed face',
'Neutral Face')
emoji.patterns<-c('\U1F64A','\U1F602','\U1F612','\U1F60D','\U1F60A',
'\U1F44C','\U1F618','\U1F44E','\U1F62D','\U1F601',
'\U1F633','\U1F44D','\U1F64C','\U1F64F','\U1F4A9',
'\U1F61C','\U1F604','\U1F634','\U162F','\U1F610')
recode.tweet<-mgsub(emoji.patterns,emoji.replacements,
tweet)
recode.tweet
recode.airbnb<-mgsub(emoji.patterns,emoji.replacements,bos.airbnb$comments)
4.4 Rstem和sentiment 包进行情感分析
Index of /src/contrib/Archive/Rstem
https://cran.r-project.org/src/contrib/Archive/sentiment/
R4.0.0对应的Rtools安装及环境变量配置!
https://zhuanlan.zhihu.com/p/44914286?utm_source=wechat_session
sentiment包中的emotions数据集最后6行
library(Rstem)
library(sentiment)
setwd("E:/Rdata/text_mining-master/")
emotions<-read.csv("emotions.csv")
tail(emotions)
#贝叶斯进行情感分类
emotions.df<-classify_emotion(bos.airbnb$comments[-c(127,181,287,309,541,568,610,652,765,876)])
emotions.df<-as.data.frame(emotions.df)
head(emotions.df)
emotions.df<-as.data.frame(emotions.df)
head(emotions.df)
ggplot(emotions.df,aes(x=BEST_FIT))+
geom_bar(aes(y=..count..,fill=BEST_FIT))+
theme_gdocs()+
theme(legend.position = "none")+
labs(x="emotion categories",
y="Bos Airbnb Reviews")
4.5 tidytext进行情感分析
#Bing词典 仅用于区分正负面词汇 6788
#AFINN词典 情感评分 2476
#NRC词典 细分情感 13901
library(tidytext)
library(textdata)
afinn<-get_sentiments(lexicon = "afinn")
nrc<-get_sentiments(lexicon = "nrc")
bing<-get_sentiments(lexicon = "bing")
AFINN Bing nrc字典的对比
library(dplyr)
library(tm)
library(tidyr)
library(ggplot2)
library(ggthemes)
#绿野仙踪文本语料
setwd("E:/Rdata/text_mining-master/")
oz<-readLines("Wizard_Of_Oz.txt")
oz.corp<-VCorpus(VectorSource(oz))
oz.corp<-clean.corpus(oz.corp)
oz.dtm<-DocumentTermMatrix(oz.corp)
oz.tidy<-tidy(oz.dtm)
oz.tidy[6810:6815,]
colnames(oz.tidy)<-c("line_number",
"word","count")
oz.tidy$line_number<-as.numeric(oz.tidy$line_number)
#将绿野仙踪词表连接nrc的joy词表
joy.words<-inner_join(oz.tidy,nrc[nrc$sentiment=='joy',1])
joy.words<-count(joy.words,word)
head(joy.words)
oz.sentiment<-inner_join(oz.tidy,bing)
oz.sentiment<-count(oz.sentiment,sentiment,index=line_number)
#转化为宽表
oz.sentiment<-spread(oz.sentiment,sentiment,n,fill=0)
oz.sentiment
oz.sentiment$polarity<-oz.sentiment$positive-oz.sentiment$negative
oz.sentiment$pos<-ifelse(oz.sentiment$polarity>=0,"pos","neg")
ggplot(oz.sentiment,aes(x=index,y=polarity,fill=pos))+
geom_bar(stat="identity",position = "identity",width=1)+
theme_gdocs()
ggplot(oz.sentiment,aes(x=index,y=polarity))+
stat_smooth()+theme_gdocs()
可以看出绿野仙踪的行文基调
绿野仙踪:主人公桃乐茜(Dorothy)卷入了一场风暴,被带到了一个充满女巫和生物的奇幻国度。为了回家,她开始了一段旅程,沿途结识了一些朋友,并向一位强大的巫师寻求帮助。平滑的极性线说明了她的旅程。
作者用相当中性的语言吸引读者到这个故事中。大约在2700行,语言变得很消极,然后不断上升到积极的故事结尾。没有读过这本书,也可以推断出这个故事有一个happy ending。