基于情感词典的文本情感分析(附代码)

基于情感词典的情感分析应该是最简单传统的情感分析方法。

本文中使用情感词典进行情感分析的思路为:

  1. 对文档分词,找出文档中的情感词、否定词以及程度副词
  2. 然后判断每个情感词之前是否有否定词及程度副词,将它之前的否定词和程度副词划分为一个组
  3. 如果有否定词将情感词的情感权值乘以-1,如果有程度副词就乘以程度副词的程度值
  4. 最后所有组的得分加起来,大于0的归于正向,小于0的归于负向。(得分的绝对值大小反映了积极或消极的程度)

主要参考文章:

https://blog.csdn.net/lom9357bye/article/details/79058946

https://blog.csdn.net/qq_36489878/article/details/103772714

目录

 

1.准备数据

1.1 BosonNLP情感词典

1.2 否定词词典

1.3 程度副词词典

1.4 停用词词典

2. 数据预处理

2.1 使用jieba分词并去除停用词

3.分数计算

3.1 找出文本中的情感词,否定词和程度副词

 3.2 计算情感词的分数

4.完整代码


1.准备数据

1.1 BosonNLP情感词典

https://kexue.fm/usr/uploads/2017/09/1922797046.zip

从下载的文件里,随便粘了几个正向的情感词,词后面的数字表示的是情感词的情感分值,一般正向的都是正数,负向的是负数:


居家 2.08857470795
畅想 2.08875837264
2AM 2.08890952167
萌萌 2.08890952167
霍思燕 2.08890952167
阳光 2.08902889821
知足 2.08909186445


注:由于BosonNLP是基于微博、新闻、论坛等数据来源构建的情感词典,因此拿来对其他类别的文本进行分析效果可能不好

也有一种将所有情感词的情感分值设为1的方法来计算,想要详细了解可参考此文章:文本情感分类(一):传统模型

1.2 否定词词典

文本情感分类(一):传统模型中提供了一个情感极性词典的下载包,包中带了一个否定词的txt。

https://kexue.fm/usr/uploads/2017/09/1922797046.zip

1.3 程度副词词典

从程度级别词语.txt中选取了一部分程度副词,可以看到只有程度词,没有程度值。

这里做了一个简单的程度副词标记,大于1,表示情感加强,小于1,表示情感弱化,下面主要按照极其1.8,超1.6,很1.5,较1,稍0.7,欠0.5进行了一个简单的标记,如下所示。也可以根据自己的需求及及进行修改。

