有什么算法可以挖掘出词典中挖掘的同义词词

情感分析:基于词典的分析方法 | 数据之声
情感分析:基于词典的分析方法
&>>&&>>&情感分析:基于词典的分析方法
1 次,平均分
所有的数据挖掘方法和思路基本上很久之前都已经形成了,只是现代计算机的普及让这些创新的想法拥有了看似可行的技术支撑,给了他们起死回生的机会,从历史的故纸堆里摇身一变,成了高大上的现代挖掘感念。的历史也可以追溯到1950年,没错你真的没有看错,那时的用于研究论文评述的态度。如今,广阔的社交媒体为提供了用武之地,文本数据源源不断地从微博、博客、微信、社交平台、新闻评论、百度知道等等交互平台抓取下来,而被广泛的应用于挖掘互联网上的主观信息。
从基础上讲,情感分析能够帮助企业掌握文本所表达的情感倾向:正向、负向、还是中性,给予企业分析特定人群特别是消费者对某一固定话题、产品、方法持有的态度。企业可以使用挖掘出来的情感信息寻找新的营销机会,改善营销方式,针对人群采取精准营销等等,美国总统奥观海团队就使用情感分析方法监控大众对其政策的态度和反应。
##1.情感分析及其应用:这里是老生常谈
在企业内,所有的数据挖掘工作归根结底要解决业务问题,这一点认识的越深刻越能脚踏实地的解决问题,而不是制造虚假问题然后解决或者是答非所问,结果不能对问题实现有效的干预。
###1.1.情感分析的用途
情感分析也一样要寻找自己的业务用途。当然情感分析有很多用途,网络社交越来越火,名博或意见领袖越来越多,可以对商品和服务打分、评价的站点更是如雨后春笋,用户的评价和建议可以全网传播,这些数据毫无疑问是精准营销的动力来源。企业也可以根据情感分析建立自己的数字形象,识别新的市场机会,做好市场细分,进而推动产品成功上市,但抓住这些评论的价值部分也是企业的巨大挑战。上述正是情感分析的应用背景,但其主要用途包括:
#####监控社交媒体
社交媒体监控可以非常有效的管理品牌的影响力或识别对品牌有影响力的意见领袖,比如你想知道自己品牌负面评论的来源,你可以监控50个行业意见领袖的微博、博客,分析他们微博或文章下面的用户评论,从而确定谁会引导负向评论,之后就可以和这些意见领袖合作,从而转化他们的负面影响。
#####公共关系
情感分析同时能帮助企业改善或重塑公共关系策略,例如情感分析可以探索销售热点和行业的流行趋势,同样就像上一步表述的一样,通过情感分析找到社会上对品牌有好感的意见领袖,建立并维持合作的关系,这种方式显然是一种行之有效的公共关系管理策略。
#####市场营销
情感分析正在逐步替代传统市场研究的内容,比如情感需求调研、使用习惯及态度研究等等,例如,三星想知道客户对他们的新机型的评价,在社交媒体和数据挖掘兴起之前他们只能通过市场调研的方式解决。而数据分析则可以抓取消费者在各大消费网站如亚马孙、京东、天猫、社交媒体留下的评论数据,分析这些文本内容。从而获知消费者对某个新机型的情感倾向,或者获得消费者对某个新机型的了解程度以及消费者对机型的哪些属性比较赞赏而对另外某些方面并不感冒。
#####政策分析
分析微博上的评论可以非常准确的了解大众对政策的理解和情感倾向,慕尼黑大学的研究表明推特上的信息能够非常准确的反映选民的政治倾向,通过分析2009年德国大选期间选民涉及政党和政客的10万条推特,结论是推特的信息能够预示大选的结果,其准确性不亚于传统的民意调研。
#####数据挖掘
情感分析作为一种数据挖掘的方式,可以用于采集竞争对手的竞争优势,企业可以轻易地跟踪社交媒体的情感倾向和社交媒体对竞争对手的情感倾向,了解消费者对竞争品牌的印象及对其产品的情感倾向,这是非常优秀的竞争优势分析策略。更重要的是,情感分析的结果和情感指数还可以作为变量应用到其他数据挖掘项目,例如预测用户流失的概率时就可以关联用户的情感指数作为变量。
###1.2.情感分析的方法论
受制于人们表达态度的方式异常复杂,情感分析面临很多挑战,简单的方法难以识别真正的情感倾向。但常见的分析方法大致可以梳理为两个常见的方向:基于词典的情感分析法和基于监督算法的情感分析方法。
####1.2.1.基于词典的情感分析法
基于词典的情感分析法起源于基于语法规则的文本分析,方法比较简单纯朴,首先需要具有语法敏感性的专业人士构建情感分析的词典:正向和负向情感词典,即将某语言中用于表达情感的词汇分为两个类别,然后比对文本中正负情感词的个数之类的方法,评估文本的情感倾向,这种方法非常容易理解。
情感词也分轻重缓急,比如喜欢和爱虽然都是正向,但其程度上不一样,因此根据语言专家的分析,给予情感词不同的情感级别或权重算是对上述分析方法的改进,毫无疑问种方法包含一定的语法分析的成分,大家不要忘了谷歌翻译的早期版本就是基于语法的方式,其效果可见一斑。
####1.2.2.基于监督算法的情感分析方法
基于监督算法的情感分析过程首先制作一个规模庞大的训练集,由人工识别文本的正负向,然后通过机器学习或算法(SVM、随机森林、朴素贝叶斯等等)等方式训练模型,得出模型后再用来识别新文本的情感倾向,比较像垃圾邮件的分类方法,首先精挑一些垃圾邮件和正常邮件,让模型学习,然后再将模型用于垃圾邮件的分选。
####1.2.3.一种准确率几达87%的新方法
本书介绍一种新方法,准确率高于常见的分析方式,而且仍有提升空间。
####1.2.4.情感分析一些积累知识和方向
首先我们应感感谢一些前辈的积累工作,就词典方面中国知网的研究者整理出了知网情感hownet词典,更新地址:/html/c_bulletin_2007.htm,另外还有台湾大学整理的台湾大学情感NTUSD,还有富士通公司的情感词典、程度词典等等。
除了情感词典以外,情感分析语料库方面也有不错的工作积累,比如清华大学李军标注的情感分析语料库等等,以及Tang先生等人另辟蹊径根据表情符号标注微博情感语料库类别的方法。
以上都是非常优秀的工作,本书将大量采用前人的研究成果,但站在巨人的肩上能不能看得更远就另是一说啦。基于多年从事文本挖掘和自然语言分析工作的经验,在情感分析方案设计和评价时我觉得应该考虑一下几点:
1.是否需要词典
2.是否具有跨行业分析的功能
3.使用什么算法,是分类任务还是分值预测,这关系到情感分析的细腻细腻度问题
4.是否需要使用规则及其与程序速度的平衡关系
5.是否关注情感归属问题,即主体词和情感的归属关系,是整体层次还是单个文本的个体层次
6.准确率和速度
1.是否需要词典2.是否具有跨行业分析的功能3.使用什么算法,是分类任务还是分值预测,这关系到情感分析的细腻细腻度问题4.是否需要使用规则及其与程序速度的平衡关系5.是否关注情感归属问题,即主体词和情感的归属关系,是整体层次还是单个文本的个体层次6.准确率和速度
以上算是情感分析的几个基本问题,本章尽量从这些方面探索实现。
##2.文本分析的基本武器:工欲善其事
工欲善其事,必先利其器,和python都是数据挖掘的利器,虽然我们选择了使用R做情感分析,但我个人认为python更具后发优势,如果你搜索自然语言处理的话,会发现很多内容和python有关,至于其他方面的比较,个人认为没有必要,只看这一点就ok了。
###2.1.Rjava包配置
既然选择了R语言,就需要将R语言打造一番,首先RJava包需要安装,它是R语言和Java的通信接口,允许在R中直接调用Java的对象和方法,恰恰能满足很多包的需要,比如Rwordseg。
Linux环境下只要R与Java的版本对应即可使用常规方法安装RJava,而windows环境下就比较麻烦,首先要安装java,去oracle官网下载JDK版安装包,注意,这里是jdk不是jre,是64位还是32位要和R版本相同。
然后需要安装Rtools,下载地址/bin/windows/Rtools/,当然要选择和R匹配的Rtools。
完成以上工作以后需要配置环境,win7右键我的电脑——高级系统设置——环境变量,在环境变量中分别新建或添加相应的环境路径。
第一个classpath,新建classpath添加下面代码
######classpath
.;%JAVA_HOME%\lib\dt.%JAVA_HOME%\lib\tools.C:\Program Files\R\R-3.2.1\library\rJava\jri
.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;C:\Program Files\R\R-3.2.1\library\rJava\jri
这里最后一个值有时需要指定到jri的版本是x64还是i386,如果是64,那么需要C:\Program Files\R\R-3.2.1\library\rJava\jri\x64,有时又不需要这么指定,真是一件神奇的事情。
第二个JAVA_HOME,新建,然后把下面的内容添加进去
######JAVA_HOME
C:\Program Files\Java\jdk1.8.0_51
C:\Program Files\Java\jdk1.8.0_51
第三个Path,新建path,注意一下所有的软件都要区分i386或x64
######Path
C:\Rtools\C:\Rtools\gcc-4.6.3\%JAVA_HOME%\%JAVA_HOME%\jre\C:\Program Files\R\R-3.2.1\bin\i386;C:\Program Files\R\R-3.2.1\bin\x64;C:\Program Files\Java\jdk1.8.0_51\jre\bin\server
C:\Rtools\bin;C:\Rtools\gcc-4.6.3\bin;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;C:\Program Files\R\R-3.2.1\bin\i386;C:\Program Files\R\R-3.2.1\bin\x64;C:\Program Files\Java\jdk1.8.0_51\jre\bin\server
第四个R_HOME,添加
######R_HOME
C:\Program Files\R\R-3.2.1
C:\Program Files\R\R-3.2.1
以上所有路径均是你的软件安装路径,其他不需要修改。
######安装rJava包
if (!suppressWarnings(require("rJava"))) {
install.packages("rJava")
require("rJava")
s &- .jnew("java/lang/String", "Hello World!")
#[1] "Java-Object{Hello World!}"
if (!suppressWarnings(require("rJava"))) {install.packages("rJava")require("rJava")}.jinit()s &- .jnew("java/lang/String", "Hello World!")s#[1] "Java-Object{Hello World!}"
如果s正常返回"Java-Object{Hello World!}"则RJava已经成功了。只有RJava配置成功了,Rwordseg安装才可能成功,前者是后者的依赖包。
###2.2.Rwordseg包安装
如果返回不正常说明你的RJava还没安装好,需要重新安装,Rwordseg是我接触最早的中文分词包,所以情有独钟,并不是因为它多么优秀,而是觉得分词是文本分析的基础工作,就其准确性而言,各种方法之间实在没有实质性的差别,因此也就没有养成换来换去的坏毛病。
其实,有一点很奇怪,大家口口声声玩大数据,但往往在某些细节问题上止步不前,比如为了分词准确性提高一个千分点,花费大量的时间、财力和精力,我请问,这些改变对数亿级的文本分析结果能有多大的影响呢。
另外,数据量巨大的分析保证我们在群体上的精确性,这也是统计的本质,而有些人在评价结果时可能纠结于极个别的个体,这本就是一种本末倒置的思维。如果我们的自变量是由个体组成的,必然需要从群体上评价准确性,而不是纠结于黑天鹅事件。至于防止黑天鹅事件,自然可以设计一套风险管理措施,比如可以分化赌注,多玩几次。扯远了,继续安装Rwordseg。
######安装Rwordseg
install.packages("Rwordseg", repos = "http://R-Forge.R-project.org")#这一步要完成需要安装Rtools
library(rJava)
library(Rwordseg)
segmentCN("在此对原作者孙健表示强烈的敬意!")
install.packages("Rwordseg", repos = "http://R-Forge.R-project.org")#这一步要完成需要安装Rtoolslibrary(rJava)library(Rwordseg)segmentCN("在此对原作者孙健表示强烈的敬意!")
如果在线安装不成功,可以下载本地安装。
######下载
https://r-forge.r-project.org/R/?group_id=1054
https://r-forge.r-project.org/R/?group_id=1054
本地安装很简单,如果你使用Rstudio编辑器,点击tools,选install.packages就可以安装。如果segmentCN函数分词成功就ok了,报错就自求多福吧!
###2.3.jieba分词包安装
虽然我比较执着于Rwordseg,并不代表各位看管执着于我的执着,推荐结巴分词包,小巧玲珑,没有那么多幺蛾子,而且R版本和python版本都有,除了词性标注等分词包必备功能以外,jiebaR还加入了一些基础的文本分析算法,比如提取关键字(TFIDF)、分析文本相似性等等,真是老少咸宜。
######安装jiebaR
library(devtools)
if (!suppressWarnings(require("Rcpp"))) {
install.packages("Rcpp")
require("Rcpp")
install_github("qinwf/jiebaR")
library(jiebaR)
seg &- worker()
seg &= "江州市长江大桥,参加了长江大桥的通车仪式。"
seg &= "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/dataset_602123/ChnSentiCorp_htl_ba_2000/neg/neg.0.txt"
fenci &- segment("江州市长江大桥参加了长江大桥的通车仪式", seg)
1234567891011
library(devtools)if (!suppressWarnings(require("Rcpp"))) {install.packages("Rcpp")require("Rcpp")}install_github("qinwf/jiebaR")library(jiebaR)seg &- worker()seg &= "江州市长江大桥,参加了长江大桥的通车仪式。"seg &= "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/dataset_602123/ChnSentiCorp_htl_ba_2000/neg/neg.0.txt"fenci &- segment("江州市长江大桥参加了长江大桥的通车仪式", seg)
如果使用install_github函数安装github上的包,需要调取devtools包,而该包需要Rtools,这也是提前安装Rtools的原因;另外jiebaR使用Rcpp开发,因此需要安装调取Rcpp包。
jiebaR其实是一个刀架,里面的每一把刀是一个分词引擎(是的,好几种),通过worker函数初始化引擎,建议不要更改默认设置(个人认为很多函数的默认设置必有其合理性),指定分词引擎后,只需要将文本通过&=符号赋值给分词引擎即可,有两种方式:其一直接赋值一句话;其二指定文本路径,分词结果存放在相应的路径内;当然也可以像使用Rwordseg包的segmentCN函数一样使用segment函数分词,只不过后者需要指定分词引擎。
之所以多说了两句,是为了努力说服大家尽量使用jiebaR,但是下面的内容我们主要使用Rwordseg包分词,是不是很变态?其实就分词这段不一样而已。
##3.基于词典的情感分析的效率高过瞎猜么?
上面已经简单介绍了基于词典的情感分析的过程,一般经过以下几个过程:数据整理、词典整理、情感词匹配、计算情感得分和方法评估等过程。这种分析方法因其思路简单而遭人诟病,更有甚者说分析效果赶不上猴子瞎猜,咱不看广告,看疗效。
###3.1.数据整理及词典构建
这一节我们完成文本语料库和词典的整理,使用谭松波和清华大学李军等人标注的语料库进行分析,词典方面使用已经公开的词典资源,包括台湾大学、中国知网等网站公布的情感词典。
####3.1.1.数据整理
语料库共包含两个数据集:清华大学李军标注的近24000个酒店评论文本和谭松波整理的12000个来自京东、携程、当当网的跨行业评论文本。李军的语料库(review_sentiment)包括两个数据集(训练集和测试集)和两个标注表(训练标注表和测试标注表),标注表注明了文本的情感倾向,正向为1,负向为-1。由于文本为分散的单个文本,所以需要批量读入文本文件。
######获取文本路径
reviewpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/train2"
# reviewpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/test2"
completepath &- list.files(reviewpath, pattern = "*.txt$", full.names = TRUE)
reviewpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/train2"# reviewpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/test2"completepath &- list.files(reviewpath, pattern = "*.txt$", full.names = TRUE)
上面第一句设定了文件存放的路径,list.files函数获取该路径下所有文件的文件名,如果full.names参数为真,则返回完整的路径,反之返回文件名称,pattern设置符合正则表达式的规则,仅提取符合要求的文件,防止读入系统文件,这里仅匹配以.txt结尾的文件。
######批量读入文本
read.txt &- function(x) {
des &- readLines(x)
return(paste(des, collapse = ""))
review &- lapply(completepath, read.txt)
#There were 50 or more warnings (use warnings() to see the first 50)
read.txt &- function(x) {des &- readLines(x)return(paste(des, collapse = ""))}review &- lapply(completepath, read.txt)#There were 50 or more warnings (use warnings() to see the first 50)
上面刚开始构造了一个函数read.txt,目的是将一个完整路径的txt文件加载进来,并将每个段落粘贴在一起,函数是一个非常中规中矩的R函数模式:第一句,打开文件并分行读取,然后将每一行粘贴在一起,最后返回完整的文本内容。在R的自编函数末尾return声明返回的内容,如果不声明仅返回函数中形成的最后一个对象。paste函数有两个指定间隔符号的参数:sep和collapse,前者将两个对象或两个对象的元素对应粘在一起时使用,后者是融合的意思,将向量或者list的元素粘在一起时使用。第二句使用了lapply函数,避免了每一个文本循环读取,保持了代码的整洁,提高了运行速度。到这里,review这个list就记录了文件下的所有文本内容,它的每一个元素是一个文本的内容。如果你忍不住想用循环处理,说明你还处于低级阶段,不到万不得已绝对不能轻易使用R语言循环。另外,如果程序警告,这里可能是部分文件最后一行没有换行导致,不用担心。
######list转数据框
docname &- list.files(reviewpath, pattern = "*.txt$")
reviewdf &- as.data.frame(cbind(docname, unlist(review)),
stringsAsFactors = F)
colnames(reviewdf) &- c("id", "msg")
reviewdf$msg &- gsub(pattern = " ", replacement ="", reviewdf$msg)
reviewdf$msg &- gsub("\t", "", reviewdf$msg) #有时需要使用\\\t
reviewdf$msg &- gsub(",", ",", reviewdf$msg)
reviewdf$msg &- gsub("~|'", "", reviewdf$msg)
reviewdf$msg &- gsub("\\\"", "", reviewdf$msg)
# Warning message:
# In scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings, :
# EOF within quoted string
123456789101112
docname &- list.files(reviewpath, pattern = "*.txt$")reviewdf &- as.data.frame(cbind(docname, unlist(review)), stringsAsFactors = F) colnames(reviewdf) &- c("id", "msg") reviewdf$msg &- gsub(pattern = " ", replacement ="", reviewdf$msg)reviewdf$msg &- gsub("\t", "", reviewdf$msg) #有时需要使用\\\t reviewdf$msg &- gsub(",", ",", reviewdf$msg)reviewdf$msg &- gsub("~|'", "", reviewdf$msg)reviewdf$msg &- gsub("\\\"", "", reviewdf$msg)# Warning message:# In scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings, :# EOF within quoted string
1行读取文件名称;2行名称和文本内容按列(cbind)捆绑在一起成为一个新的数据框;3行修改列名;4行移除文本中的所有空格,第一个参数指定正则表达式模式,第二个参数指定替换为的内容;5、6行替换所有的\t和英文逗号,因为csv各式的文档以英文逗号为分隔符,文中有英文逗号会报错,除了英文逗号可能引起read.csv函数读取csv文件报错以外,还有英文单引号(')、英文双引号(")、波浪号(~),都会引起读取时发生警告,带来csv文件或txt文件读取不完整的后果,如果一旦发生“EOF within quoted string”就要想办法找出这类符号将其替换掉;因此,7行依次替换了波浪号(~)和英文单引号('),它们之间用“|”符号隔开,表示或的关系;8行替换所有的英文双引号("),因为双引号在R中有特殊含义,所以要使用三个斜杠(\\)转义。数据算是基本处理了一下,下一步要给文本匹配上情感标注。
######关联标注
reviewclass &- read.table("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/train2.rlabelclass",
stringsAsFactor = F)
# reviewclass &- read.table("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/test2.rlabelclass", stringsAsFactor = F)
colnames(reviewclass) &- c("id", "label")
library(plyr)
reviewdf &- join(reviewdf, reviewclass)
reviewdf &- reviewdf[!is.na(reviewdf$label),]
train &- reviewdf
# test &- reviewdf
reviewclass &- read.table("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/train2.rlabelclass", stringsAsFactor = F)# reviewclass &- read.table("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/test2.rlabelclass", stringsAsFactor = F)colnames(reviewclass) &- c("id", "label")library(plyr)reviewdf &- join(reviewdf, reviewclass)reviewdf &- reviewdf[!is.na(reviewdf$label),]train &- reviewdf# test &- reviewdf
read.table读取训练集文本标注数据,该函数是R中读取数据的根函数,很多函数都是继承的read.table,所以看到格式陌生的文件,可以尝试使用这个函数读取,2行更改标注文档的列名称,id列和reviewdf中的id列相同,plyr包里的join函数会根据名称相同的列进行匹配关联,join默认设置下执行左连接;然后将整理好预料赋值给train备用。这样李军标注的训练数据集就整理完成了,整理测试数据集使用相同的代码,将注释掉的代码恢复,同时将上一句注释掉即可。
dataset_602124数据集分为当当、京东、携程三个子文件夹,下面又分别有pos和neg文件夹,顾名思义,pos文件夹下全是情感正向的文本;neg下则为负向的文件,为了操作简便,分别把文本统一整理到pos和neg文件夹内,然后在进行数据处理。
######获取文本路径
reviewpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/dataset_602124/pos"
# reviewpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/dataset_602124/neg"
completepath &- list.files(reviewpath, pattern = "*.txt$", full.names = TRUE)
reviewpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/dataset_602124/pos"# reviewpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/dataset_602124/neg"completepath &- list.files(reviewpath, pattern = "*.txt$", full.names = TRUE)
读取dataset_602124数据集,设定文嘉路径。
######批量读入文本
review &- lapply(completepath, read.txt) #自编函数read.txt
docname &- list.files(reviewpath, pattern = "*.txt$")
label &- rep(1, length(docname))
# label &- rep(-1, length(docname))
reviewdf &- as.data.frame(cbind(docname, unlist(review), label),
stringsAsFactors = F)
colnames(reviewdf) &- c("id", "msg", "label")
reviewdf$msg &- gsub(pattern = " ", replacement ="", reviewdf$msg)
reviewdf$msg &- gsub("\t", "", reviewdf$msg) #有时需要使用\\\t
reviewdf$msg &- gsub(",", ",", reviewdf$msg)
reviewdf$msg &- gsub("~|'", "", reviewdf$msg)
reviewdf$msg &- gsub("\\\"", "", reviewdf$msg)
pos6 &- reviewdf
# neg6 &- reviewdf
temp &- rbind(pos6, neg6)
train &- rbind(train, temp)
write.csv(train, 'H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/data/train.csv', row.names = FALSE)
# write.csv(test, 'H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/test.csv', row.names = FALSE)
123456789101112131415161718
review &- lapply(completepath, read.txt) #自编函数read.txtdocname &- list.files(reviewpath, pattern = "*.txt$")label &- rep(1, length(docname))# label &- rep(-1, length(docname))reviewdf &- as.data.frame(cbind(docname, unlist(review), label), stringsAsFactors = F) colnames(reviewdf) &- c("id", "msg", "label") reviewdf$msg &- gsub(pattern = " ", replacement ="", reviewdf$msg)reviewdf$msg &- gsub("\t", "", reviewdf$msg) #有时需要使用\\\t reviewdf$msg &- gsub(",", ",", reviewdf$msg)reviewdf$msg &- gsub("~|'", "", reviewdf$msg)reviewdf$msg &- gsub("\\\"", "", reviewdf$msg)pos6 &- reviewdf# neg6 &- reviewdftemp &- rbind(pos6, neg6)train &- rbind(train, temp)write.csv(train, 'H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/data/train.csv', row.names = FALSE)# write.csv(test, 'H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/test.csv', row.names = FALSE)
1、2行不多说;3行为读入的数据添加情感倾向标签,正向数据全部标注为1,负向数据全部标注为-1(暂时注释掉了),rep函数复制产生一个和向量docname等长的向量;4、5行捆绑3列生成数据框,并重新命名3列,列顺序、名称和train数据框保持一致;6、7、8、9、10行不多说了;11、12、13行将已有的数据按行粘(rbind)在一起,生成用于训练的语料库train,测试的语料库仅仅使用李军的测试集test即可(注释代码);只需将train和test输出到专用的数据存储文件夹data,write.csv输出语料文档,输出格式为csv,参数row.names设置为非,表示不输出行编号。
这样测试集和训练集语料库都已经准备好了,并存储在data文件夹里备用。
####3.1.2.词典构建
尽管我们搜集了很多部情感词典(参看电子文档),经过挑选,暂时整合中国知网、台大、清华和一部未标注来源的词典等四部词典,再次感谢这些词典的作者。首先我们需要将这些词典整合为正向情感词集pos和负向情感词集neg。我已经将各个词典的正负文本整理到两个文件夹内:
######词典整理
dictpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdict/posdic"
# dictpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdict/negdic"
completepath &- list.files(dictpath, pattern = "*.txt$", full.names = TRUE)
dict &- lapply(completepath, readLines)
dict &- unique(unlist(dict))
pos &- dict
# neg &- dict
write.csv(pos, 'H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/pos.csv', row.names = FALSE)
# write.csv(neg, 'H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/neg.csv', row.names = FALSE)
dictpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdict/posdic"# dictpath &- "H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdict/negdic"completepath &- list.files(dictpath, pattern = "*.txt$", full.names = TRUE)dict &- lapply(completepath, readLines)dict &- unique(unlist(dict))pos &- dict# neg &- dictwrite.csv(pos, 'H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/pos.csv', row.names = FALSE)# write.csv(neg, 'H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/neg.csv', row.names = FALSE)
1、2行不多说,3行lapply函数直接使用了readLines函数,将每个文件按行读取;4行将list解散(unlist)成为一个向量,各个词典中肯定有相互重复的词,所以使用unique函数去一次重,这样词典的正向词汇就整理完成了,负向词汇使用相同的方法,部分特异的代码可以使用注释掉的内容;最后将正负向词汇文件输出到dict文件夹即可。
###3.2.分词整理
基于词典的情感分析实际上算法和模型先入为主的预订了:统计文本正负情感词的得分之和,如果得分为正,则文本情感倾向为正,反之亦然。所以不需要训练模型,直接使用测试集测试一下即可。
基于以上,首先要进行中文分词,在分词之前要将文本预处理一下,包括清除一些英文和数字等等。
######分词预处理
test &- read.csv("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/test.csv", sep = ",", header = T, stringsAsFactors = F)
sentence &- as.vector(test$msg)
sentence &- gsub("[[:digit:]]*", "", sentence) #清除数字[a-zA-Z]
sentence &- gsub("[a-zA-Z]", "", sentence)
sentence &- gsub("\\.", "", sentence)
test &- test[!is.na(sentence), ]
sentence &- sentence[!is.na(sentence)]
test &- test[!nchar(sentence) & 2, ]
sentence &- sentence[!nchar(sentence) & 2]
test &- read.csv("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/rawdata/review_sentiment/test.csv", sep = ",", header = T, stringsAsFactors = F)sentence &- as.vector(test$msg) sentence &- gsub("[[:digit:]]*", "", sentence) #清除数字[a-zA-Z]sentence &- gsub("[a-zA-Z]", "", sentence)sentence &- gsub("\\.", "", sentence)test &- test[!is.na(sentence), ]sentence &- sentence[!is.na(sentence)]test &- test[!nchar(sentence) & 2, ]sentence &- sentence[!nchar(sentence) & 2]
1行读取csv文件,并设置stringsAsFactors参数为非,不将字符转化为因子;2行将文本内容转化为向量3行清除数字;4行清除英文字符;由于某些文档可能是由外国人或者一些装逼汉写的评价,全部是英文,经过以上几步处理就只剩下了dot符号,所以5行将这类符号清除;经过以上处理,可能一些文本已经变成了空值或者小于两个字符了,所以6行将原数据框中这些空值文本清除,7行将对应的sentence里的空值清除,注意6行的筛选使用sentence是否为空值完成的,所以要先筛除数据框test内的空值,然后再筛除sentence内的空值;最后两句筛出字符数小于2的文本,nchar函数对字符计数,英文叹号为R语言里的“非”函数。
另外既然整合了大量的词典,就要尽量保证分词器能够把这些情感词汇分出来,所以需要将情感词典添加到分词器的词典中去,虽然这种方法在特殊情况下并不一定凑效,但至少增加了分词器在分词时的计算权重。
######添加词典
pos &- read.csv("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/pos.csv", header = T,
sep = ",", stringsAsFactors = F)
weight &- rep(1, length(pos[,1]))
pos &- cbind(pos, weight)
neg &- read.csv("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/neg.csv", header = T,
sep = ",", stringsAsFactors = F)
weight &- rep(-1, length(neg[,1]))
neg &- cbind(neg, weight)
posneg &- rbind(pos, neg)
names(posneg) &- c("term", "weight")
posneg &- posneg[!duplicated(posneg$term), ]
dict &- posneg[, "term"]
library(Rwordseg)
insertWords(dict)
1234567891011121314
pos &- read.csv("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/pos.csv", header = T, sep = ",", stringsAsFactors = F)weight &- rep(1, length(pos[,1]))pos &- cbind(pos, weight)neg &- read.csv("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/neg.csv", header = T, sep = ",", stringsAsFactors = F)weight &- rep(-1, length(neg[,1]))neg &- cbind(neg, weight)posneg &- rbind(pos, neg)names(posneg) &- c("term", "weight")posneg &- posneg[!duplicated(posneg$term), ]dict &- posneg[, "term"]library(Rwordseg)insertWords(dict)
1读取正向情感词;2行创建权重向量,这里任何一个正向词汇的权重均为1,负向词汇的权重为-1;3行为pos添加权重列;4、5、6行作用同上,为负向情感词添加权重列;7行将正负情感词按行粘贴在一起;8行更改列名称;因为各个词典对情感词的倾向定义可能矛盾,出现同一个词具有情感正向和负向两种倾向的情况,尽管这种情况更加符合现实,但是违背了基于词典的情感分析的原假设,所以要将这些词去重,我们的方法是一个词如果同时属于正向和负向,仅保留正向分类,9行duplicated函数的作用和unique函数比较相似,它返回重复项的位置编号,比如“爱”这个词在数据框和向量中第二次及更多次出现的位置,第一次出现的位置不返回,如果加了“非”函数,表示仅保留第一次出现的词汇;10、11、12行提取posneg的term列并使用Rwordseg包添加自定义词典的函数insertWords将词典添加入分词器。
下面就可以分词了,Rwordseg分词包分词函数segmentCN,仅需将向量、字符串、或者文本文件地址赋值给参数strwords即可,结果是一个和原向量等长的list,list的每一个元素是一个词语字符向量,对应原来的文本向量,函数的其他参数仅nosymbol需要关注一下,指定是否保留文本中的符号。
######分词
system.time(x &- segmentCN(strwords = sentence))
temp &- lapply(x, length)
temp &- unlist(temp)
id &- rep(test[, "id"], temp)
label &- rep(test[, "label"], temp)
term &- unlist(x)
testterm &- as.data.frame(cbind(id, term, label), stringsAsFactors = F)
system.time(x &- segmentCN(strwords = sentence)) temp &- lapply(x, length)temp &- unlist(temp)id &- rep(test[, "id"], temp)label &- rep(test[, "label"], temp)term &- unlist(x)testterm &- as.data.frame(cbind(id, term, label), stringsAsFactors = F)
1行使用分词函数segmentCN分词,其结果是一个和sentence等长的list,list的每一个元素是对应文本的分词结果,这里使用了system.time函数返回代码块的执行时间,建议每次可能耗费时间较长的过程,都要使用少量数据预估一下时间,这是一个优秀的习惯;但是需要将分词结果和文本的id关联上,那文本分出多少个词就要重复多少次文本id,所以2行使用lapply函数返回x中每一个元素的长度,即文本分出多少个词;lapply返回的是一个list,所以3行unlist;然后4行将每一个对应的id复制相应的次数,就可以和词汇对应了;5行将id对应的情感倾向标签复制相同的次数;6行将list解散为向量;7行将一一对应的三个向量按列捆绑为数据框,分词整理就基本结束了。
在分析过程中,难免会产生很多中间变量,它们会占用大量内存,虽然dplyr包的管道函数看似解决了这个问题,但是毕竟用处不广。我通常会将所有的临时中间变量命名为temp,只需要保证下一个temp出现之前,临时变量不会再延用就可以了,如上面的temp,这样中间变量始终只有一个,即temp。
虽然算法已经足够简单,没有必要去除停用词,但是为了显示诚意,文本分析里每一个环节都不能少,这里还是认真的去除停用词,真的不是走过场哦。
######去除停用词
stopword &- read.csv("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/stopword.csv", header = T, sep = ",", stringsAsFactors = F)
stopword &- stopword[!stopword$term %in% posneg$term,]
testterm &- testterm[!testterm$term %in% stopword,]
stopword &- read.csv("H:/zimeiti/窥视数据背后的逻辑:基于R与python/bookwriting/第十二章舆情分析/dict/stopword.csv", header = T, sep = ",", stringsAsFactors = F)stopword &- stopword[!stopword$term %in% posneg$term,]testterm &- testterm[!testterm$term %in% stopword,]
读取停用词典;停用词中有可能有些词有感情色彩,既然要去除停用词,首先要将这类具有情感色彩的停用词从停用词表中去除,函数%in%在posneg$term中查找stopword的元素,如果查到了就返回真值,没查到就返回假,结果是一个和stopword等长的波尔值向量,“非”函数将布尔值反向;3行去除停用词。
###3.3.情感指数计算
现在有了分词表testterm和情感词典posneg,另外已经给情感词添加了权重列,那么只需要匹配term列,即可给testterm表关联上情感权重,然后就可以计算篇文本的情感得分了,需要注意的是如果一个情感词出现多次,其权重加倍,当然你也可以尝试对词汇去重,每个情感词只记录一次,也许会有出乎意料的结果。
######关联情感词权重
library(plyr)
testterm &- join(testterm, posneg)
testterm &- testterm[!is.na(testterm$weight), ]
head(testterm)
library(plyr)testterm &- join(testterm, posneg)testterm &- testterm[!is.na(testterm$weight), ]head(testterm)
使用plyr包的join函数进行左关联,给testterm表关联上情感权重,然后筛除那些没有权重的非情感词汇。奇怪的是很多看似不具有情感的词,这些词典竟然也把他们当成了情感词,看来还需要优化情感词典。
######计算情感指数
dictresult &- aggregate(weight ~ id, data = testterm, sum)
dictlabel &- rep(-1, length(dictresult[, 1]))
dictlabel[dictresult$weight & 0] &- 1
dictresult &- as.data.frame(cbind(dictresult, dictlabel), stringsAsFactors = F)
dictresult &- aggregate(weight ~ id, data = testterm, sum)dictlabel &- rep(-1, length(dictresult[, 1]))dictlabel[dictresult$weight & 0] &- 1dictresult &- as.data.frame(cbind(dictresult, dictlabel), stringsAsFactors = F)
使用透视表函数aggregate对weight列以文本id分组求和,就得出了每个文本的情感得分,情感得分大于0,倾向为正,标记为1,反之标为-1;2行使用rep函数产生一个和dictresult等长的-1向量;3行筛选情感得分大于0的文本,然后根据返回的布尔向量筛选,将向量dictlabel相应的位置更改为1;将向量dictlabel和数据框dictresult捆绑在一起,dictlabel列即基于词典的情感分析法给出的文本标签。
###3.4.方法评价:优缺点分析
词典法基本上省略了建模过程,也就用不着进行交叉检验了,只需要统计人工标记和词典标记的交叉表就可以反映出其准确性了。
######交叉表评价
temp &- unique(testterm[, c("id", "label")])
dictresult &- join(dictresult, temp)
evalue &- table(dictresult$dictlabel, dictresult$label)
# -1 399 35
temp &- unique(testterm[, c("id", "label")])dictresult &- join(dictresult, temp)evalue &- table(dictresult$dictlabel, dictresult$label)# -1 1# -1 399 35# 1
筛选测试数据的文本id和人工标签label,并去重;2行temp和分类结果通过文本id左关联;table函数对人工标注列和词典标注列做交叉表,大家看到返回的结果并不乐观,准确率仅仅达到59.1%,实在是名副其实的瞎猜。但从执行的过程中我们也发现,很多不具有情感色彩的词被定义为了情感词,例如的、了、还、在、我、都、把、上等字词,这些字词都是高频字词,而我们的计算方法按照出现频次重复计算,所以导致上面的结果偏差很大。
暂时的改进办法:修改优化词典,去除这类词汇,或者更改为去重计算,即一条评论中某词无论出现多少次都只计算一次权重。这里使用我们优化的词典再按照源代码执行一次看看结果怎么样,代码无需更改,只需要将正负情感词典地址改为“自备词典”文件夹里的词典即可。
######词典稍优化的结果
# -1 993 287
# 1 965 1648
evalue# -1 1# -1 993 287# 1 965 1648
简单修改了一下词典,准确率就上升到了67.8%,所以从各个方面考量,基于词典的情感分析方法仍然有提升的空间。比如细化词的权重等等。
毫无疑问,如果不追求高大上的算法的话,词典法不失为一种好方法,其实有时候我们使用了很多方法,结果发现并没有什么质变,也浪费了大量时间;比如在优化词典的时候,我希望使用高大上的算法解决问题,自动分辨出情感词,结果浪费了大量的时间,尝试了卡方统计量、各种分类器等等,结果可想而知,最后还是使用人工的方法将词典优化了一遍,是的,是用肉眼。其实有时候看起来最笨的方法也许是现阶段最有效最合适最省事的方法,只是它看起来很low,这也许就是笨方法的高深之处,“聪明人”是不屑于使用这些方法的。
另外,仅仅使用词汇并不能非常准确的识别一条文本所表达的情感倾向。一些修辞手法例如反讽、欲扬先抑等等也会给基于词典的情感分析造成困难。
数据代码: 密码:微信索取
除特别注明外,本站所有文章均为原创,转载请注明出处来自
发现数据之美
关注我们微信公众号:大音如霜
咨询服务:
邮箱:每日文章搜索
2017年十一月
6789101112
13141516171819
20212223242526
精品文章订阅
您也可以使用第三方帐号快捷登录
扫一扫二维码分享

我要回帖

更多关于 同义词算法 的文章

 

随机推荐