微博情绪可视化

微博情绪分析,时空可视化

库的调用

import math
import numpy as np
import jieba
from harvesttext import HarvestText #从harvesttext中引入HarvestText,调用专门用于处理微博评论的函数
import re#用于正则表达式的替换
from datetime import datetime
import matplotlib.pyplot as plt
from pyecharts.charts import Geo
from pyecharts import options as opts

生成一万条评论,并且生成情绪文件,便于做分词字典

因为两百万条评论跑起来实在是太费劲了,于是只跑了10000条评论,我将前一万条评论按行读入的同时将行导入一个新的test.txt文件中。同时将五个文件中的情绪词导入了同一个txt文件,以便于后面做评论的清洗和分词

def create_test():#生成一个一万条的评论进行测试,同时将情绪词合并成一个,作为后续停用词的使用
    f=open("\\weibo.txt","r",encoding="utf-8")
    f.readline()#将第一行的标题行读取,但是不加入
    new=open("\\test.txt","w",encoding="utf-8")
    for i in range(10000):
        x=f.readline()
        new.write(x)
    new.close()
    f.close()
    anger=open("\\Anger\\data\\emotion_lexicon\\anger.txt","r",encoding="utf-8")
    disgust=open("\\Anger\\data\\emotion_lexicon\\disgust.txt","r",encoding="utf-8")
    fear=open("\\Anger\\data\\emotion_lexicon\\fear.txt","r",encoding="utf-8")
    sadness=open("\\Anger\\data\\emotion_lexicon\\sadness.txt","r",encoding="utf-8")
    joy=open("\\Anger\\data\\emotion_lexicon\\joy.txt","r",encoding="utf-8")
    anger=anger.readlines()
    disgust = disgust.readlines()
    fear = fear.readlines()
    sadness = sadness.readlines()
    joy = joy.readlines()
    emotion=open("\\emotion.txt","w",encoding="utf-8")
    for i in anger:
        emotion.write(i)
    for i in disgust:
        emotion.write(i)
    for i in fear:
        emotion.write(i)
    for i in joy:
        emotion.write(i)
    for i in sadness:
        emotion.write(i)
    emotion.close()

微博评论清洗

def filter(path1,path2):
    weibo=open(path1,"r",encoding="utf-8")
    #emotion=open(path2,"r",encoding="utf-8")
    jieba.load_userdict(path2)#对情感按行读入字典
    lis=[]#按照地点坐标,text,id,时间点作为一行列表合并到一起
    for item in weibo:#对每条评论进行处理,text在第二部分
        item=item.strip().split("\t")
        lis.append(item)
    for item in lis: #对噪音进行处理
        item[1] = HarvestText().clean_text(item[1])
        item[1] = re.sub("我在:", "", item[1]) #不知道什么原因,函数并没有将 我在 我在这里这两个词删去
        item[1] = re.sub("我在这里:", "", item[1])
        item[1]=jieba.lcut(item[1],cut_all=False)#由于只需要分析情绪词,因此我们不对其做停用词过滤
    return lis

函数的参数path1, path2分别表示10000条评论,path2表示所有的情绪词。

我才用了jieba库的内置函数load_userdict(file)生成了一个分词字典。

通过观察评论的格式,我发现评论被分为四个部分local,text,id,time,且每个部分由\t字符隔开,且字符串最后有\n换行符。因此先提取weibo的每条评论,先用strip()函数去除头尾的空白符,然后用split()函数将评论分成四部分。然后使用harvesttext库的HarvestText().clean_text()每条评论的文本部分进行url清洗。但是不知道为什么并没有把“我在:”,“我在这里:”删去,因此采用re库里的re.sub()函数对字符串匹配部分替换,re.sub也是使用正则表达式匹配字符串的常用函数。

接着对评论部分进行精确分词

对评论进行情绪分析,并且处理时间和经纬度

