基于情感词典的情感分析流程图_情感的解释

基于情感词典的情感分析流程图_情感的解释思路以及代码都来源于下面两篇文章:一个不知死活的胖子:Python做文本情感分析之情感极性分析 RanFengzheng的博客:基于情感词典的文本情感极性分析相关代码基于情感词典的情感分析应该是最简单的情感分析方法了,大致说一下使用情感词典进行情感分析的思路:对文档分词,找出文档中的情感词、否定词以及程度副词,然后判断每个情感词之前是否有否定词及程度副词,将它之前的否定词和程度副词划分为一个组…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

思路以及代码都来源于下面两篇文章:

一个不知死活的胖子Python做文本情感分析之情感极性分析

 Ran Fengzheng 的博客基于情感词典的文本情感极性分析相关代码

基于情感词典的情感分析应该是最简单的情感分析方法了,大致说一下使用情感词典进行情感分析的思路:

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

准备:

1.BosonNLP情感词典

既然是基于情感词典的分析,当然需要一份包含所有情感词的词典,网上已有现成的,直接下载即可。

https://bosonnlp.com/dev/resource

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

丰富多彩 1.87317228434
神采飞扬 1.87321290817
细微 1.87336937803
178.00 1.87338705728
不辞辛劳 1.87338705728
保加利亚 1.87338705728

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

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

文本情感分类(一):传统模型

2.否定词词典

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

不大
不丁点儿
不甚
不怎么
聊
没怎么
不可以
怎么不
几乎不
从来不
从不
不用
不曾
不该
不必
不会
不好
不能
很少
极少
没有
不是
难以
放下
扼杀
终止
停止
放弃
反对
缺乏
缺少
不
甭
勿
别
未
反
没
否
木有
非
无
请勿
无须
并非
毫无
决不
休想
永不
不要
未尝
未曾
毋
莫
从未
从未有过
尚未
一无
并未
尚无
从没
绝非
远非
切莫
绝不
毫不
禁止
忌
拒绝
杜绝
弗

3.程度副词词典

程度副词如:非常、很、特别…等词

原博中提供了《知网》情感分析用词语集(beta版)的下载链接,该词典中包含了程度副词已经对应的程度值,但是下载下来之后发现只有程度副词,并没有对应的程度值。

从程度级别词语.txt中选取了一部分程度副词,可以看到只有程度词,没有程度值,这个时候就自己看情况赋一个值好了:

中文程度级别词语		219

1. “极其|extreme / 最|most”	69
百分之百
倍加
备至
不得了
不堪
不可开交
不亦乐乎
不折不扣
彻头彻尾
充分
到头
地地道道
非常
极
极度
极端
极其
极为
截然
尽
惊人地

改完之后的格式如下,程度副词和程度值用逗号分割,程度值可以自己定义:

百分之百,2
倍加,2
备至,2
不得了,2
不堪,2
不可开交,2
不亦乐乎,2
不折不扣,2
彻头彻尾,2
.....

4.停用词词典

数据堂的下载本地总是打不开,因此原博中提供的数据堂的中文停用词下载也是没下载下来,然后使用了snownlp源码中的停用词词典,但是后来发现有些情感词被当做停用词了

数据堂停用词下载:http://www.datatang.com/data/43894

snownlp源码:https://github.com/isnowfy/snownlp (停用词在snownlp/normal文件夹下 stopwords.txt)

5.分词工具

由于使用python,选择了jieba分词

数据和工具都准备好了,现在可以开始情感分析了~

来一个简单的句子:我今天很高兴也非常开心

(1)分词,去除停用词

我、今天、也被当作停用词去掉,剩下很、高兴、非常、开心

def seg_word(sentence):
    """使用jieba对文档分词"""
    seg_list = jieba.cut(sentence)
    seg_result = []
    for w in seg_list:
        seg_result.append(w)
    # 读取停用词文件
    stopwords = set()
    fr = codecs.open('stopwords.txt', 'r', 'utf-8')
    for word in fr:
        stopwords.add(word.strip())
    fr.close()
    # 去除停用词
    return list(filter(lambda x: x not in stopwords, seg_result))

(2)将分词结果转为字典,key为单词,value为单词在分词结果中的索引,后来想到一个问题,如果把单词作为key的话假如一个情感词在文中出现了多次,那么应该是只记录了这个词最后一次出现的位置,其他的被覆盖掉了。

将上一步得到的分词结果转为字典:

{‘很’: 0, ‘高兴’: 1, ‘非常’: 2, ‘开心’: 3}

def list_to_dict(word_list):
    """将分词后的列表转为字典,key为单词,value为单词在列表中的索引,索引相当于词语在文档中出现的位置"""
    data = {}
    for x in range(0, len(word_list)):
        data[word_list[x]] = x
    return data

