• 天池:数据分析达人赛1:用户情感可视化分析


    【教学赛】数据分析达人赛1:用户情感可视化分析

    赛题背景

    赛题以网络舆情分析为背景,要求选手根据用户的评论来对品牌的议题进行数据分析与可视化。通过这道赛题来引导常用的数据可视化图表,以及数据分析方法,对感兴趣的内容进行探索性数据分析。

    赛题数据

    数据源: earphone_sentiment.csv,为10000+条行业用户关于耳机的评论

    字段名称类型描述说明
    content_idInt数据ID/
    contentString文本内容/
    subjectString主题提取或依据上下文归纳出来的主题
    sentiment_valueInt情感分析分析出的情感
    sentiment_wordString情感词情感词

    赛题任务

    1. 词云可视化(评论中的关键词,不同情感的词云)
    2. 柱状图(不同主题,不同情感,不同情感词)
    3. 相关性系数热力图(不同主题,不同情感,不同情感词)

    代码实现

    前情提要:
    这个代码我是用Jupyter写的,可能在Github上看效果更好些,这是项目地址:xxx
    这个耳机评论数据还是挺不好的,我不知道是不是故意的,让你把它变好?根据下面的前几张图,起初我以为题目会让你预测那70%的地方,但根本没法这么做,像sentiment_value ,sentiment_word你可以仔细看一下特别扯。下面的话,我仅仅去按找赛题任务,去编写代码实现一些画图。
    画图的话,可以参考Seaborn,有些没有的就用最原始的Matplotlib。

    1. 导入常用的一些数据处理的包

      import pandas as pd
      import numpy as np
      from matplotlib import pyplot as plt
      import seaborn as sns
      import warnings
      from pylab import *
      import jieba
      import jieba.posseg as pseg
      from wordcloud import WordCloud, STOPWORDS #导入模块worldcloud
      # from PIL import Image
      
      warnings.filterwarnings('ignore')
      # sns.set_context("talk", font_scale=0.5, rc={"lines.linewidth":4})
      sns.set(font_scale=1.2)
      sns.set(rc={"lines.linewidth":1})
      sns.set_style("whitegrid")
      # 设置中文
      mpl.rcParams['font.sans-serif'] = u'SimHei'
      plt.rcParams['axes.unicode_minus'] = False
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
    2. 读取数据

      data = pd.read_csv('./earphone_sentiment.csv')
      data
      
      • 1
      • 2

      在这里插入图片描述

    3. 观察值的情况

      data['subject'].unique() 
      
      • 1

      在这里插入图片描述

      data['sentiment_word'].unique() 
      
      • 1

      在这里插入图片描述

      data['sentiment_value'].unique() 
      
      • 1

      在这里插入图片描述

    4. subject分布图绘制

      df_subject = data['subject'].reset_index()
      df_subject.columns = ["Count", "subject"]
      df_subject = df_subject.groupby("subject").aggregate('count').reset_index()
      df_subject.sort_values(by="Count" , inplace=True, ascending=True) 
      # 把‘外形’,‘功能’,‘舒适’归结为一个
      sizes = df_subject['Count'].tolist()
      labels = df_subject['subject'].tolist()
      sizes = [sizes[0] + sizes[1] + sizes[2]] + sizes[3:]
      labels = ['外形,功能,舒适'] + labels[3:]
      _, axes=plt.subplots(1, 2, figsize=(20, 8))
      plt.subplot(1, 2, 1)
      plt.pie(sizes, labels=labels, autopct='%1.1f%%',shadow=False, radius=1)
      df_subject.sort_values(by="Count" , inplace=True, ascending=False)
      sns.barplot(x='subject', y='Count', data=df_subject, color='salmon', ax=axes[1])
      # plt.title("subject分布图")
      plt.suptitle('subject分布图')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      在这里插入图片描述

    5. sentiment_word分布图绘制

      df_subject = data['sentiment_word'].reset_index()
      nan_number = df_subject['sentiment_word'].isnull().sum ()
      df_subject.columns = ["Count", "sentiment_word"]
      # aggregate统计不了Nan
      df_subject = df_subject.groupby("sentiment_word").aggregate('count').reset_index()
      df_subject.sort_values(by="Count" , inplace=True, ascending=True) 
      sizes = df_subject['Count'].tolist()
      labels = df_subject['sentiment_word'].tolist()
      n = 0
      sum_ = 0
      for i in sizes:
          if i < 100:
              n += 1
              sum_ += i
      sizes = sizes[n:] + [nan_number, sum_]
      labels = labels[n:] + ['NaN', '其他(' + str(n) + ")"]
      new_sizes = sorted(sizes)
      sizes = sorted(enumerate(sizes), key=lambda x : x[1])
      new_labels = []
      for i in sizes:
          new_labels.append(labels[i[0]])
      
      _, axes=plt.subplots(1, 2, figsize=(20, 8))
      plt.subplot(1, 2, 1)
      plt.pie(new_sizes, labels=new_labels, autopct='%1.1f%%',shadow=False, radius=1.2)
      sns.barplot(x=new_labels[::-1], y=new_sizes[::-1], color='salmon', ax=axes[1])
      axes[1].set_ylabel('Count')
      axes[1].set_xlabel('sentiment_word')
      plt.suptitle('sentiment_word分布图')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30

      在这里插入图片描述

    6. sentiment_value分布图绘制

      df_subject = data['sentiment_value'].reset_index()
      df_subject.columns = ["Count", "sentiment_value"]
      df_subject = df_subject.groupby("sentiment_value").aggregate('count').reset_index()
      df_subject.sort_values(by="Count", inplace=True, ascending=True) 
      _, axes=plt.subplots(1, 2, figsize=(20, 8))
      plt.subplot(1, 2, 1)
      plt.pie(df_subject['Count'], labels=df_subject['sentiment_value'], autopct='%1.1f%%',shadow=False, radius=1.2)
      df_subject.sort_values(by="Count", inplace=True, ascending=False)
      # 这里必须加order属性,如果不加会按-1,0,1这样的由大到小的顺序
      # 上面两个图没加还没问题,可能是因为x轴是str,所以又去根据y轴排序
      sns.barplot(x='sentiment_value', y='Count', data=df_subject, color='salmon', order=df_subject["sentiment_value"], ax=axes[1])
      plt.suptitle('sentiment_value分布图')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13

      在这里插入图片描述

    7. 查看评论长度大小的分布

      length = pd.DataFrame(columns=['length'])
      for index, row in data.iterrows():
          length.loc[index] = len(row['content'])
          
      plt.figure(figsize=(8,6))
      sns.scatterplot(data=length, x=range(length.shape[0]), y=np.sort(length['length']))
      plt.xlabel('index')
      plt.ylabel('length')
      plt.title('content长度大小分布')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      在这里插入图片描述

    8. 评论长度直方图

      # 将大于150的点归为150
      length['length'] = length['length'].map(lambda x: 150 if x > 150 else x)
      
      plt.figure(figsize=(8,6))
      sns.histplot(data=length, x="length", bins=70, binrange=(0,151))
      plt.title('content长度直方图')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      在这里插入图片描述

    9. 对数据的一些简单处理

      """
      删除重复数据
      """
      data = data.drop_duplicates(subset='content', keep='first')
      data  # 17176 -> 16883
      
      """
      删除标点符号或英文字母过多的评论
      """
      count = []
      for index, row in data.iterrows():
          other = 0
          chinese = 0
          for i in row['content']:
              if '\u4e00' <= i <= '\u9fa5':
                  chinese += 1
              else:
                  other += 1
          if chinese < other:
              count.append(index)
      for i in count:
          data = data.drop(i)
      data  # 17176 -> 16883 -> 15058
      
      """
      删除过长(250)和过短(5)的评论
      """
      data = data.drop(data[data['content'].str.len() < 5].index)
      data = data.drop(data[data['content'].str.len() > 250].index)
      data  # 17176 -> 16883 -> 15058 -> 14387
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
    10. 开始分词

      def fenci(text):
      #     cut_word = [i for i in list(jieba.cut(text)) if '\u4e00' <= i <= '\u9fff']
          cut_word = [i for i in list(jieba.cut(text)) if '\u4e00' <= i <= '\u9fff' or '\u0030' <= i <= '\u0039' or '\u0061' <= i <= '\u007a' or '\u0041' <= i <= '\u005a']
          text = ' '.join(cut_word)
          return text
      
      data = data.reset_index(drop=True)
      data['分词后'] = ''
      for i in range(len(data)):
          data['分词后'].iloc[i] = fenci(data['content'].iloc[i])
      data
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      在这里插入图片描述

    11. 处理停用词(我,的,地,这种没什么实际意义的词)

      stopwords = [i.strip() for i in open('./stoplist.txt', 'r').readlines()] 
      
      pos_data = data[data['sentiment_value'] == 1]
      neg_data = data[data['sentiment_value'] == -1]
      neu_data = data[data['sentiment_value'] == 0]
      pos_str = ''
      neg_str = ''
      neu_str = ''
      
      str_ = ''
      list_ = []
      for index, row in pos_data.iterrows():
          str_ += row['分词后'] + ' '
      list_ = str_.split(' ')
      for i in list_:
          if i not in stopwords:
              # remove有坑,会删不尽
              # list_.remove(i)
              pos_str += i + ' '
      pos_str = pos_str.rstrip()
          
      str_ = ''
      list_ = []
      for index, row in neg_data.iterrows():
          str_ += row['分词后'] + ' '
      list_ = str_.split(' ')
      for i in list_:
          if i not in stopwords:
              neg_str += i + ' '
      neg_str = neg_str.rstrip()
      
      str_ = ''
      list_ = []
      for index, row in neu_data.iterrows():
          str_ += row['分词后'] + ' '
      list_ = str_.split(' ')
      for i in list_:
          if i not in stopwords:
              neu_str += i + ' '
      neu_str = neu_str.rstrip()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
    12. 绘制云词图

      plt.figure(figsize=(8,6))
      wordcloud = WordCloud(font_path='./msyh.ttf',background_color='white', width=1800, height=800, max_words=300, max_font_size=180).generate(pos_str)
      plt.imshow(wordcloud)
      plt.axis('off')
      plt.title('积极情感词云图')
      plt.show()
      
      plt.figure(figsize=(8,6))
      wordcloud = WordCloud(font_path='./msyh.ttf',background_color='white', width=1800, height=800, max_words=300, max_font_size=180).generate(neg_str)
      plt.imshow(wordcloud)
      plt.axis('off')
      plt.title('消极情感词云图')
      plt.show()
      
      plt.figure(figsize=(8,6))
      wordcloud = WordCloud(font_path='./msyh.ttf',background_color='white', width=1800, height=800, max_words=300, max_font_size=180).generate(neu_str)
      plt.imshow(wordcloud)
      plt.axis('off')
      plt.title('中性情感词云图')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

      在这里插入图片描述

    13. 绘制柱状图(不同主题,不同情感)

      plt.figure(figsize=(8,6))
      sns.histplot(data=data, x="subject", hue="sentiment_value", multiple="dodge", shrink=.8)
      plt.title('不同主题,不同情感')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4

      在这里插入图片描述

    14. 绘制柱状图(不同情感词,不同情感)

      plt.figure(figsize=(8,6))
      sns.histplot(data=pos_data, x="sentiment_word", shrink=.8)
      plt.title('不同情感词,不同情感(积极)')
      plt.show()
      
      plt.figure(figsize=(8,6))
      sns.histplot(data=neg_data, x="sentiment_word", shrink=.8)
      plt.title('不同情感词,不同情感(消极)')
      plt.show()
      
      neu_data['sentiment_word'] = neu_data['sentiment_word'].fillna('NaN')
      plt.figure(figsize=(8,6))
      sns.histplot(data=neu_data, x="sentiment_word", shrink=.8)
      plt.title('不同情感词,不同情感(中性)')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

      在这里插入图片描述

    15. 绘制热力图(不同情感词,不同主题)

      # 创建透视表(count技术,np.sum求和)
      pivot_data = data.pivot_table(columns='sentiment_word',index='subject',values='sentiment_value',aggfunc="count")
      pivot_data = pivot_data.fillna(0)
      
      plt.figure(figsize=(16,12))
      sns.heatmap(pivot_data, annot=True, fmt=".0f", cmap="YlGnBu")
      plt.title('不同情感词,不同主题')
      plt.show()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      在这里插入图片描述

  • 相关阅读:
    2022 年 25 大 Java 8 面试问题和答案 - 从基础到有经验
    java基于微信小程序的美食制作教程系统 uniapp 小程序
    文案配音软件哪个好?(适合新手使用)
    PostgreSQL11 | pgadmin4基本使用
    大二Web课程设计:HTML+CSS学校静态网页设计——南京师范大学泰州学院(11页)
    C++设计模式-外观(Facade)
    web前端期末大作业【足球网页】学生网页设计作业源码
    Bert基础(二)--多头注意力
    <Rust><iced><resvg>基于rust使用iced构建GUI实例:使用resvg库实现svg转png
    推荐 NestJs 官方教程
  • 原文地址:https://blog.csdn.net/Fishermen_sail/article/details/125350120