def time_emotion_local(filename):
    path="\\Anger\\data\\emotion_lexicon\\"
    emo=[]#用来分类保存情绪词
    for i in range(len(filename)): #这一步将情绪词分开方便处理,anger,disgust,joy,fear,sadness
        fp=open(path+filename[i],"r",encoding="utf-8")
        item=[i.strip() for i in fp.readlines()]
        emo.append(item)
        fp.close()
    def wordsplit(lis):
        nonlocal emo
        time,emotion_lis,local=[],[],[]#保存每条评论的时间,情绪词典,地址
        for i in range(len(lis)):
            a=lis[i][0][1:-2].split(",")
            b=[float(a[0]),float(a[1])]
            local.append(b)
            time.append(lis[i][3])
        for i in range(len(time)):
            tm=time[i].strip().split(" +0800")#剔除+0800,接着对时间标准化为时间类型
            tm=" ".join(tm)
            time[i]=datetime.strptime(tm,"%c")#datetime.datetime(2013, 10, 11, 22, 8, 28)为年 月 日 小时 分钟 秒数
        for item in lis:
            emotion_dict = {"anger": 0, "disgust": 0, "fear": 0, "joy": 0, "sadness": 0}
            for it in item[1]:#判断评论里是否带有情绪词
                if it in emo[0]:
                    emotion_dict["anger"]+=1
                elif it in emo[1]:
                    emotion_dict["disgust"]+=1
                elif it in emo[2]:
                    emotion_dict["fear"]+=1
                elif it in emo[3]:
                    emotion_dict["joy"]+=1
                elif it in emo[4]:
                    emotion_dict["sadness"]+=1
            set_emo=set(list(emotion_dict.values()))#生成一个集合,如果说无情感或者情感数目相同,则集合长度为1
            if len(set_emo)==1:
                emotional="no emotion"
            else:
                emotional=max(emotion_dict,key=emotion_dict.get)#返回键值最大的键
            emotion_lis.append(emotional)
        return time,emotion_lis,local
    return wordsplit #

该函数使用了闭包,虽然没有必要,但还是试了试

首先是对时间的处理:首先要把+0800删除,然后就是一种标准化的时间类型,在时间类型中有专门的表达格式%c。先提取评论信息的时间部分,这部分是一个字符串,然后我将他们通过split函数删去" +0800"之后再用空格符连接起来,也可以采用正则表达式 match=re.sub(r"+\d{4}“,”",str)。然后,调用函数datetime.striptime()对时间进行标准化处理,并转为时间格式.

之后是对地点的处理:我需要将地点转化为经度和维度的浮点数保存在地址列表中,a=lis[i][0][1:-2].split(“,”)提取每条评论的数字部分,并对其关于逗号分割,在利用float函数对数字进行浮点化。

对情绪的处理,我们利用for语句对每条评论做了分析,字典用来统计每条评论的情感,利用if语句判断判断带有情感的分词出现次数。我需要对字典值进行判断,如果都为0或者相等记为no emotion,所以我将所有键值转为列表后,在转为集合(利用set函数),如果说值都一样,那么set的长度应该只为1。否则,返回键值最大的键作为评论情绪的代表。

情绪关于时间可视化

def ptime(emotion,time_lis,emotion_dic,foun):
    month_dict = {}
    month = [i+1 for i in range(12)]  # 生成一个0到11的列表,表示月份
    month_dict = month_dict.fromkeys(month, 0)
    week_dict={}
    week=[i for i in range(7)]#生成一个0到6的列表,从0到6为周一到周日
    week_dict=week_dict.fromkeys(week,0)
    hour_dict = {}
    hour = [i for i in range(24)]  # 生成一个0到23的列表,小时
    hour_dict = hour_dict.fromkeys(hour, 0)
    if foun=="week":
        for i in range(len(emotion_dic)):
            if emotion_dic[i]==emotion:#当发现情绪匹配时,将对应的星期加一
                we=time_lis[i].weekday()
                week_dict[we]+=1
        week_value=list(week_dict.values())#生成键值列表
        plt.plot(week,week_value,color="green",marker="*",label="week_{}".format(emotion))
        plt.xlabel("week")
        plt.ylabel("numbers")
        for a, b in zip(week, week_value):#zip函数,将元素一一对应,打包成元组
            plt.text(a, b + 1, b,fontsize=10)#分别表示坐标,打印的值
        plt.show()
    elif foun=="hour":
        for i in range(len(emotion_dic)):
            if emotion_dic[i]==emotion:
                ho=time_lis[i].hour
                hour_dict[ho]+=1
        hour_value=list(hour_dict.values())#生成键值列表
        plt.plot(hour,hour_value,color="red",marker="*",label="hour_{}".format(emotion))#设置图像参数
        plt.xlabel("hour")
        plt.ylabel("numbers")
        for a, b in zip(hour, hour_value):#zip函数,将元素一一对应,打包成元组
            plt.text(a, b + 1, b,fontsize=10)#分别表示坐标,打印的值
        plt.show()
    elif foun=="month":
        for i in range(len(emotion_dic)):
            if emotion_dic[i]==emotion:
                mo=time_lis[i].month
                month_dict[mo]+=1
        month_value=list(month_dict.values())#生成键值列表
        plt.plot(month,month_value,color="blue",marker="*",label="month_{}".format(emotion))#设置图像参数
        plt.xlabel("month")
        plt.ylabel("numbers")
        for a, b in zip(month, month_value):#zip函数,将元素一一对应,打包成元组
            plt.text(a, b + 1, b,fontsize=10)#分别表示坐标,打印的值
        plt.show()