(3)对分词结果分类,找出情感词、否定词和程度副词

情感词sen_word(高兴和开心,key为单词的索引,value为情感权值):

 {1: ‘1.48950851679’, 3: ‘2.61234173173’}

程度副词degree_word(很和非常,key为索引,value为程度值)
{0: ‘1.75’, 2: ‘2’}

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

def classify_words(word_dict):
    """词语分类,找出情感词、否定词、程度副词"""
    # 读取情感字典文件
    sen_file = open('BosonNLP_sentiment_score.txt', 'r+', encoding='utf-8')
    # 获取字典文件内容
    sen_list = sen_file.readlines()
    # 创建情感字典
    sen_dict = defaultdict()
    # 读取字典文件每一行内容,将其转换为字典对象,key为情感词,value为对应的分值
    for s in sen_list:
        # 每一行内容根据空格分割,索引0是情感词,索引1是情感分值(情感词典文件中有一行是空行,因此执行的时候会报错,注意处理一下空行,这里没有处理)
        sen_dict[s.split(' ')[0]] = s.split(' ')[1]

    # 读取否定词文件
    not_word_file = open('notDic.txt', 'r+', encoding='utf-8')
    # 由于否定词只有词,没有分值,使用list即可
    not_word_list = not_word_file.readlines()

    # 读取程度副词文件
    degree_file = open('degree.txt', 'r+', encoding='utf-8')
    degree_list = degree_file.readlines()
    degree_dic = defaultdict()
    # 程度副词与情感词处理方式一样,转为程度副词字典对象,key为程度副词,value为对应的程度值
    for d in degree_list:
        degree_dic[d.split(',')[0]] = d.split(',')[1]

    # 分类结果,词语的index作为key,词语的分值作为value,否定词分值设为-1
    sen_word = dict()
    not_word = dict()
    degree_word = dict()

(4)计算得分

首先设置初始权重W为1,从第一个情感词开始,用权重W*该情感词的情感值作为得分(用score记录),然后判断与下一个情感词之间是否有程度副词及否定词,如果有否定词将W*-1,如果有程度副词,W*程度副词的程度值,此时的W作为遍历下一个情感词的权重值,循环直到遍历完所有的情感词,每次遍历过程中的得分score加起来的总和就是这篇文档的情感得分。

def socre_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])
            # 情感词下标加1,获取下一个情感词的位置
            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*1.48950851679=1.48950851679

高兴和下一个情感词开心之间出现了程度副词非常,程度值为2,因此W=W*2=1*2=2,然后获取下一个情感词

下一个情感词是开心,此时W=2,score=score+2*2.61234173173=1.48950851679+2*2.61234173173=6.71419198025

遍历结束

这里也发现两个问题:

(1)第一个情感词之前出现的程度副词和否定词被忽略了

(2)在判断两个情感词之间出现否定词以及程度副词时,W没有被初始化为1,这样W就被累乘了

        有兴趣的可以修改一下~

完整代码:

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

def seg_word(sentence):
    """使用jieba对文档分词"""
    seg_list = jieba.cut(sentence)
    seg_result = []
    for w in seg_list:
        seg_result.append(w)
    # 读取停用词文件
    stopwords = set()
    fr = codecs.open('stopwords.txt', 'r', 'utf-8')
    for word in fr:
        stopwords.add(word.strip())
    fr.close()
    # 去除停用词
    return list(filter(lambda x: x not in stopwords, seg_result))


def classify_words(word_dict):
    """词语分类,找出情感词、否定词、程度副词"""
    # 读取情感字典文件
    sen_file = open('BosonNLP_sentiment_score.txt', 'r+', encoding='utf-8')
    # 获取字典文件内容
    sen_list = sen_file.readlines()
    # 创建情感字典
    sen_dict = defaultdict()
    # 读取字典文件每一行内容,将其转换为字典对象,key为情感词,value为对应的分值
    for s in sen_list:
        # 每一行内容根据空格分割,索引0是情感词,索引01是情感分值
        sen_dict[s.split(' ')[0]] = s.split(' ')[1]

    # 读取否定词文件
    not_word_file = open('notDic.txt', 'r+', encoding='utf-8')
    # 由于否定词只有词,没有分值,使用list即可
    not_word_list = not_word_file.readlines()

    # 读取程度副词文件
    degree_file = open('degree.txt', 'r+', encoding='utf-8')
    degree_list = degree_file.readlines()
    degree_dic = defaultdict()
    # 程度副词与情感词处理方式一样,转为程度副词字典对象,key为程度副词,value为对应的程度值
    for d in degree_list:
        degree_dic[d.split(',')[0]] = d.split(',')[1]

    # 分类结果,词语的index作为key,词语的分值作为value,否定词分值设为-1
    sen_word = dict()
    not_word = dict()
    degree_word = dict()

    # 分类
    for word in word_dict.keys():
        if word in sen_dict.keys() and word not in not_word_list and word not in degree_dic.keys():
            # 找出分词结果中在情感字典中的词
            sen_word[word_dict[word]] = sen_dict[word]
        elif word in not_word_list and word not in degree_dic.keys():
            # 分词结果中在否定词列表中的词
            not_word[word_dict[word]] = -1
        elif word in degree_dic.keys():
            # 分词结果中在程度副词中的词
            degree_word[word_dict[word]] = degree_dic[word]
    sen_file.close()
    degree_file.close()
    not_word_file.close()
    # 将分类结果返回
    return sen_word, not_word, degree_word


