前言
本文详细介绍了如何使用Python的SnowNLP库进行情感分析,计算情感得分,绘制情感分布图。
具体操作步骤
一、环境配置与数据准备
1.1 安装依赖库
首先,确保已经安装了所需的库:
pip install pandas snownlp matplotlib xlsxwriter
1.2 导入模块
接着,导入我们所需要用到的库:
import pandas as pd # 导入pandas库用于数据处理
from snownlp import SnowNLP # 导入SnowNLP库用于中文文本的情感分析
import matplotlib.pyplot as plt # 导入matplotlib.pyplot库用于绘图
import matplotlib # 导入matplotlib库
import os # 导入os库用于文件操作
1.3 设置Matplotlib参数
设置Matplotlib参数,以便在绘制情感分布图时能够正确显示中文字符,解决负号显示问题:
matplotlib.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体为黑体
matplotlib.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题
1.4 定义文件路径
定义文件路径。如果输出目录不存在,就创建输出目录,便于保存情感分析的输出内容:
input_file_path = "韩束.xlsx" # 输入的Excel文件路径
output_sentiment_file = "韩束sentiment.xlsx" # 输出的情感分析结果Excel文件路径
output_folder_bar_chart = '柱状图' # 存储柱状图的文件夹路径
output_folder_combo_chart = '组合图' # 存储组合图的文件夹路径
# 确保输出文件夹存在
for folder in [output_folder_bar_chart, output_folder_combo_chart]: # 遍历所有需要创建的文件夹
if not os.path.exists(folder): # 如果文件夹不存在就创建文件夹
os.makedirs(folder)
需要注意的是,这里需要将'韩束.xlsx' 修改为自己的文件路径。
二、数据加载与处理
2.1 读取Excel数据
# 读取Excel文件
df = pd.read_excel(input_file_path)
2.2 去重
id是不同评论者的唯一标识,这里我们将id和comment均相同的数据视为重复的评论数据,进行去重操作:
df.drop_duplicates(subset=['id', 'comment'], inplace=True) # 去除重复的评论
2.3 过滤空值
df['comment'] = df['comment'].fillna('') # 将评论列中的空值填充为空字符串
df = df[df['comment'] != ''] # 过滤掉空评论
fillna()
函数用于填充 DataFrame 中的空值(NA/NaN),基本语法如下:
DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None)
参数说明:
-
value: 标量、字典、序列或 DataFrame。用于填充缺失值的值。
-
method: {‘backfill’, ‘bfill’, ‘pad’, ‘ffill’, None}。填充方法。
-
axis: {0 or ‘index’, 1 or ‘columns’}。沿着哪个轴填充。
-
inplace: bool,默认为 False。如果为 True,则在原地修改数据。
-
limit: int,默认为 None。如果方法是 pad 或 ffill,则这是连续的填充的最大数量;如果方法是 backfill 或 bfill,则这是连续的填充的最大数量。
-
downcast: dict,默认为 None。一个字典,其键是列名,其值是 numpy 数据类型。如果可能,将尝试将列转换为这些类型。
三、情感分析
3.1 定义情感分析函数
def sentiment_analysis(comment):
s = SnowNLP(comment) # 使用SnowNLP对评论进行情感分析
return s.sentiments # 返回情感得分(0-1之间)
3.2 计算情感得分
调用情感分析函数计算每条评论的情感得分:
df['sentiment'] = df['comment'].apply(sentiment_analysis) # 对每一行的评论应用情感分析函数
3.3 情感分类
根据计算得到的情感得分进行分类:
positive_df = df[df['sentiment'] >= 0.8] # 情感得分大于等于0.8的评论归类为特别喜欢
neutral_df = df[(df['sentiment'] > 0.2) & (df['sentiment'] < 0.8)] # 情感得分在0.2到0.8之间的评论归类为一般
negative_df = df[df['sentiment'] <= 0.2] # 情感得分小于等于0.2的评论归类为不喜欢
由于这里想要根据用户评论情感了解用户的偏好,分析极端情感更有意义,因此我们以0.2和0.8为界,将评论分为特别喜欢、一般和不喜欢三类。可根据自己想要分析的问题自行调整界线以及情感分类类别。
3.4 情感分类结果保存
将分类结果按照不同类别写入新Excel文件的不同工作表:
with pd.ExcelWriter(output_sentiment_file, engine='xlsxwriter') as writer: # 使用pd.ExcelWriter打开一个新的Excel文件
positive_df.to_excel(writer, sheet_name='特别喜欢', index=False) # 将特别喜欢的评论写入“特别喜欢”工作表
neutral_df.to_excel(writer, sheet_name='一般', index=False) # 将一般的评论写入“一般”工作表
negative_df.to_excel(writer, sheet_name='不喜欢', index=False) # 将不喜欢的评论写入“不喜欢”工作表
print("情感分析完成,结果已保存到'{}'文件中。".format(output_sentiment_file)) # 打印提示信息
四、结果可视化
4.1 数据合并
先将各工作表的数据合并,这里需要新增一列emotion用于记录评论所属情感类别。用于前面写入数据时不同情感类别评论写入了以所属类别命名的工作表中,因此这里的emotion值为评论所属的工作表的表名。
sheets = ['特别喜欢', '一般', '不喜欢'] # 定义要绘制柱状图的工作表列表
all_data = pd.DataFrame() # 初始化一个空的DataFrame来存储所有数据
# 读取所有工作表的数据,并合并到一个DataFrame中
for sheet in sheets:
df = pd.read_excel(output_sentiment_file, sheet_name=sheet) # 从Excel文件中读取对应的工作表数据
df['emotion'] = sheet # 添加一列表示情感分类
all_data = pd.concat([all_data, df], ignore_index=True) # 将新读取的数据追加到all_data中
4.2 柱状图
首先绘制整体评论的情感分布柱状图:
plt.figure(figsize=(10, 6)) # 创建一个新的图形对象,大小为10x6英寸
overall_sentiment = all_data.groupby('emotion').size() # 计算每个情感分类的数量
overall_sentiment.plot(kind='bar', color=['#FF9DA7', '#91CC70', '#FFC425']) # 绘制柱状图,指定颜色
plt.title('整体情感分布') # 设置图表标题
plt.xlabel('情感分类') # 设置X轴标签
plt.ylabel('评论数量') # 设置Y轴标签
for i, value in enumerate(overall_sentiment):
plt.text(i, value + 1, str(value), ha='center') # 在柱子上方显示数值
plt.tight_layout() # 自动调整子图参数,使其填充整个图像区域
plt.xticks(rotation=0) # 设置X轴刻度旋转角度为0度
plt.savefig(os.path.join(output_folder_bar_chart, '整体情感分布柱状图.png')) # 保存图表为PNG文件
plt.close() # 关闭当前图形对象
接着按评论商品的类别将各评论进行分组,并分别读取不同类别商品的评论相关数据,绘制各类别的情感分布柱状图:
category_sentiment = all_data.groupby(['catagory', 'emotion']).size().unstack(fill_value=0) # 计算每个商品类别的情感分布
# 遍历每个商品类别,为每个类别绘制情感柱状图
for category, sentiment_counts in category_sentiment.iterrows():
plt.figure(figsize=(10, 6)) # 创建一个新的图形对象,大小为10x6英寸
sentiment_counts.plot(kind='bar', color=['#FF9DA7', '#91CC70', '#FFC425']) # 绘制柱状图,指定颜色
plt.title(f'{category}情感分布') # 设置图表标题
plt.xlabel('情感分类') # 设置X轴标签
plt.ylabel('评论数量') # 设置Y轴标签
for i, value in enumerate(sentiment_counts):
plt.text(i, value + 1, str(value), ha='center') # 在柱子上方显示数值
plt.tight_layout() # 自动调整子图参数,使其填充整个图像区域
plt.xticks(rotation=0) # 设置X轴刻度旋转角度为0度
output_file = os.path.join(output_folder_bar_chart, f'{category}_情感分布柱状图.png') # 构建输出文件名
plt.savefig(output_file) # 保存图表为PNG文件
plt.close() # 关闭当前图形对象
print(f"整体和不同功能类别的情感分布柱状图已保存到'{output_folder_bar_chart}'文件夹中。") # 打印提示信息
4.3 组合图
组合图在情感分布柱状图的基础上增加了情感值平均分的折线图,可以从全面的视角更深入地了解用户对不同商品类别的整体满意程度和不满意程度。由于我们特别关注极端的情感反应,且前面的柱状图已经分析了不同情感类别的分布情况,因此这里的组合图过滤掉了情感类别为“一般”的评论。(这里可根据需求修改,可以使用全部数据不进行过滤)
filtered_data = all_data[all_data['emotion'] != '一般'] # 过滤“一般”类别的评论
接着分别计算绘制组合图所需的数据,如情感平均分、各情感类别评论数量,并保存到新Excel文件:
# 计算每个商品类别的特别喜欢和不喜欢的评论数量
category_sentiment_counts = filtered_data.groupby(['catagory', 'emotion']).size().unstack(fill_value=0)
# 计算每个商品类别的情感值平均分
category_average_sentiment = filtered_data.groupby('catagory')['sentiment'].mean()
# 将结果保存到新的Excel文件中
output_file_path = os.path.join(output_folder_combo_chart, '情感分析结果.xlsx')
with pd.ExcelWriter(output_file_path, engine='xlsxwriter') as writer:
category_sentiment_counts.to_excel(writer, sheet_name='情感数量')
category_average_sentiment.to_frame(name='情感值平均分').reset_index().to_excel(writer, sheet_name='情感值平均分', index=False)
print(f"情感分析结果已保存到'{output_file_path}'文件中。")
然后使用前面计算的数据绘制组合图:
fig, ax1 = plt.subplots(figsize=(12, 8)) # 创建一个新的图形对象,大小为12x8英寸
color1 = '#FF9DA7' # 特别喜欢的颜色
color2 = '#FFC425' # 不喜欢的颜色
width = 0.35 # 柱状图的宽度
categories = category_sentiment_counts.index
bar1 = ax1.bar(categories, category_sentiment_counts['特别喜欢'], width, label='特别喜欢', color=color1) # 绘制特别喜欢的柱状图
bar2 = ax1.bar(categories, category_sentiment_counts['不喜欢'], width, bottom=category_sentiment_counts['特别喜欢'].values, label='不喜欢', color=color2) # 绘制不喜欢的柱状图
ax1.set_xlabel('功能类别') # 设置X轴标签
ax1.set_ylabel('评论数量', color='tab:blue') # 设置左侧Y轴标签及其颜色
ax1.set_title('不同类别商品情感分布图') # 设置图表标题
ax1.tick_params(axis='y', labelcolor='tab:blue') # 设置左侧Y轴刻度标签颜色
ax2 = ax1.twinx() # 创建共享X轴的第二个坐标轴
ax2.plot(categories, category_average_sentiment, label='情感值平均分', color='green', marker='o') # 绘制情感平均分折线图
ax2.set_ylabel('情感值平均分', color='green') # 设置右侧Y轴标签及其颜色
fig.legend(loc="upper left", bbox_to_anchor=(0.1, 0.9), ncol=2) # 设置图例位置
plt.tight_layout() # 自动调整子图参数,使其填充整个图像区域
plt.savefig(os.path.join(output_folder_combo_chart, '不同功能类别情感分布及情感值平均分组合图.png')) # 保存图表为PNG文件
plt.close() # 关闭当前图形对象
print(f"不同功能类别情感分布及情感值平均分组合图已保存到'{output_folder_combo_chart}'文件夹中。") # 打印提示信息
结语
如果大家觉得有帮助,请多多点赞收藏支持一下,谢谢大家。如果遇到什么问题,也非常欢迎大家私信。