百分之百,1.8
倍加,1.8
备至,1.8
不得了,1.8
不堪,1.8
不可开交,1.8
不亦乐乎,1.8
不折不扣,1.8
彻头彻尾,1.8
充分,1.8
到头,1.8
地地道道,1.8
非常,1.8
极,1.8
极度,1.8
极端,1.8
极其,1.8
极为,1.8
截然,1.8
尽,1.8
惊人地,1.8
绝,1.8
绝顶,1.8
绝对,1.8
绝对化,1.8
刻骨,1.8
酷,1.8
满,1.8
满贯,1.8
满心,1.8
莫大,1.8
奇,1.8
入骨,1.8
甚为,1.8
十二分,1.8
十分,1.8
十足,1.8
死,1.8
滔天,1.8
痛,1.8
透,1.8
完全,1.8
完完全全,1.8
万,1.8
万般,1.8
万分,1.8
万万,1.8
无比,1.8
无度,1.8
无可估量,1.8
无以复加,1.8
无以伦比,1.8
要命,1.8
要死,1.8
已极,1.8
已甚,1.8
异常,1.8
逾常,1.8
贼,1.8
之极,1.8
之至,1.8
至极,1.8
卓绝,1.8
最为,1.8
佼佼,1.8
郅,1.8
綦,1.8
齁,1.8
最,1.8
不过,1.5
不少,1.5
不胜,1.5
惨,1.5
沉,1.5
沉沉,1.5
出奇,1.5
大为,1.5
多,1.5
多多,1.5
多加,1.5
多么,1.5
分外,1.5
格外,1.5
够瞧的,1.5
够戗,1.5
好,1.5
好不,1.5
何等,1.5
很,1.5
很是,1.5
坏,1.5
可,1.5
老,1.5
老大,1.5
良,1.5
颇,1.5
颇为,1.5
甚,1.5
实在,1.5
太,1.5
太甚,1.5
特,1.5
特别,1.5
尤,1.5
尤其,1.5
尤为,1.5
尤以,1.5
远,1.5
着实,1.5
曷,1.5
碜,1.5
大不了,0.8
多,0.8
更,0.8
更加,0.8
更进一步,0.8
更为,0.8
还,0.8
还要,0.8
较,0.8
较比,0.8
较为,0.8
进一步,0.8
那般,0.8
那么,0.8
那样,0.8
强,0.8
如斯,0.8
益,0.8
益发,0.8
尤甚,0.8
逾,0.8
愈,0.8
愈 ... 愈,0.8
愈发,0.8
愈加,0.8
愈来愈,0.8
愈益,0.8
远远,0.8
越 ... 越,0.8
越发,0.8
越加,0.8
越来越,0.8
越是,0.8
这般,0.8
这样,0.8
足,0.8
足足,0.8
点点滴滴,0.7
多多少少,0.7
怪,0.7
好生,0.7
还,0.7
或多或少,0.7
略,0.7
略加,0.7
略略,0.7
略微,0.7
略为,0.7
蛮,0.7
稍,0.7
稍稍,0.7
稍微,0.7
稍为,0.7
稍许,0.7
挺,0.7
未免,0.7
相当,0.7
些,0.7
些微,0.7
些小,0.7
一点,0.7
一点儿,0.7
一些,0.7
有点,0.7
有点儿,0.7
有些,0.7
半点,0.5
不大,0.5
不丁点儿,0.5
不甚,0.5
不怎么,0.5
聊,0.5
没怎么,0.5
轻度,0.5
弱,0.5
丝毫,0.5
微,0.5
相对,0.5
不为过,1.6
超,1.6
超额,1.6
超外差,1.6
超微结构,1.6
超物质,1.6
出头,1.6
多,1.6
浮,1.6
过,1.6
过度,1.6
过分,1.6
过火,1.6
过劲,1.6
过了头,1.6
过猛,1.6
过热,1.6
过甚,1.6
过头,1.6
过于,1.6
过逾,1.6
何止,1.6
何啻,1.6
开外,1.6
苦,1.6
老,1.6
偏,1.6
强,1.6
溢,1.6
忒,1.6

1.4 停用词词典

参考的是https://github.com/isnowfy/snownlp/blob/master/snownlp/normal/stopwords.txt(为什么高兴成停用词了?_?)

要注意一下需要将否定词或者是程度副词的词典过滤掉,不然否定词在去除停用词的时候都过滤掉了,就缺少了一些程度副词或者否定词。使用以下方法进行过滤:

(代码复制过来缩进怎么成这样,强迫症看着难受。。。)

#生成stopword表,需要去除一些否定词和程度词汇
stopwords = set()
fr = open('停用词.txt','r',encoding='utf-8')
for word in fr:
	stopwords.add(word.strip())#Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
#读取否定词文件
not_word_file = open('否定词.txt','r+',encoding='utf-8')
not_word_list = not_word_file.readlines()
not_word_list = [w.strip() for w in not_word_list]
#读取程度副词文件
degree_file = open('程度副词.txt','r+')
degree_list = degree_file.readlines()
degree_list = [item.split(',')[0] for item in degree_list]
#生成新的停用词表			
with open('stopwords.txt','w',encoding='utf-8') as f:
	for word in stopwords:
		if(word not in not_word_list) and (word not in degree_list):
			f.write(word+'\n')

2. 数据预处理

2.1 使用jieba分词并去除停用词

#jieba分词后去除停用词
def seg_word(sentence):
	seg_list = jieba.cut(sentence)
	seg_result = []
	for i in seg_list:
		seg_result.append(i)
	stopwords = set()
	with open('stopwords.txt','r') as fr:
		for i in fr:
			stopwords.add(i.strip())
	return list(filter(lambda x :x not in stopwords,seg_result))

3.分数计算

3.1 找出文本中的情感词,否定词和程度副词

如句子:我今天很高兴也非常开心,去除停用词后得到:

['很', '高兴', '非常', '开心']

情感词:高兴、开心,key为单词的索引,value为情感权值:

 sen_word:{1: '1.48950851679', 3: '2.61234173173'}

否定词:没有出现否定词,所以否定词为空:
not_word:{}


程度副词:很、非常

degree_word:{0: '1.5', 2: '1.8'}
 