首先是对函数传入参数的表示:所要表示的情绪,时间列表,情绪列表,时间匹配模式。

首先生成一个列表,以周为例,0到11,然后根据列表进行字典操作,对每周的匹配值进行初始化为0,具体使用的函数为dict.fromkeys(month,0)。(忽然发现好像并不需要这么做,直接用列表就行)。之后用for循环对每一条评论进行遍历,当发现找到需要匹配的情绪时,将对应的时间提取weekday,然后将对应星期加一。接着进行可视化,即对键和键值分别做为x,即可。

以下为joy情感的可视化图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

空间半径变化对情绪的变化及其可视化

一开始我设置了所有的评论重心作为空间原点,但是发现有某些值对重心产生了很大的偏移,导致重心范围内几乎很难找到一条评论,于是我采用了天安们坐标作为在圆心。

def location_mid(local):#但是发现有某个坐标影响特别大,导致重心位置偏离过大
    x=0
    y=0
    for i in range(len(local)):
        x+=local[i][0]
        y+=local[i][1]
    x_ave=x/len(local)
    y_ave=y/len(local)
    mid=[x_ave,y_ave]
    return mid
def location_bar(local,emotion_lis,start,r):#地点列表,感情列表,起点,终点r,r设为从0到0.5截取50段的列表便于之后画堆积图
    start=np.array(start)
    distance=[]
    for dis in local:
        dis=np.array(dis)
        length=math.sqrt(sum((dis-start)**2))
        distance.append(length)#保存每条评论距离重心的距离
    #生成情感占比列表
    anger_lis=[]
    disgust_lis=[]
    fear_lis=[]
    joy_lis=[]
    sadness=[]
    no_emotion=[]
    for i in range(len(r)):#50个间距
        emo = {"anger": 0, "disgust": 0, "fear": 0, "joy": 0, "sadness": 0,"no emotion":0}#50组分布
        for j in range(len(distance)):
            if distance[j]<=r[i]:
                emo[emotion_lis[j]]+=1
        total=sum(emo.values())
        for key in emo:
            emo[key]=emo[key]/(total+1)#为了防止分母为0
        #保存了200组
        anger_lis.append(emo["anger"])
        disgust_lis.append(emo["disgust"])
        fear_lis.append(emo["fear"])
        joy_lis.append(emo["joy"])
        sadness.append(emo["sadness"])
        no_emotion.append(emo["no emotion"])
    #绘制堆积图
    #堆积图预处理
    y = np.sum([anger_lis,disgust_lis], axis=0).tolist()#列表对应元素相加
    y2=np.sum([y,fear_lis], axis=0).tolist()
    y3=np.sum([y2,joy_lis], axis=0).tolist()
    y4=np.sum([y3,sadness], axis=0).tolist()
    fig,ax = plt.subplots(figsize=(50, 8), dpi=200)
    ax.bar(r, anger_lis, width=0.1, label='anger')
    ax.bar(r, disgust_lis, width=0.1, bottom=anger_lis, label='disgust')#buttom指底部从什么位置开始
    ax.bar(r, fear_lis, width=0.1, bottom=y, label='fear')
    ax.bar(r, joy_lis, width=0.1, bottom=y2, label='joy')
    ax.bar(r,sadness, width=0.1,bottom=y3,label="sadness")
    ax.bar(r,no_emotion,width=0.1, bottom=y4,label='no emotion')
    ax.legend()#生成图例
    plt.show()

我利用柱形图绘制堆积图,x轴表示搜索半径的扩大,y轴表示各类情绪的百分比

函数的参数分别为:地点列表,感情列表,起点,终点r,r设为从0到0.5截取200段的列表便于之后画堆积图

首先将地点列表化为元组,利用sum,sqrt函数计算点到点的距离,并且保存距离将距离内所有的评论的情感加入到字典中,然后对其进行标准化化为百分比的形式。对每个r的取值都进行该项操作,生成五组列表。

对于绘图,np.sum().tolist()表示的是列表的对应值相加,因为对于bar函数,需要设置bottom参数,表示从什么位置开始绘制bar,否则会出现叠加相互掩盖的现象。当然,最后不能忘了plt.show()
在这里插入图片描述