def list_to_dict(word_list):
    """将分词后的列表转为字典,key为单词,value为单词在列表中的索引,索引相当于词语在文档中出现的位置"""
    data = {}
    for x in range(0, len(word_list)):
        data[word_list[x]] = x
    return data


def get_init_weight(sen_word, not_word, degree_word):
    # 权重初始化为1
    W = 1
    # 将情感字典的key转为list
    sen_word_index_list = list(sen_word.keys())
    if len(sen_word_index_list) == 0:
        return W
    # 获取第一个情感词的下标,遍历从0到此位置之间的所有词,找出程度词和否定词
    for i in range(0, sen_word_index_list[0]):
        if i in not_word.keys():
            W *= -1
        elif i in degree_word.keys():
            # 更新权重,如果有程度副词,分值乘以程度副词的程度分值
            W *= float(degree_word[i])
    return W


def socre_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])
            # 情感词下标加1,获取下一个情感词的位置
            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 setiment_score(sententce):
    # 1.对文档分词
    seg_list = seg_word(sententce)
    # 2.将分词结果列表转为dic,然后找出情感词、否定词、程度副词
    sen_word, not_word, degree_word = classify_words(list_to_dict(seg_list))
    # 3.计算得分
    score = socre_sentiment(sen_word, not_word, degree_word, seg_list)
    return score

# 测试
print(setiment_score("我今天很高兴也非常开心"))

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/172407.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Tomcat日志乱码问题

    Tomcat日志乱码问题昨天本来准备更新一下Tomcat版本,但是发现新版本的日志打印中文会出现乱码(Tomcat自身打印的日志),不管是使用bat脚本启动还是在Idea中启动,都是乱码。研究了一个晚上,百度上的那些方式都试遍了,都是设置各种JVM启动参数,发现并没有卵用。在使用bat文件启动Tomcat时,Tomcat目录下的logs文件夹会生成相应的日志文件,发现旧版本生成的日志文件编码是GBK,而Windows控…

    2022年6月20日
    37
  • 各大巨头电商提供的IP库API接口-新浪、搜狐、阿里

    各大巨头电商提供的IP库API接口-新浪、搜狐、阿里

    2021年11月5日
    37
  • c/c++面试题大汇总_北京易联达C语言面试咋样

    c/c++面试题大汇总_北京易联达C语言面试咋样c++的知识点

    2022年8月6日
    3
  • Java8 stream流之分组 groupingBy 的使用

    Java8 stream流之分组 groupingBy 的使用使用stream流可以让我们的代码看上去很简洁,本文举例了按照班级分组、分组再过滤、统计分组后人数、嵌套分组、分组排序…

    2022年8月20日
    50
  • 有关于微信公众号网页开发的流程(自己的看法)

    有关于微信公众号网页开发的流程(自己的看法)微信官方文档(真的很详细)第一步:网页授权回调域名网页服务内容这个网页回调域名是非常关键的,后面会讲解这个回调的域名的使用,回调字面意思就是会跳转的。第二步:使用微信官方的api进行code获取这个就是获取code进行页面跳转,给跳转的页面传递code参数,你可以在自己的页面进行获取,将code传给后端参考链接(请在微信客户端中打开此链接体验):scope为sns…

    2022年6月6日
    431
  • Idea导入jar包的两种方法

    Idea导入jar包的两种方法今天用IDEA,需要导入一个Jar包,因为以前都是用eclipse的,所以对这个idea还不怎么上手,连打个Jar包都是谷歌了一下。但是发现网上谷歌到的做法一般都是去File–&gt;ProjectStructure中去设置,有没有如同eclipse一样简便的右键添加方法呢。然后自己摸索了一下,找到了。以阿里大于的依赖包为例子,因为正好是我遇到要导入的Jar包。先说下第一种方法吧。也就是Fi…

    2022年6月22日
    54

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号