#找出文本中的情感词、否定词和程度副词
def classify_words(word_list):
	#读取情感词典文件
	sen_file = open('BosonNLP_sentiment_score.txt','r+',encoding='utf-8')
	#获取词典文件内容
	sen_list = sen_file.readlines()
	#创建情感字典
	sen_dict = defaultdict()
	#读取词典每一行的内容,将其转换成字典对象,key为情感词,value为其对应的权重
	for i in sen_list:
		if len(i.split(' '))==2:
			sen_dict[i.split(' ')[0]] = i.split(' ')[1]

	#读取否定词文件
	not_word_file = open('否定词.txt','r+',encoding='utf-8')
	not_word_list = not_word_file.readlines()
	#读取程度副词文件
	degree_file = open('程度副词.txt','r+')
	degree_list = degree_file.readlines()
	degree_dict = defaultdict()
	for i in degree_list:
		degree_dict[i.split(',')[0]] = i.split(',')[1]

	sen_word = dict()
	not_word = dict()
	degree_word = dict()
	#分类
	for i in range(len(word_list)):
		word = word_list[i]
		if word in sen_dict.keys() and word not in not_word_list and word not in degree_dict.keys():
			# 找出分词结果中在情感字典中的词
			sen_word[i] = sen_dict[word]
		elif word in not_word_list and word not in degree_dict.keys():
			# 分词结果中在否定词列表中的词
			not_word[i] = -1
		elif word in degree_dict.keys():
			# 分词结果中在程度副词中的词
			degree_word[i]  = degree_dict[word]


	#关闭打开的文件
	sen_file.close()
	not_word_file.close()
	degree_file.close()
	#返回分类结果
	return sen_word,not_word,degree_word

 3.2 计算情感词的分数

采用的规则如下:

遍历所有的情感词,查看当前情感词的前面是否有否定词和程度副词,如果没有否定词,就对当前情感词乘以1,如果有否定词或者有多个否定词,可以乘以(-1)^否定词的个数;如果有程度副词,就在当前情感词前面乘以程度副词的程度等级。

伪代码:

finalSentiScore = (-1) ^ (num of notWords) * degreeNum * sentiScore
#计算情感词的分数
def score_sentiment(sen_word,not_word,degree_word,seg_result):
	#权重初始化为1
	W = 1
	score = 0
	#情感词下标初始化
	sentiment_index = -1
	#情感词的位置下标集合
	sentiment_index_list = list(sen_word.keys())
	#遍历分词结果
	for i in range(0,len(seg_result)):
		#如果是情感词
		if i in sen_word.keys():
			#权重*情感词得分
			score += W*float(sen_word[i])
			#情感词下标加一,获取下一个情感词的位置
			sentiment_index += 1
			if sentiment_index < len(sentiment_index_list)-1:
				#判断当前的情感词与下一个情感词之间是否有程度副词或否定词
				for j in range(sentiment_index_list[sentiment_index],sentiment_index_list[sentiment_index+1]):
					#更新权重,如果有否定词,权重取反
					if j in not_word.keys():
						W *= -1
					elif j in degree_word.keys():
						W *= float(degree_word[j])	
		#定位到下一个情感词
		if sentiment_index < len(sentiment_index_list)-1:
			i = sentiment_index_list[sentiment_index+1]
	return score

‘我今天很高兴也非常开心’分词去停用词得到:['很', '高兴', '非常', '开心']

最开始W=1,score=0

第一个情感词是高兴,高兴的情感权值为1.48950851679,score=W*情感权值=1.48950851679

高兴和下一个情感词开心之间出现了非常,程度值为1.8,因此W=W*1.8=1.8

然后获取下一个情感词

下一个情感词是开心,此时W=1.8,score=score+1.8*2.61234173173= 6.191723633904

遍历结束

问题:这样写漏掉了第一个情感词前的否定词和程度副词。。。(这里漏掉了“很”)
 

4.完整代码

from collections import defaultdict
import os
import re
import jieba
import codecs

#生成stopword表,需要去除一些否定词和程度词汇
stopwords = set()
fr = open('停用词.txt','r',encoding='utf-8')
for word in fr:
	stopwords.add(word.strip())#Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
#读取否定词文件
not_word_file = open('否定词.txt','r+',encoding='utf-8')
not_word_list = not_word_file.readlines()
not_word_list = [w.strip() for w in not_word_list]
#读取程度副词文件
degree_file = open('程度副词.txt','r+')
degree_list = degree_file.readlines()
degree_list = [item.split(',')[0] for item in degree_list]
#生成新的停用词表			
with open('stopwords.txt','w',encoding='utf-8') as f:
	for word in stopwords:
		if(word not in not_word_list) and (word not in degree_list):
			f.write(word+'\n')