空间分布的可视化之pyechart

def location_geo(local,emotion_lis):
    geo = Geo()#生成一个用于打开查看的链接
    geo.add_schema(maptype="北京")
    emo = {'sadness': 5, 'joy': 15, 'fear': 25, 'disgust': 35, 'anger': 45}#为了便于后面画图分段用
    data=[]#用来存储数值,之后会标在地图上
    for k in range(len(emotion_lis)):
        if emotion_lis[k] != "no emotion":
            data.append((emotion_lis[k] + str(k), emo[emotion_lis[k]]))#生成单独的数值对,且不重复
            geo.add_coordinate(emotion_lis[k] + str(k), local[k][1], local[k][0])#在地图上加点,分别为测试点,坐标经度,纬度
    geo.add("北京微博情绪分布图", data, symbol_size=5)
    geo.set_series_opts(label_opts=opts.LabelOpts(is_show=False))#去除坐标点的值的大小
    pieces = [
        {'min': 1, 'max': 10, 'label': 'sadness', 'color': 'blue'},
        {'min': 10, 'max': 20, 'label': 'joy', 'color': 'yellow'},
        {'min': 20, 'max': 30, 'label': 'fear', 'color': 'cyan'},
        {'min': 30, 'max': 40, 'label': 'disgust', 'color': 'green'},
        {'min': 40, 'max': 50, 'label': 'anger', 'color': 'red'}
    ]
    geo.set_global_opts(
        visualmap_opts=opts.VisualMapOpts(is_piecewise=True, pieces=pieces),
        title_opts=opts.TitleOpts(title="北京-情绪分布"),
    )#用颜色区别数据大小
    return geo

首先生成这种可交互的图需要调用 from pyecharts.charts import Geo

对于geo.add_schema函数可以直接根据城市名称加入地图

对每个情绪赋值,对于之后的函数set_global_opts函数内的设置,需要根据区间选择点的颜色。
在这里插入图片描述

思考字典字典扩充

利用字典做情感分析我认为是简单可行的,但是,当一条评论里出现了多种情绪,单凭数量判断评论的情绪是不够的,因为他没有考虑各种情绪之间的联系,当出现负面情绪时,可能会有fear and sadness。也许可以采用机器学习的方法?寻找个词之间的关联。

以下是关于字典的自动扩充的一个思路:

首先将各个没有纳入字典的分词与五类情绪做相关性检验,如果某一分词出现的情况与joy高度正相关,但是与其他几类情绪出现情况相关性小,甚至负相关,那么可以认为该词表示了joy,就可以将其加入到字典中。这种方法也可以排除 “我”这类词汇被加入字典中。

情绪时空模式的管理意义

通过对时间模式的分析:

在营销上,可以探究人在不同情绪时,对于不同种类产品的消费增减,从而加强在这个时间点的广告投放。假设人在悲伤的时候会增加对饮食的摄入量,那么可以通过分析什么时段悲伤情绪最多,从而加强这个时段的广告推送。

对于社会治理方面,我觉得可以通过分析悲伤情绪的时间点,从而在这个时段密切关注网络的情绪,及时的救助有需要帮助的人,或者对网络进行降温,这样也许可以使得更少的人自杀或者做出一些过激行为。

通过对空间模式的分析:

在国家治理方面,可以分析一个地区的幸福度,对于负面情绪明显的地区,可以重点寻找原因,并且及时解决。

在市场方面,可以敏感的发现各地的舆情变化,从而对这种变化做出反映。例如,当上海、深圳等头部城市出现了fear、sadness大面积分布的情况时,也许可以对市场做出预警,提前做好防御措施。
产品的消费增减,从而加强在这个时间点的广告投放。假设人在悲伤的时候会增加对饮食的摄入量,那么可以通过分析什么时段悲伤情绪最多,从而加强这个时段的广告推送。

对于社会治理方面,我觉得可以通过分析悲伤情绪的时间点,从而在这个时段密切关注网络的情绪,及时的救助有需要帮助的人,或者对网络进行降温,这样也许可以使得更少的人自杀或者做出一些过激行为。

通过对空间模式的分析:

在国家治理方面,可以分析一个地区的幸福度,对于负面情绪明显的地区,可以重点寻找原因,并且及时解决。

在市场方面,可以敏感的发现各地的舆情变化,从而对这种变化做出反映。例如,当上海、深圳等头部城市出现了fear、sadness大面积分布的情况时,也许可以对市场做出预警,提前做好防御措施。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值