Bootstrap

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

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

赛题背景

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

赛题数据

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

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

赛题任务

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

代码实现

前情提要:
这个代码我是用Jupyter写的,可能在Github上看效果更好些。我在Github上传了另一个我的情感分析项目,比这个设计的内容要更全面,这是呢个项目的CSDN地址GitHub项目地址
这个耳机评论数据还是挺不好的,我不知道是不是故意的,让你把它变好?根据下面的前几张图,起初我以为题目会让你预测那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
    
  2. 读取数据

    data = pd.read_csv('./earphone_sentiment.csv')
    data
    

    在这里插入图片描述

  3. 观察值的情况

    data['subject'].unique() 
    

    在这里插入图片描述

    data['sentiment_word'].unique() 
    

    在这里插入图片描述

    data['sentiment_value'].unique() 
    

    在这里插入图片描述

  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()
    

    在这里插入图片描述

  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()
    

    在这里插入图片描述

  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()
    

    在这里插入图片描述

  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()
    

    在这里插入图片描述

  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()
    

    在这里插入图片描述

  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
    
  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
    

    在这里插入图片描述

  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()
    
  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()
    

    在这里插入图片描述

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

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

    在这里插入图片描述

  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()
    

    在这里插入图片描述

  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()
    

    在这里插入图片描述

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;