#jieba分词后去除停用词
def seg_word(sentence):
	seg_list = jieba.cut(sentence)
	seg_result = []
	for i in seg_list:
		seg_result.append(i)
	stopwords = set()
	with open('stopwords.txt','r') as fr:
		for i in fr:
			stopwords.add(i.strip())
	return list(filter(lambda x :x not in stopwords,seg_result))		



#找出文本中的情感词、否定词和程度副词
def classify_words(word_list):
	#读取情感词典文件
	sen_file = open('BosonNLP_sentiment_score.txt','r+',encoding='utf-8')
	#获取词典文件内容
	sen_list = sen_file.readlines()
	#创建情感字典
	sen_dict = defaultdict()
	#读取词典每一行的内容,将其转换成字典对象,key为情感词,value为其对应的权重
	for i in sen_list:
		if len(i.split(' '))==2:
			sen_dict[i.split(' ')[0]] = i.split(' ')[1]

	#读取否定词文件
	not_word_file = open('否定词.txt','r+',encoding='utf-8')
	not_word_list = not_word_file.readlines()
	#读取程度副词文件
	degree_file = open('程度副词.txt','r+')
	degree_list = degree_file.readlines()
	degree_dict = defaultdict()
	for i in degree_list:
		degree_dict[i.split(',')[0]] = i.split(',')[1]

	sen_word = dict()
	not_word = dict()
	degree_word = dict()
	#分类
	for i in range(len(word_list)):
		word = word_list[i]
		if word in sen_dict.keys() and word not in not_word_list and word not in degree_dict.keys():
			# 找出分词结果中在情感字典中的词
			sen_word[i] = sen_dict[word]
		elif word in not_word_list and word not in degree_dict.keys():
			# 分词结果中在否定词列表中的词
			not_word[i] = -1
		elif word in degree_dict.keys():
			# 分词结果中在程度副词中的词
			degree_word[i]  = degree_dict[word]


	#关闭打开的文件
	sen_file.close()
	not_word_file.close()
	degree_file.close()
	#返回分类结果
	return sen_word,not_word,degree_word

#计算情感词的分数
def score_sentiment(sen_word,not_word,degree_word,seg_result):
	#权重初始化为1
	W = 1
	score = 0
	#情感词下标初始化
	sentiment_index = -1
	#情感词的位置下标集合
	sentiment_index_list = list(sen_word.keys())
	#遍历分词结果
	for i in range(0,len(seg_result)):
		#如果是情感词
		if i in sen_word.keys():
			#权重*情感词得分
			score += W*float(sen_word[i])
			#情感词下标加一,获取下一个情感词的位置
			sentiment_index += 1
			if sentiment_index < len(sentiment_index_list)-1:
				#判断当前的情感词与下一个情感词之间是否有程度副词或否定词
				for j in range(sentiment_index_list[sentiment_index],sentiment_index_list[sentiment_index+1]):
					#更新权重,如果有否定词,权重取反
					if j in not_word.keys():
						W *= -1
					elif j in degree_word.keys():
						W *= float(degree_word[j])	
		#定位到下一个情感词
		if sentiment_index < len(sentiment_index_list)-1:
			i = sentiment_index_list[sentiment_index+1]
	return score


#计算得分
def sentiment_score(sentence):
	#1.对文档分词
	seg_list = seg_word(sentence)
	#2.将分词结果转换成字典,找出情感词、否定词和程度副词
	sen_word,not_word,degree_word = classify_words(seg_list)
	#3.计算得分
	score = score_sentiment(sen_word,not_word,degree_word,seg_list)
	return score


print("我今天很高兴也非常开心    ",sentiment_score("我今天很高兴也非常开心"))
print('天灰蒙蒙的,路上有只流浪狗,旁边是破旧不堪的老房子   ',sentiment_score('天灰蒙蒙的,路上有只流浪狗,旁边是破旧不堪的老房子'))
print('愤怒、悲伤和埋怨解决不了问题    ',sentiment_score('愤怒、悲伤和埋怨解决不了问题'))
print('要每天都开心快乐    ',sentiment_score('要每天都开心快乐'))
print('我不喜欢这个世界,我只喜欢你    ',sentiment_score('我不喜欢这个世界,我只喜欢你'))



实验结果:

随机测试了5个句子,可以看出整体上情感分析的结果不错,越积极的文本评分越高,消极文本同理。

当然这种方法受很多因素的影响,不可控性大,比如情感字典的选择(里面情感词的权重赋值)、停用词表的选择、分数计算规则的设计。而基于机器(深度)学习的方法无疑是优于基于情感字典的方法的。
 

评论 109
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值