赛题以网络舆情分析为背景,要求选手根据用户的评论来对品牌的议题进行数据分析与可视化。通过这道赛题来引导常用的数据可视化图表,以及数据分析方法,对感兴趣的内容进行探索性数据分析。
数据源: earphone_sentiment.csv,为10000+条行业用户关于耳机的评论
字段名称 | 类型 | 描述 | 说明 |
---|---|---|---|
content_id | Int | 数据ID | / |
content | String | 文本内容 | / |
subject | String | 主题 | 提取或依据上下文归纳出来的主题 |
sentiment_value | Int | 情感分析 | 分析出的情感 |
sentiment_word | String | 情感词 | 情感词 |
前情提要:
这个代码我是用Jupyter写的,可能在Github上看效果更好些。我在Github上传了另一个我的情感分析项目,比这个设计的内容要更全面,这是呢个项目的CSDN地址,GitHub项目地址。
这个耳机评论数据还是挺不好的,我不知道是不是故意的,让你把它变好?根据下面的前几张图,起初我以为题目会让你预测那70%的地方,但根本没法这么做,像sentiment_value ,sentiment_word你可以仔细看一下特别扯。下面的话,我仅仅去按找赛题任务,去编写代码实现一些画图。
画图的话,可以参考Seaborn,有些没有的就用最原始的Matplotlib。
导入常用的一些数据处理的包
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
读取数据
data = pd.read_csv('./earphone_sentiment.csv')
data
观察值的情况
data['subject'].unique()
data['sentiment_word'].unique()
data['sentiment_value'].unique()
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()
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()
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()
查看评论长度大小的分布
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()
评论长度直方图
# 将大于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()
对数据的一些简单处理
"""
删除重复数据
"""
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
开始分词
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
处理停用词(我,的,地,这种没什么实际意义的词)
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()
绘制云词图
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()
绘制柱状图(不同主题,不同情感)
plt.figure(figsize=(8,6))
sns.histplot(data=data, x="subject", hue="sentiment_value", multiple="dodge", shrink=.8)
plt.title('不同主题,不同情感')
plt.show()
绘制柱状图(不同情感词,不同情感)
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()
绘制热力图(不同情感词,不同主题)
# 创建透视表(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()