连接数据库的:
import pandas as pd
import pymysql
from sqlalchemy import create_engine
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from ipywidgets import interact, widgets
import re
import jieba
from collections import Counter
from wordcloud import WordCloud
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体为 SimHei(或其他中文字体名)
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题
# 1. MySQL连接与数据加载
def create_mysql_connection():
"""创建MySQL数据库连接"""
connection = pymysql.connect(
host='localhost',
user='root',
password='123456',
database='qingxi',
charset='utf8mb4'
)
return connection
# 使用SQLAlchemy创建引擎
engine = create_engine('mysql+pymysql://root:123456@localhost/big_data_review')
def load_review_data():
"""从MySQL加载用户评论数据"""
query = "SELECT * FROM wukong_cleaned"
df = pd.read_sql(query, engine)
# 确保日期列是datetime类型
df['发布时间(2024年)'] = pd.to_datetime(df['发布时间(2024年)'])
return df
# 2. 数据预处理
def extract_user_id(df):
"""从用户链接提取用户ID"""
pattern = r'(?:id/|profiles/)([^/]+)'
df['用户ID_extracted'] = df['用户链接'].apply(
lambda x: re.search(pattern, x).group(1) if pd.notnull(x) else None)
return df
# 定义情感词库(可根据实际情况扩充)
positive_words = set(['好玩', '优秀', '精彩', '推荐', '喜欢', '爱', '棒', '出色', '惊艳', '神作',
'经典', '完美', '流畅', '惊喜', '超值', '值得', '上瘾', '沉浸', '刺激', '欢乐',
'满意', '好评', '支持', '买爆', '无敌', '顶级', '神级', '完美', '绝了', '良心',
'诚意', '爽快', '震撼', '创新', '独特', '优质', '舒服', '过瘾', '享受', '满意',
'感动', '值得一玩', '无可挑剔', '诚意满满', '佳作', '精品', '诚意之作'])
negative_words = set(['垃圾', '差评', '失望', '糟糕', '后悔', '不值', '无聊', '烂', '坑', '骗钱',
'卡顿', '掉线', '闪退', 'bug', '问题', '差劲', '恶心', '弃坑', '卸载', '别买',
'垃圾游戏', '浪费时间', '骗局', '垃圾制作', '千万别买', '坑爹', '垃圾优化', '失望透顶',
'垃圾画质', '操作烂', '平衡差', '毁了', '垃圾玩法', '垃圾内容', '垃圾策划', '优化差',
'体验差', 'bug多', '太烂', '无法玩', '极差', '弃游', '退游', '不推荐', '避雷'])
neutral_words = set(['游戏', '感觉', '比较', '有点', '可能', '应该', '可以', '一般', '还行', '算是',
'大概', '或许', '似乎', '某种', '某种程度', '某种意义', '某种方面', '某种角度',
'来说', '而已', '方面', '角度', '整体', '部分', '内容', '方式', '模式', '元素',
'问题', '情况', '表现', '效果', '体验', '感觉', '样子', '东西', '部分', '时间',
'时候', '期间', '过程', '阶段', '水平', '程度', '质量', '数量', '关系', '原因',
'理由', '结果', '影响', '意义', '价值', '作用', '功能', '特点', '特色', '优势',
'缺点', '不足', '局限', '限制', '方面', '角度', '方式', '方法', '手段', '途径',
'形式', '内容', '结构', '组织', '系统', '机制', '规则', '玩法', '操作', '画面',
'画质', '音乐', '音效', '剧情', '故事', '角色', '任务', '关卡', '难度', '挑战',
'乐趣', '趣味', '上瘾', '沉浸', '流畅', '卡顿', '优化', '配置', '要求', '支持',
'兼容', '稳定', '更新', '维护', '服务', '客服', '态度', '反馈', '建议', '意见',
'希望', '期待', '改进', '提升', '完善', '加强', '增加', '减少', '调整', '优化',
'修复', '解决', '处理', '出现', '发生', '遇到', '发现', '存在', '需要', '应该',
'必须', '可以', '可能', '或许', '大概', '似乎', '好像', '感觉', '认为', '觉得',
'看来', '看样子', '有点', '稍微', '比较', '相当', '非常', '极其', '特别', '格外',
'有点', '稍微', '多少', '有点', '略微', '还算', '还可以', '过得去', '一般般',
'普普通通', '平平淡淡', '中规中矩', '马马虎虎', '凑合', '将就', '勉强', '其实',
'实际上', '事实上', '真的', '确实', '的确', '到底', '究竟', '到底是', '究竟是',
'就是', '正是', '才是', '而是', '不是', '并非', '只是', '仅仅', '不过', '而已',
'罢了', '就是说', '换句话说', '也就是说', '即', '即', '比如', '例如', '像', '如同',
'犹如', '好像', '仿佛', '似乎', '如同', '类似', '相似', '一样', '同样', '相同',
'不同', '差异', '区别', '分别', '对比', '比较', '相比', '比起', '与...相比', '和...一样',
'跟...一样', '如同...一样', '好像...一样', '仿佛...一样', '似乎...一样', '比', '比较',
'更加', '越发', '越来越', '越...越...', '不但', '不仅', '而且', '并且', '甚至', '乃至',
'或者', '还是', '要么', '不是...就是...', '与其...不如...', '宁可...也不...', '因为',
'由于', '所以', '因此', '因而', '可见', '既然', '那么', '就', '则', '于是', '从而',
'但是', '可是', '然而', '不过', '只是', '尽管', '虽然', '即使', '哪怕', '就算', '不管',
'无论', '不论', '都', '也', '还', '再', '又', '也', '才', '就', '只有', '只要', '一旦',
'如果', '要是', '假如', '倘若', '除非', '否则', '不然', '否则就', '要不然', '要么',
'或者', '还是', '是...还是...', '不管...还是...', '无论...还是...', '不但...而且...',
'不仅...还...', '虽然...但是...', '尽管...可是...', '即使...也...', '哪怕...也...',
'宁可...也不...', '与其...不如...', '因为...所以...', '由于...因此...', '既然...那么...',
'只有...才...', '只要...就...', '一旦...就...', '如果...那么...', '要是...就...',
'假如...就...', '倘若...就...', '除非...否则...', '不是...而是...', '不但不...反而...',
'连...也...', '甚至...也...', '尚且...何况...', '何况', '况且', '再说', '此外', '另外',
'而且', '并且', '同时', '尤其', '特别', '特别是', '主要是', '关键是', '首先', '其次',
'再次', '最后', '总之', '综上所述', '由此可见', '看来', '看样子', '显然', '明显',
'确实', '的确', '真的', '非常', '极其', '特别', '格外', '尤其', '更加', '越发',
'越来越', '越...越...', '多么', '何等', '多么地', '何等的', '这么', '那么', '这样',
'那样', '如此', '这般', '怎么', '怎样', '如何', '为什么', '什么', '哪里', '何时',
'谁', '哪个', '哪些', '所有', '一切', '全部', '整个', '任何', '每个', '各自', '分别',
'独自', '一起', '共同', '互相', '彼此', '相互', '都', '全', '总', '共', '一共', '总共',
'仅仅', '只', '才', '就', '已经', '刚刚', '马上', '立刻', '即将', '就要', '快要',
'马上就要', '即将要', '就要到', '快要到', '到', '在', '当', '于', '趁', '随着', '自从',
'直到', '在...之前', '在...之后', '在...时候', '在...期间', '在...中', '在...里',
'在...之上', '在...之下', '在...左边', '在...右边', '在...前面', '在...后面', '在...旁边',
'在...附近', '在...周围', '在...中间', '在...内部', '在...外部', '在...里面', '在...外面',
'向', '朝', '往', '到', '去', '来', '回', '从', '自', '由', '由...组成', '由...构成',
'根据', '按照', '依照', '遵照', '为了', '为', '替', '给', '对', '对于', '关于', '至于',
'和', '跟', '与', '同', '及', '以及', '并', '并且', '而', '而且', '或', '或者', '还是',
'既...又...', '一边...一边...', '一面...一面...', '不是...而是...', '不但...而且...',
'不仅...还...', '虽然...但是...', '尽管...可是...', '即使...也...', '哪怕...也...',
'宁可...也不...', '与其...不如...', '因为...所以...', '由于...因此...', '既然...那么...',
'只有...才...', '只要...就...', '一旦...就...', '如果...那么...', '要是...就...',
'假如...就...', '倘若...就...', '除非...否则...', '是否', '是不是', '有没有', '是不是...',
'有没有...', '呢', '吗', '吧', '啊', '呀', '哇', '哦', '哟', '哎', '唉', '哼', '哈', '嘿',
'哎呀', '哎哟', '啊呀', '哇塞', '天哪', '哎呀妈呀', '我的天', '真的吗', '是不是', '有没有',
'难道', '莫非', '究竟', '到底', '到底是', '究竟是', '就是', '正是', '才是', '而是', '不是',
'并非', '只是', '仅仅', '不过', '而已', '罢了', '就是说', '换句话说', '也就是说', '即', '即',
'比如', '例如', '像', '如同', '犹如', '好像', '仿佛', '似乎', '如同', '类似', '相似', '一样',
'同样', '相同', '不同', '差异', '区别', '分别', '对比', '比较', '相比', '比起', '与...相比',
'和...一样', '跟...一样', '如同...一样', '好像...一样', '仿佛...一样', '似乎...一样', '比',
'比较', '更加', '越发', '越来越', '越...越...', '不但', '不仅', '而且', '并且', '甚至', '乃至',
'或者', '还是', '要么', '不是...就是...', '与其...不如...', '宁可...也不...', '因为', '由于',
'所以', '因此', '因而', '可见', '既然', '那么', '就', '则', '于是', '从而', '但是', '可是',
'然而', '不过', '只是', '尽管', '虽然', '即使', '哪怕', '就算', '不管', '无论', '不论', '都',
'也', '还', '再', '又', '也', '才', '就', '只有', '只要', '一旦', '如果', '要是', '假如', '倘若',
'除非', '否则', '不然', '否则就', '要不然', '要么', '或者', '还是', '是...还是...', '不管...还是...',
'无论...还是...', '不但...而且...', '不仅...还...', '虽然...但是...', '尽管...可是...', '即使...也...',
'哪怕...也...', '宁可...也不...', '与其...不如...', '因为...所以...', '由于...因此...', '既然...那么...',
'只有...才...', '只要...就...', '一旦...就...', '如果...那么...', '要是...就...', '假如...就...',
'倘若...就...', '除非...否则...', '不是...而是...', '不但不...反而...', '连...也...', '甚至...也...',
'尚且...何况...', '何况', '况且', '再说', '此外', '另外', '而且', '并且', '同时', '尤其', '特别',
'特别是', '主要是', '关键是', '首先', '其次', '再次', '最后', '总之', '综上所述', '由此可见',
'看来', '看样子', '显然', '明显', '确实', '的确', '真的', '非常', '极其', '特别', '格外', '尤其',
'更加', '越发', '越来越', '越...越...', '多么', '何等', '多么地', '何等的', '这么', '那么', '这样',
'那样', '如此', '这般', '怎么', '怎样', '如何', '为什么', '什么', '哪里', '何时', '谁', '哪个',
'哪些', '所有', '一切', '全部', '整个', '任何', '每个', '各自', '分别', '独自', '一起', '共同',
'互相', '彼此', '相互', '都', '全', '总', '共', '一共', '总共', '仅仅', '只', '才', '就', '已经',
'刚刚', '马上', '立刻', '即将', '就要', '快要', '马上就要', '即将要', '就要到', '快要到', '到',
'在', '当', '于', '趁', '随着', '自从', '直到', '在...之前', '在...之后', '在...时候', '在...期间',
'在...中', '在...里', '在...之上', '在...之下', '在...左边', '在...右边', '在...前面', '在...后面',
'在...旁边', '在...附近', '在...周围', '在...中间', '在...内部', '在...外部', '在...里面', '在...外面',
'向', '朝', '往', '到', '去', '来', '回', '从', '自', '由', '由...组成', '由...构成', '根据', '按照',
'依照', '遵照', '为了', '为', '替', '给', '对', '对于', '关于', '至于', '和', '跟', '与', '同', '及',
'以及', '并', '并且', '而', '而且', '或', '或者', '还是', '既...又...', '一边...一边...', '一面...一面...'])
# 情感词汇分类
emotion_categories = {
'positive': {
'游戏体验': ['好玩', '精彩', '刺激', '欢乐', '沉浸', '上瘾', '爽快', '震撼', '舒服', '过瘾', '享受'],
'游戏质量': ['优秀', '出色', '惊艳', '神作', '经典', '完美', '流畅', '优质', '独特', '创新', '无可挑剔'],
'推荐程度': ['推荐', '喜欢', '爱', '棒', '超值', '值得', '买爆', '良心', '诚意', '佳作', '精品', '诚意之作'],
'满意度': ['满意', '好评', '支持', '惊喜', '感动', '满意', '无可挑剔']
},
'negative': {
'游戏体验': ['无聊', '卡顿', '掉线', '闪退', '浪费时间', '体验差', '操作烂', '无法玩', '弃游', '退游', '避雷'],
'游戏质量': ['垃圾', '烂', '差劲', '垃圾游戏', '垃圾制作', '垃圾优化', '垃圾画质', '太烂', '极差', '优化差', 'bug多'],
'不推荐': ['差评', '失望', '糟糕', '后悔', '不值', '别买', '千万别买', '坑爹', '不推荐', '弃坑', '卸载'],
'问题反馈': ['坑', '骗钱', 'bug', '问题', '骗局', '毁了', '垃圾玩法', '垃圾内容', '垃圾策划', '平衡差']
}
}
def chinese_word_segmentation(text):
"""使用结巴分词进行中文分词"""
if pd.isnull(text):
return []
# 使用结巴分词
words = jieba.cut(text)
# 过滤停用词和单字
stopwords = set(['的', '了', '和', '是', '我', '在', '有', '也', '都', '不', '就', '很', '着', '到', '上', '下', '来', '去', '得', '地', '把', '被', '对', '对于', '关于', '啊', '呀', '哇', '呢', '吗', '吧', '啊', '呀', '哇', '哦', '哟', '哎', '唉', '哼', '哈', '嘿', '哎呀', '哎哟', '啊呀', '哇塞', '天哪', '哎呀妈呀', '我的天', '真的', '确实', '的确', '非常', '极其', '特别', '格外', '尤其', '更加', '越发', '越来越', '越', '多', '少', '大', '小', '高', '低', '长', '短', '好', '坏', '快', '慢', '前', '后', '左', '右', '上', '下', '里', '外', '中', '间', '内', '外', '旁', '边', '附近', '周围', '中间', '内部', '外部', '里面', '外面', '向', '朝', '往', '到', '去', '来', '回', '从', '自', '由', '由', '组成', '构成', '根据', '按照', '依照', '遵照', '为了', '为', '替', '给', '对', '对于', '关于', '至于', '和', '跟', '与', '同', '及', '以及', '并', '并且', '而', '而且', '或', '或者', '还是', '既', '又', '一边', '一边', '一面', '一面', '不是', '而是', '不但', '而且', '不仅', '还', '虽然', '但是', '尽管', '可是', '即使', '也', '哪怕', '也', '宁可', '也不', '与其', '不如', '因为', '由于', '所以', '因此', '因而', '可见', '既然', '那么', '就', '则', '于是', '从而', '但是', '可是', '然而', '不过', '只是', '尽管', '虽然', '即使', '哪怕', '就算', '不管', '无论', '不论', '都', '也', '还', '再', '又', '也', '才', '就', '只有', '只要', '一旦', '如果', '要是', '假如', '倘若', '除非', '否则', '不然', '否则就', '要不然', '要么', '或者', '还是', '是', '还是', '不管', '还是', '无论', '还是', '不但', '而且', '不仅', '还', '虽然', '但是', '尽管', '可是', '即使', '也', '哪怕', '也', '宁可', '也不', '与其', '不如', '因为', '由于', '所以', '因此', '因而', '可见', '既然', '那么', '就', '则', '于是', '从而', '但是', '可是', '然而', '不过', '只是', '尽管', '虽然', '即使', '哪怕', '就算', '不管', '无论', '不论', '都', '也', '还', '再', '又', '也', '才', '就', '只有', '只要', '一旦', '如果', '要是', '假如', '倘若', '除非', '否则', '不然', '否则就', '要不然', '要么', '或者', '还是', '是', '还是', '不管', '还是', '无论', '还是'])
return [word for word in words if len(word) > 1 and word not in stopwords]
def extract_emotion_words(text):
"""提取文本中的情感词汇并分类"""
if pd.isnull(text):
return []
words = jieba.cut(text)
found_words = []
for word in words:
for sentiment in emotion_categories:
for category in emotion_categories[sentiment]:
if word in emotion_categories[sentiment][category]:
found_words.append({
'word': word,
'sentiment': sentiment,
'category': category
})
return found_words
def analyze_sentiment_with_details(text):
"""
增强版情感分析:
1. 情感词精确匹配 + 权重计算
2. 结合文本整体倾向(标点、否定词修正)
3. 输出更详细的情感标签和分数
"""
if pd.isnull(text):
return {
'sentiment': 'neutral',
'emotion_words': [],
'pos_count': 0,
'neg_count': 0,
'sentiment_score': 0 # 新增情感分数(-1~1)
}
# 1. 基础分词与情感词提取
words = jieba.lcut(text)
emotion_words = extract_emotion_words(text) # 复用原提取逻辑
# 2. 情感词权重计算(可自定义权重)
pos_weight = 1.0
neg_weight = -1.0
sentiment_score = 0
# 3. 否定词修正(比如“不推荐” vs “推荐”)
negation_words = {'不', '没', '无', '非', '未', '别', '莫', '勿', '毋'}
degree_words = {
'极其': 1.5, '非常': 1.3, '特别': 1.3, '十分': 1.2, '很': 1.1,
'有点': 0.8, '稍微': 0.7, '些许': 0.6, '还算': 0.9
}
for i, word in enumerate(words):
# 检查是否为情感词
is_emotion_word = False
for ew in emotion_words:
if word == ew['word']:
is_emotion_word = True
# 检查情感词前一个词是否为否定词或程度词
if i > 0:
prev_word = words[i-1]
# 处理否定词
if prev_word in negation_words:
if ew['sentiment'] == 'positive':
sentiment_score += neg_weight
else:
sentiment_score += pos_weight
continue # 已处理,跳过后续权重计算
# 处理程度词
if prev_word in degree_words:
if ew['sentiment'] == 'positive':
sentiment_score += pos_weight * degree_words[prev_word]
else:
sentiment_score += neg_weight * degree_words[prev_word]
continue # 已处理,跳过后续权重计算
# 普通情感词权重
if ew['sentiment'] == 'positive':
sentiment_score += pos_weight
else:
sentiment_score += neg_weight
# 特殊情感词组合(如“垃圾优化”)
if i < len(words) - 1:
combo = word + words[i+1]
if combo in negative_words:
sentiment_score += neg_weight * 1.2 # 组合词权重更高
elif combo in positive_words:
sentiment_score += pos_weight * 1.2
# 4. 标点符号强化(!、!!、??? 增强情感)
punctuation_scores = {
'!': 0.2,
'!!': 0.4,
'!!!': 0.6,
'?': 0.1,
'??': 0.2,
'???': 0.3,
'~': 0.1,
'~~': 0.2
}
# 查找连续标点
punc_pattern = re.compile(r'([!?.~])\1*')
for match in punc_pattern.finditer(text):
punc_seq = match.group(0)
if punc_seq in punctuation_scores:
# 根据情感极性调整标点影响
sentiment_score += punctuation_scores[punc_seq] * np.sign(sentiment_score)
# 5. 情感分类判定
pos_count = len([w for w in emotion_words if w['sentiment'] == 'positive'])
neg_count = len([w for w in emotion_words if w['sentiment'] == 'negative'])
# 情感词数量差距较大时,直接按数量判断
if abs(pos_count - neg_count) >= 2:
sentiment = 'positive' if pos_count > neg_count else 'negative'
else:
# 优先用情感分数判断,分数相近时 fallback 到词数
if abs(sentiment_score) > 0.1:
sentiment = 'positive' if sentiment_score > 0 else 'negative'
else:
if pos_count > neg_count:
sentiment = 'positive'
elif neg_count > pos_count:
sentiment = 'negative'
else:
sentiment = 'neutral'
# 规范化分数到 [-1, 1] 区间
normalized_score = max(-1, min(1, sentiment_score))
return {
'sentiment': sentiment,
'emotion_words': emotion_words,
'pos_count': pos_count,
'neg_count': neg_count,
'sentiment_score': normalized_score # 新增分数用于验证
}
def evaluate_recommendation(row):
"""评估推荐判断是否合理"""
actual = row['是否推荐']
predicted = row['sentiment_analysis']
if actual == '推荐' and predicted == 'positive':
return '正确推荐'
elif actual == '不推荐' and predicted == 'negative':
return '正确不推荐'
elif actual == '推荐' and predicted == 'negative':
return '错误推荐'
elif actual == '不推荐' and predicted == 'positive':
return '错误不推荐'
else:
return '中性评价'
def filter_by_sentiment(words, sentiment):
"""根据情感过滤词语"""
if sentiment == 'positive':
return [word for word in words if word in positive_words]
elif sentiment == 'negative':
return [word for word in words if word in negative_words]
else:
return words
def word_frequency_analysis(df, by_recommendation=None):
"""词频统计分析"""
if by_recommendation is not None:
df = df[df['是否推荐'] == by_recommendation]
all_words = []
for words_list in df['分词结果']:
# 检查是否为NaN或None
if pd.isna(words_list) or words_list is None:
continue
# 确保words_list是一个列表
if isinstance(words_list, str):
try:
words_list = eval(words_list)
except:
continue
if isinstance(words_list, list):
# 根据推荐类型过滤词语
if by_recommendation == '推荐':
words_list = filter_by_sentiment(words_list, 'positive')
elif by_recommendation == '不推荐':
words_list = filter_by_sentiment(words_list, 'negative')
all_words.extend([word for word in words_list if isinstance(word, str)])
word_counts = Counter(all_words)
return pd.DataFrame(word_counts.most_common(), columns=['word', 'count'])
# 3. 可视化函数
def plot_daily_review_users(df):
"""绘制每日评价人数曲线"""
daily_counts = df.groupby(df['发布时间(2024年)'].dt.date).size().reset_index(name='count')
plt.figure(figsize=(12, 6))
sns.lineplot(data=daily_counts, x='发布时间(2024年)', y='count')
plt.title('每日评价人数曲线')
plt.xlabel('日期')
plt.ylabel('评价人数')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
def plot_recommendation_pie(df, selected_date=None):
"""绘制推荐/不推荐饼图"""
if selected_date and selected_date != 'All':
filtered_df = df[df['发布时间(2024年)'].dt.date == pd.to_datetime(selected_date).date()]
else:
filtered_df = df.copy()
rec_counts = filtered_df['是否推荐'].value_counts().reset_index()
rec_counts.columns = ['推荐状态', '数量']
rec_counts['推荐状态'] = rec_counts['推荐状态'].map({'推荐': '推荐', '不推荐': '不推荐'})
fig = px.pie(rec_counts, values='数量', names='推荐状态',
title=f'推荐状态分布 ({selected_date if selected_date and selected_date != "All" else "全部日期"})')
fig.show()
def plot_hours_recommendation(df):
"""绘制游戏时长与推荐关系分析"""
plt.figure(figsize=(12, 6))
# 将 '大圣游戏时长' 列转换为数值类型,非数值的转换为 NaN
df['大圣游戏时长'] = pd.to_numeric(df['大圣游戏时长'], errors='coerce')
# 过滤掉非数值的行
filtered_df = df.dropna(subset=['大圣游戏时长'])
# 过滤异常值(假设超过500小时为异常)
filtered_df = filtered_df[filtered_df['大圣游戏时长'] <= 500]
sns.boxplot(data=filtered_df, x='是否推荐', y='大圣游戏时长')
plt.title('游戏时长与推荐关系分析')
plt.xlabel('推荐状态')
plt.ylabel('游戏时长(小时)')
plt.xticks([0, 1], ['不推荐', '推荐'])
plt.show()
# 添加统计信息
print("\n游戏时长统计信息:")
print(filtered_df.groupby('是否推荐')['大圣游戏时长'].describe())
def generate_wordcloud(word_freq_df, title, sentiment):
"""生成词云图"""
# 根据情感类型进一步过滤
if sentiment == 'positive':
word_freq_df = word_freq_df[word_freq_df['word'].isin(positive_words)]
elif sentiment == 'negative':
word_freq_df = word_freq_df[word_freq_df['word'].isin(negative_words)]
if len(word_freq_df) == 0:
print(f"\n没有足够的情感词数据生成{title}词云")
return
word_dict = dict(zip(word_freq_df['word'], word_freq_df['count']))
wc = WordCloud(
font_path='simhei.ttf', # 使用中文字体,需确保字体文件存在
width=800,
height=600,
background_color='white',
max_words=100,
colormap='viridis' if sentiment == 'positive' else 'inferno'
).generate_from_frequencies(word_dict)
plt.figure(figsize=(10, 8))
plt.imshow(wc, interpolation='bilinear')
plt.title(title)
plt.axis('off')
plt.show()
def plot_emotion_category_distribution(df):
"""绘制情感词汇分类分布"""
# 提取所有情感词汇
all_emotion_words = []
for words in df['emotion_words']:
all_emotion_words.extend(words)
# 创建DataFrame
if not all_emotion_words:
print("未提取到情感词汇")
return
emotion_df = pd.DataFrame(all_emotion_words)
# 正面情感词汇分类分布
pos_df = emotion_df[emotion_df['sentiment'] == 'positive']
if not pos_df.empty:
pos_counts = pos_df['category'].value_counts().reset_index()
pos_counts.columns = ['分类', '数量']
fig1 = px.bar(pos_counts,
x='分类', y='数量',
title='正面情感词汇分类分布',
labels={'分类': '情感分类', '数量': '出现次数'})
fig1.show()
# 负面情感词汇分类分布
neg_df = emotion_df[emotion_df['sentiment'] == 'negative']
if not neg_df.empty:
neg_counts = neg_df['category'].value_counts().reset_index()
neg_counts.columns = ['分类', '数量']
fig2 = px.bar(neg_counts,
x='分类', y='数量',
title='负面情感词汇分类分布',
labels={'分类': '情感分类', '数量': '出现次数'})
fig2.show()
def plot_emotion_words_distribution(df):
"""绘制情感词汇分布图(分积极和消极)"""
# 提取所有情感词汇
all_emotion_words = []
for words in df['emotion_words']:
all_emotion_words.extend(words)
if not all_emotion_words:
print("未提取到情感词汇")
return
# 创建DataFrame
emotion_df = pd.DataFrame(all_emotion_words)
# 统计每个词汇的出现次数
word_stats = emotion_df.groupby(['word', 'sentiment']).size().reset_index(name='count')
# 分离积极和消极词汇
pos_words = word_stats[word_stats['sentiment'] == 'positive'].sort_values('count', ascending=False).head(20)
neg_words = word_stats[word_stats['sentiment'] == 'negative'].sort_values('count', ascending=False).head(20)
# 创建子图
plt.figure(figsize=(15, 10))
# 积极词汇分布
plt.subplot(2, 1, 1)
sns.barplot(data=pos_words, x='count', y='word', palette='Greens_d')
plt.title('Top 20 积极情感词汇分布')
plt.xlabel('出现次数')
plt.ylabel('词汇')
# 消极词汇分布
plt.subplot(2, 1, 2)
sns.barplot(data=neg_words, x='count', y='word', palette='Reds_d')
plt.title('Top 20 消极情感词汇分布')
plt.xlabel('出现次数')
plt.ylabel('词汇')
plt.tight_layout()
plt.show()
def generate_emotion_words_table(df):
"""生成情感词汇提取统计表"""
# 提取所有情感词汇
all_emotion_words = []
for words in df['emotion_words']:
all_emotion_words.extend(words)
if not all_emotion_words:
print("未提取到情感词汇")
return None
# 创建DataFrame
emotion_df = pd.DataFrame(all_emotion_words)
# 统计每个词汇的出现次数
word_stats = emotion_df.groupby(['word', 'sentiment', 'category']).size().reset_index(name='count')
word_stats = word_stats.sort_values(by='count', ascending=False)
# 添加情感强度标记
word_stats['强度'] = word_stats['count'].apply(
lambda x: '高频' if x >= 10 else ('中频' if x >= 5 else '低频'))
# 重新排列列顺序
word_stats = word_stats[['word', 'sentiment', 'category', 'count', '强度']]
return word_stats
def display_sample_reviews(df, n=5):
"""展示样本评论及其分析结果"""
if len(df) == 0:
print("没有可用的评论数据")
return
# 确保样本数量不超过数据行数
n = min(n, len(df))
sample_df = df.sample(n)[['评论', '是否推荐', 'sentiment_analysis', 'pos_count', 'neg_count', 'recommendation_evaluation', 'sentiment_score']]
print("\n随机样本评论分析结果:")
print(sample_df.to_string(index=False))
# 展示情感词汇示例
print("\n情感词汇示例:")
for idx, row in df[df['emotion_words'].apply(len) > 0].sample(min(n, len(df[df['emotion_words'].apply(len) > 0]))).iterrows():
print(f"\n评论: {row['评论'][:80]}...")
print(f"情感分数: {row['sentiment_score']:.2f}")
print("情感词汇:")
for word in row['emotion_words']:
print(f" {word['word']} ({word['sentiment']} - {word['category']})")
# 新增:原评论情感分类展示功能
def display_sentiment_categories(df, n=5):
"""展示不同情感类别的评论示例"""
print("\n=== 原评论情感分类展示 ===")
# 按情感分类分组并获取示例
positive_samples = df[df['sentiment_analysis'] == 'positive']
negative_samples = df[df['sentiment_analysis'] == 'negative']
neutral_samples = df[df['sentiment_analysis'] == 'neutral']
# 确保有足够的样本
if len(positive_samples) == 0:
print("\n没有找到积极情感的评论")
else:
print("\n1. 积极情感评论示例:")
display_positive = positive_samples.sample(min(n, len(positive_samples)))[['评论', '是否推荐', 'pos_count', 'neg_count', 'sentiment_score']]
display_positive.columns = ['评论内容', '用户是否推荐', '积极词汇数', '消极词汇数', '情感分数']
print(display_positive.to_string(index=False))
if len(negative_samples) == 0:
print("\n没有找到消极情感的评论")
else:
print("\n2. 消极情感评论示例:")
display_negative = negative_samples.sample(min(n, len(negative_samples)))[['评论', '是否推荐', 'pos_count', 'neg_count', 'sentiment_score']]
display_negative.columns = ['评论内容', '用户是否推荐', '积极词汇数', '消极词汇数', '情感分数']
print(display_negative.to_string(index=False))
if len(neutral_samples) == 0:
print("\n没有找到中性情感的评论")
else:
print("\n3. 中性情感评论示例:")
display_neutral = neutral_samples.sample(min(n, len(neutral_samples)))[['评论', '是否推荐', 'pos_count', 'neg_count', 'sentiment_score']]
display_neutral.columns = ['评论内容', '用户是否推荐', '积极词汇数', '消极词汇数', '情感分数']
print(display_neutral.to_string(index=False))
def analyze_sentiment_categories(df):
"""分析情感分类分布并展示详细数据"""
# 统计情感分类数量
sentiment_counts = df['sentiment_analysis'].value_counts().reset_index()
sentiment_counts.columns = ['情感类别', '评论数量']
sentiment_counts['比例(%)'] = (sentiment_counts['评论数量'] / len(df) * 100).round(2)
print("\n=== 评论情感分类统计 ===")
print(sentiment_counts.to_string(index=False))
# 计算不同推荐状态下的情感分布
recommendation_sentiment = df.groupby(['是否推荐', 'sentiment_analysis']).size().unstack(fill_value=0)
recommendation_sentiment['总计'] = recommendation_sentiment.sum(axis=1)
recommendation_sentiment['积极比例(%)'] = (recommendation_sentiment['positive'] / recommendation_sentiment['总计'] * 100).round(2)
recommendation_sentiment['消极比例(%)'] = (recommendation_sentiment['negative'] / recommendation_sentiment['总计'] * 100).round(2)
recommendation_sentiment['中性比例(%)'] = (recommendation_sentiment['neutral'] / recommendation_sentiment['总计'] * 100).round(2)
print("\n=== 推荐状态与情感分类交叉分析 ===")
print(recommendation_sentiment.to_string())
# 新增:情感分析验证可视化
def plot_sentiment_score_distribution(df):
"""
绘制情感分数分布图,验证情感分析的合理性:
- 积极评论应集中在正分
- 消极评论应集中在负分
- 中性评论应接近0分
"""
plt.figure(figsize=(12, 6))
sns.histplot(
data=df,
x='sentiment_score',
hue='sentiment_analysis',
multiple='layer',
palette={'positive': '#2ecc71', 'negative': '#e74c3c', 'neutral': '#95a5a6'},
bins=30,
kde=True
)
plt.title('情感分数分布与情感标签验证')
plt.xlabel('情感分数 (-1~1)')
plt.ylabel('评论数量')
plt.legend(title='情感标签')
plt.show()
# 输出统计描述
print("\n情感分数统计描述:")
print(df.groupby('sentiment_analysis')['sentiment_score'].describe())
# 新增:推荐判断准确性分析
def plot_recommendation_accuracy(df):
"""绘制推荐判断准确性分析图"""
eval_counts = df['recommendation_evaluation'].value_counts().reset_index()
eval_counts.columns = ['评估结果', '数量']
fig = px.pie(eval_counts, values='数量', names='评估结果',
title='推荐判断准确性分析')
fig.show()
# 计算准确率
correct_count = len(df[(df['recommendation_evaluation'] == '正确推荐') |
(df['recommendation_evaluation'] == '正确不推荐')])
total_count = len(df)
accuracy = correct_count / total_count * 100
print(f"\n推荐判断准确率: {accuracy:.2f}%")
# 4. 主执行流程
def main():
# 1. 加载数据
print("正在加载数据...")
reviews_df = load_review_data()
if len(reviews_df) == 0:
print("没有找到评论数据,程序退出")
return
print(f"成功加载 {len(reviews_df)} 条评论数据")
# 2. 提取用户ID
print("正在进行数据预处理...")
reviews_df = extract_user_id(reviews_df)
# 3. 可视化基本图表
print("\n1. 用户每日评价人数曲线:")
plot_daily_review_users(reviews_df)
print("\n2. 游戏时长与推荐关系分析:")
plot_hours_recommendation(reviews_df)
# 4. 中文分词与词频分析
print("\n3. 正在进行中文分词处理...")
reviews_df['分词结果'] = reviews_df['评论'].apply(chinese_word_segmentation)
# 将分词结果转换为字符串表示,便于后续处理
reviews_df['分词结果'] = reviews_df['分词结果'].apply(lambda x: str(x) if isinstance(x, list) else x)
print("\n4. 词频统计分析:")
word_freq_all = word_frequency_analysis(reviews_df)
print("所有评论中的Top 20词:")
print(word_freq_all.head(20))
word_freq_recommended = word_frequency_analysis(reviews_df, by_recommendation='推荐')
print("\n推荐评论中的Top 20词:")
print(word_freq_recommended.head(20))
word_freq_not_recommended = word_frequency_analysis(reviews_df, by_recommendation='不推荐')
print("\n不推荐评论中的Top 20词:")
print(word_freq_not_recommended.head(20))
# 5. 生成词云图
print("\n5. 生成推荐评论词云图:")
generate_wordcloud(word_freq_recommended, '推荐评论词云图', 'positive')
print("\n6. 生成不推荐评论词云图:")
generate_wordcloud(word_freq_not_recommended, '不推荐评论词云图', 'negative')
# 6. 情感分析
print("\n7. 正在进行情感分析...")
sentiment_results = reviews_df['评论'].apply(analyze_sentiment_with_details)
reviews_df['sentiment_analysis'] = sentiment_results.apply(lambda x: x['sentiment'])
reviews_df['pos_count'] = sentiment_results.apply(lambda x: x['pos_count'])
reviews_df['neg_count'] = sentiment_results.apply(lambda x: x['neg_count'])
reviews_df['emotion_words'] = sentiment_results.apply(lambda x: x['emotion_words'])
reviews_df['sentiment_score'] = sentiment_results.apply(lambda x: x['sentiment_score'])
reviews_df['recommendation_evaluation'] = reviews_df.apply(evaluate_recommendation, axis=1)
# 7. 情感分析可视化
print("\n8. 情感分析可视化:")
# 情感分布饼图
fig1 = px.pie(reviews_df, names='sentiment_analysis', title='评论情感分布')
fig1.show()
# 推荐判断结果饼图
plot_recommendation_accuracy(reviews_df)
# 情感词汇分类分布
plot_emotion_category_distribution(reviews_df)
# 情感词汇分布图
plot_emotion_words_distribution(reviews_df)
# 情感分数分布验证
plot_sentiment_score_distribution(reviews_df)
# 8. 生成情感词汇统计表
print("\n9. 生成情感词汇统计表...")
emotion_words_table = generate_emotion_words_table(reviews_df)
if emotion_words_table is not None:
# 保存到CSV
emotion_words_table.to_csv('emotion_words_statistics.csv', index=False, encoding='utf_8_sig')
print("情感词汇统计表已保存到 emotion_words_statistics.csv")
# 显示高频词汇
print("\n高频情感词汇:")
print(emotion_words_table[emotion_words_table['强度'] == '高频'].head(20))
# 9. 展示样本分析结果
display_sample_reviews(reviews_df, n=10)
# 新增:原评论情感分类展示
print("\n10. 原评论情感分类展示:")
analyze_sentiment_categories(reviews_df)
display_sentiment_categories(reviews_df, n=5) # 每个类别展示5条示例
# 10. 保存分析结果
reviews_df.to_csv('analyzed_reviews_with_emotions.csv', index=False, encoding='utf_8_sig')
print("\n分析结果已保存到 analyzed_reviews_with_emotions.csv")
# 11. 交互式饼图(在Jupyter Notebook中运行效果最佳)
print("\n11. 准备交互式推荐比例饼图...")
unique_dates = reviews_df['发布时间(2024年)'].dt.date.unique()
date_options = [str(date) for date in sorted(unique_dates)]
# 在Jupyter Notebook中取消下面注释以使用交互功能
# interact(plot_recommendation_pie, df=widgets.fixed(reviews_df),
# selected_date=widgets.Dropdown(options=['All'] + date_options, value='All', description='Select Date'))
print("\n所有分析已完成!")
if __name__ == "__main__":
main()
from pyecharts import options as opts
from pyecharts.charts import WordCloud
import pandas as pd
from snownlp import SnowNLP
# 停用词
# 白名单:强制保留的强情感词(优先级最高)
WHITELIST = {'好评', '优秀', '完美', '支持', '好玩', '震撼', '期待', '经典', '满意'}
# 新增停用词:时间/量词/无情感词
NEW_STOPWORDS = {'一场', '时期', '以后', '一个', '一种', '一次', '这里', '那里', '这么', '那么'}
STOPWORDS = {
# 原有停用词
'的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个',
'上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好',
'自己', '这', '问题', '时候', '可以', '我们', '什么', '他们', '但是', '如果',
'因为', '这样', '不是', '可能', '觉得', '现在', '起来', '然后', '里面', '还是',
'那些', '只能', '就是', '时候', '用户', '评论', '有点', '那种', '地图', '已经',
'非常', '感觉', '目前', '一次', '一种', '虽然', '一些', '一套', '一句', '画面',
'那个', '那里', '这里', '那么', '这么', '比较', '还有', '对于', '属于', '而且',
'序章', '手柄',
# 新增中性名词
'游戏', '作品', '大圣', '画面', '体验', '内容', '战斗', '系统', '时候', '场景',
'支持', '玩家', '中国', '文化', '设计', '功能', '效果', '模式', '过程', '难度','产品'}
# 保留词性:形容词(a)/副词(d)/动词(v)/名词(n)中强情感词(需人工筛选)
KEEP_POS = {'a', 'd', 'v', 'n'} # 名词仅保留强情感名词,其他词性过滤
def is_chinese(word):
"""判断是否为中文字符"""
return all('\u4e00' <= char <= '\u9fff' for char in word)
def process_text(words):
"""分词处理:过滤停用词、非中文词、单字"""
return [word for word in words
if is_chinese(word) and word not in STOPWORDS and len(word) > 1]
def calculate_sentiment_score(word):
"""计算词语的情感分数 (0-1),值越高越积极"""
# 对于单个词,SnowNLP可能不准确,这里做一些优化
# 对于已知积极词直接赋予高分
positive_words = {'好评', '优秀', '完美', '支持', '好玩', '震撼', '期待', '经典', '满意'}
negative_words = {'差评', '垃圾', '卡顿', '失望', '问题', '不好', '难受', '后悔', '糟糕'}
if word in positive_words:
return 0.9
elif word in negative_words:
return 0.1
# 对其他词使用SnowNLP计算
return SnowNLP(word).sentiments
def create_wordcloud(data, title, subtitle, shape="circle"):
"""创建基于情感极性的词云图"""
# 统计词频
word_freq = pd.Series(data).value_counts().reset_index()
word_freq.columns = ['word', 'freq']
# 计算每个词的情感分数
word_freq['sentiment'] = word_freq['word'].apply(calculate_sentiment_score)
# 过滤掉消极词汇(只保留情感分数>0.5的词)
positive_words = word_freq[word_freq['sentiment'] > 0.5].copy()
# 计算加权分数:情感分数 * 词频 (情感优先,频次其次)
positive_words['weight'] = positive_words['sentiment'] * positive_words['freq']
# 按加权分数排序并取前100个
top_words = positive_words.sort_values('weight', ascending=False).head(100)
# 为了词云显示效果,将weight归一化到1-100范围
min_weight = top_words['weight'].min()
max_weight = top_words['weight'].max()
top_words['normalized_weight'] = 1 + 99 * (top_words['weight'] - min_weight) / (max_weight - min_weight)
return (
WordCloud(init_opts=opts.InitOpts(width="800px", height="600px"))
.add(
series_name="",
data_pair=[(word, weight) for word, weight in zip(top_words['word'], top_words['normalized_weight'])],
word_size_range=[20, 120],
shape=shape,
textstyle_opts=opts.TextStyleOpts(font_family="Microsoft YaHei"),
)
.set_global_opts(
title_opts=opts.TitleOpts(
title=title,
subtitle=subtitle,
title_textstyle_opts=opts.TextStyleOpts(font_size=24, font_family="Microsoft YaHei"),
subtitle_textstyle_opts=opts.TextStyleOpts(font_size=16, font_family="Microsoft YaHei"),
),
tooltip_opts=opts.TooltipOpts(is_show=True, formatter="{b}: {c}"),
toolbox_opts=opts.ToolboxOpts(
is_show=True,
feature=opts.ToolBoxFeatureOpts(
save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(background_color="white")
)
),
)
)
# 读取积极评论分词数据
df = pd.read_csv('F:/Activity和数据库例程/positive_comments_tokenized.csv')
# 解析分词结果并展平为单词列表
all_words = []
for row in df['分词结果']:
try:
words = eval(row)
all_words.extend(words)
except:
continue
# 处理文本,过滤非中文词汇
filtered_words = process_text(all_words)
# 创建基于情感极性的词云
positive_cloud = create_wordcloud(
filtered_words,
"基于情感极性的积极词云",
"大小反映积极程度(优先)和频次(其次)",
shape="diamond"
)
# 渲染词云图
positive_cloud.render("F:/Activity和数据库例程/sentiment_based_wordcloud.html")
print("基于情感极性的词云已生成:/mnt/sentiment_based_wordcloud.html")
import pandas as pd
from pyecharts import options as opts
from pyecharts.charts import WordCloud, Page
import jieba
from snownlp import SnowNLP
import re
# ----------------------
# 配置参数
# ----------------------
FONT_PATH = "C:\\Windows\\Fonts\\msyh.ttc" # Windows系统字体路径
STOPWORDS = {
# 中文停用词
'的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个',
'上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好',
'自己', '这', '游戏', '问题', '这个', '时候', '可以', '我们', '什么', '他们',
'但是', '如果', '因为', '这样', '不是', '可能', '觉得', '现在', '起来', '然后',
'里面', '还是', '那些', '只能', '就是', '时候', '用户', '评论', '有点', '那种',
'地图', '已经', '非常', '感觉', '目前', '一次', '一种', '虽然', '一些', '一套',
'一句', '画面', '时候', '这个', '那个', '那里', '这里', '那么', '这么', '这样','第一章','一下','只是','之后','玩家','来说','比较','还有','对于','属于','真的','而且','序章','手柄'
# 英文停用词
'the', 'it', 'this', 'that', 'and', 'is', 'in', 'of', 'to', 'a', 'an', 'as', 'with',
'for', 'be', 'by', 'on', 'so', 'at', 'but', 'or', 'from', 'into', 'over', 'such',
'than', 'then', 'them', 'their', 'was', 'were', 'has', 'have', 'had', 'will', 'its',
'his', 'her', 'she', 'he', 'they', 'them', 'those', 'these', 'there', 'here', 'when',
'where', 'why', 'how', 'all', 'any', 'only', 'just', 'even', 'most', 'other', 'some',
'much', 'more', 'less', 'so', 'very', 'too', 'if', 'while', 'once', 'until', 'about',
'against', 'between', 'during', 'without', 'under', 'along', 'across', 'through', 'during',
'game', 'my', 'boss', 'are', 'your', 'im', 'you', 'Im', 'The', 'my', 'The', 'one', 'two','Boss','the','The'
# 数字相关
'2024', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '100', '20', '30'
}
NUMBERS_PATTERN = re.compile(r'\d+|[\u0030-\u0039]') # 匹配所有数字
ENGLISH_PATTERN = re.compile(r'[a-zA-Z]+') # 匹配英文单词
# ----------------------
# 数据处理函数
# ----------------------
def analyze_sentiment(df):
"""基于推荐标签和情感分析分类评论"""
# 提取推荐和不推荐评论
recommended = df[df['是否推荐'] == '推荐']['评论'].dropna()
not_recommended = df[df['是否推荐'] != '推荐']['评论'].dropna()
# 情感分类
positive_words = []
negative_words = []
for comment in recommended:
# 推荐评论默认积极,再通过情感分析微调
words = process_text(comment)
if SnowNLP(comment).sentiments >= 0.4: # 放宽积极阈值
positive_words.extend(words)
else:
negative_words.extend(words)
for comment in not_recommended:
# 不推荐评论默认消极,再通过情感分析微调
words = process_text(comment)
if SnowNLP(comment).sentiments < 0.6: # 放宽消极阈值
negative_words.extend(words)
else:
positive_words.extend(words)
return positive_words, negative_words
def process_text(text):
"""分词并过滤停用词、数字、英文"""
# 移除数字和英文
text = re.sub(NUMBERS_PATTERN, '', text)
text = re.sub(ENGLISH_PATTERN, '', text)
# 分词并过滤停用词
words = jieba.lcut(text)
return [word for word in words if word not in STOPWORDS and len(word) > 1]
# ----------------------
# 词云图生成函数
# ----------------------
def create_wordcloud(data, title, subtitle, shape="circle", color="#333"):
"""创建词云图"""
# 统计词频
word_freq = pd.Series(data).value_counts().reset_index()
word_freq.columns = ['word', 'freq']
# 取前100个高频词
top_words = word_freq.head(100)
return (
WordCloud(init_opts=opts.InitOpts(width="800px", height="600px"))
.add(
series_name="",
data_pair=[(word, freq) for word, freq in zip(top_words['word'], top_words['freq'])],
word_size_range=[20, 120],
shape=shape,
textstyle_opts=opts.TextStyleOpts(font_family="Microsoft YaHei"),
)
.set_global_opts(
title_opts=opts.TitleOpts(
title=title,
subtitle=subtitle,
title_textstyle_opts=opts.TextStyleOpts(font_size=24, font_family="Microsoft YaHei"),
subtitle_textstyle_opts=opts.TextStyleOpts(font_size=16, font_family="Microsoft YaHei"),
),
tooltip_opts=opts.TooltipOpts(is_show=True),
toolbox_opts=opts.ToolboxOpts(
is_show=True,
feature=opts.ToolBoxFeatureOpts(
save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(background_color="white")
)
),
)
)
# ----------------------
# 主程序
# ----------------------
if __name__ == "__main__":
# 读取数据
df = pd.read_csv('F:/Activity和数据库例程/wukong_cleaned.csv', encoding='gb18030')
# 情感分析
positive_words, negative_words = analyze_sentiment(df)
# 创建词云图
positive_cloud = create_wordcloud(
positive_words,
"积极情感词云图",
"基于推荐评论和积极情感分析",
shape="diamond",
color="#1E90FF"
)
negative_cloud = create_wordcloud(
negative_words,
"消极情感词云图",
"基于不推荐评论和消极情感分析",
shape="circle",
color="#CD5C5C"
)
# 创建HTML页面
page = Page(layout=Page.DraggablePageLayout)
page.add(positive_cloud, negative_cloud)
page.render("wukong_sentiment_wordclouds.html")
print("词云图HTML页面已生成:wukong_sentiment_wordclouds.html")
import pandas as pd
import pymysql
from sqlalchemy import create_engine
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from ipywidgets import interact, widgets
import re
import jieba
from collections import Counter
from wordcloud import WordCloud
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体为 SimHei(或其他中文字体名)
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题
# 1. MySQL连接与数据加载
def create_mysql_connection():
"""创建MySQL数据库连接"""
connection = pymysql.connect(
host='localhost',
user='root',
password='123456',
database='qingxi',
charset='utf8mb4'
)
return connection
# 使用SQLAlchemy创建引擎
engine = create_engine('mysql+pymysql://root:123456@localhost/big_data_review')
def load_review_data():
"""从MySQL加载用户评论数据"""
query = "SELECT * FROM wukong_cleaned"
df = pd.read_sql(query, engine)
# 确保日期列是datetime类型
df['发布时间(2024年)'] = pd.to_datetime(df['发布时间(2024年)'])
return df
# 2. 数据预处理
def extract_user_id(df):
"""从用户链接提取用户ID"""
pattern = r'(?:id/|profiles/)([^/]+)'
df['用户ID_extracted'] = df['用户链接'].apply(
lambda x: re.search(pattern, x).group(1) if pd.notnull(x) else None)
return df
# 定义情感词库(可根据实际情况扩充)
positive_words = set(['好玩', '优秀', '精彩', '推荐', '喜欢', '爱', '棒', '出色', '惊艳', '神作',
'经典', '完美', '流畅', '惊喜', '超值', '值得', '上瘾', '沉浸', '刺激', '欢乐',
'满意', '好评', '支持', '买爆', '无敌', '顶级', '神级', '完美', '绝了', '良心',
'诚意', '爽快', '震撼', '创新', '独特', '优质', '舒服', '过瘾', '享受', '满意',
'感动', '值得一玩', '无可挑剔', '诚意满满', '佳作', '精品', '诚意之作'])
negative_words = set(['垃圾', '差评', '失望', '糟糕', '后悔', '不值', '无聊', '烂', '坑', '骗钱',
'卡顿', '掉线', '闪退', 'bug', '问题', '差劲', '恶心', '弃坑', '卸载', '别买',
'垃圾游戏', '浪费时间', '骗局', '垃圾制作', '千万别买', '坑爹', '垃圾优化', '失望透顶',
'垃圾画质', '操作烂', '平衡差', '毁了', '垃圾玩法', '垃圾内容', '垃圾策划', '优化差',
'体验差', 'bug多', '太烂', '无法玩', '极差', '弃游', '退游', '不推荐', '避雷'])
neutral_words = set(['游戏', '感觉', '比较', '有点', '可能', '应该', '可以', '一般', '还行', '算是',
'大概', '或许', '似乎', '某种', '某种程度', '某种意义', '某种方面', '某种角度',
'来说', '而已', '方面', '角度', '整体', '部分', '内容', '方式', '模式', '元素',
'问题', '情况', '表现', '效果', '体验', '感觉', '样子', '东西', '部分', '时间',
'时候', '期间', '过程', '阶段', '水平', '程度', '质量', '数量', '关系', '原因',
'理由', '结果', '影响', '意义', '价值', '作用', '功能', '特点', '特色', '优势',
'缺点', '不足', '局限', '限制', '方面', '角度', '方式', '方法', '手段', '途径',
'形式', '内容', '结构', '组织', '系统', '机制', '规则', '玩法', '操作', '画面',
'画质', '音乐', '音效', '剧情', '故事', '角色', '任务', '关卡', '难度', '挑战',
'乐趣', '趣味', '上瘾', '沉浸', '流畅', '卡顿', '优化', '配置', '要求', '支持',
'兼容', '稳定', '更新', '维护', '服务', '客服', '态度', '反馈', '建议', '意见',
'希望', '期待', '改进', '提升', '完善', '加强', '增加', '减少', '调整', '优化',
'修复', '解决', '处理', '出现', '发生', '遇到', '发现', '存在', '需要', '应该',
'必须', '可以', '可能', '或许', '大概', '似乎', '好像', '感觉', '认为', '觉得',
'看来', '看样子', '有点', '稍微', '比较', '相当', '非常', '极其', '特别', '格外',
'有点', '稍微', '多少', '有点', '略微', '还算', '还可以', '过得去', '一般般',
'普普通通', '平平淡淡', '中规中矩', '马马虎虎', '凑合', '将就', '勉强', '其实',
'实际上', '事实上', '真的', '确实', '的确', '到底', '究竟', '到底是', '究竟是',
'就是', '正是', '才是', '而是', '不是', '并非', '只是', '仅仅', '不过', '而已',
'罢了', '就是说', '换句话说', '也就是说', '即', '即', '比如', '例如', '像', '如同',
'犹如', '好像', '仿佛', '似乎', '如同', '类似', '相似', '一样', '同样', '相同',
'不同', '差异', '区别', '分别', '对比', '比较', '相比', '比起', '与...相比', '和...一样',
'跟...一样', '如同...一样', '好像...一样', '仿佛...一样', '似乎...一样', '比', '比较',
'更加', '越发', '越来越', '越...越...', '不但', '不仅', '而且', '并且', '甚至', '乃至',
'或者', '还是', '要么', '不是...就是...', '与其...不如...', '宁可...也不...', '因为',
'由于', '所以', '因此', '因而', '可见', '既然', '那么', '就', '则', '于是', '从而',
'但是', '可是', '然而', '不过', '只是', '尽管', '虽然', '即使', '哪怕', '就算', '不管',
'无论', '不论', '都', '也', '还', '再', '又', '也', '才', '就', '只有', '只要', '一旦',
'如果', '要是', '假如', '倘若', '除非', '否则', '不然', '否则就', '要不然', '要么',
'或者', '还是', '是...还是...', '不管...还是...', '无论...还是...', '不但...而且...',
'不仅...还...', '虽然...但是...', '尽管...可是...', '即使...也...', '哪怕...也...',
'宁可...也不...', '与其...不如...', '因为...所以...', '由于...因此...', '既然...那么...',
'只有...才...', '只要...就...', '一旦...就...', '如果...那么...', '要是...就...',
'假如...就...', '倘若...就...', '除非...否则...', '不是...而是...', '不但不...反而...',
'连...也...', '甚至...也...', '尚且...何况...', '何况', '况且', '再说', '此外', '另外',
'而且', '并且', '同时', '尤其', '特别', '特别是', '主要是', '关键是', '首先', '其次',
'再次', '最后', '总之', '综上所述', '由此可见', '看来', '看样子', '显然', '明显',
'确实', '的确', '真的', '非常', '极其', '特别', '格外', '尤其', '更加', '越发',
'越来越', '越...越...', '多么', '何等', '多么地', '何等的', '这么', '那么', '这样',
'那样', '如此', '这般', '怎么', '怎样', '如何', '为什么', '什么', '哪里', '何时',
'谁', '哪个', '哪些', '所有', '一切', '全部', '整个', '任何', '每个', '各自', '分别',
'独自', '一起', '共同', '互相', '彼此', '相互', '都', '全', '总', '共', '一共', '总共',
'仅仅', '只', '才', '就', '已经', '刚刚', '马上', '立刻', '即将', '就要', '快要',
'马上就要', '即将要', '就要到', '快要到', '到', '在', '当', '于', '趁', '随着', '自从',
'直到', '在...之前', '在...之后', '在...时候', '在...期间', '在...中', '在...里',
'在...之上', '在...之下', '在...左边', '在...右边', '在...前面', '在...后面', '在...旁边',
'在...附近', '在...周围', '在...中间', '在...内部', '在...外部', '在...里面', '在...外面',
'向', '朝', '往', '到', '去', '来', '回', '从', '自', '由', '由...组成', '由...构成',
'根据', '按照', '依照', '遵照', '为了', '为', '替', '给', '对', '对于', '关于', '至于',
'和', '跟', '与', '同', '及', '以及', '并', '并且', '而', '而且', '或', '或者', '还是',
'既...又...', '一边...一边...', '一面...一面...', '不是...而是...', '不但...而且...',
'不仅...还...', '虽然...但是...', '尽管...可是...', '即使...也...', '哪怕...也...',
'宁可...也不...', '与其...不如...', '因为...所以...', '由于...因此...', '既然...那么...',
'只有...才...', '只要...就...', '一旦...就...', '如果...那么...', '要是...就...',
'假如...就...', '倘若...就...', '除非...否则...', '是否', '是不是', '有没有', '是不是...',
'有没有...', '呢', '吗', '吧', '啊', '呀', '哇', '哦', '哟', '哎', '唉', '哼', '哈', '嘿',
'哎呀', '哎哟', '啊呀', '哇塞', '天哪', '哎呀妈呀', '我的天', '真的吗', '是不是', '有没有',
'难道', '莫非', '究竟', '到底', '到底是', '究竟是', '就是', '正是', '才是', '而是', '不是',
'并非', '只是', '仅仅', '不过', '而已', '罢了', '就是说', '换句话说', '也就是说', '即', '即',
'比如', '例如', '像', '如同', '犹如', '好像', '仿佛', '似乎', '如同', '类似', '相似', '一样',
'同样', '相同', '不同', '差异', '区别', '分别', '对比', '比较', '相比', '比起', '与...相比',
'和...一样', '跟...一样', '如同...一样', '好像...一样', '仿佛...一样', '似乎...一样', '比',
'比较', '更加', '越发', '越来越', '越...越...', '不但', '不仅', '而且', '并且', '甚至', '乃至',
'或者', '还是', '要么', '不是...就是...', '与其...不如...', '宁可...也不...', '因为', '由于',
'所以', '因此', '因而', '可见', '既然', '那么', '就', '则', '于是', '从而', '但是', '可是',
'然而', '不过', '只是', '尽管', '虽然', '即使', '哪怕', '就算', '不管', '无论', '不论', '都',
'也', '还', '再', '又', '也', '才', '就', '只有', '只要', '一旦', '如果', '要是', '假如', '倘若',
'除非', '否则', '不然', '否则就', '要不然', '要么', '或者', '还是', '是...还是...',
'不管...还是...',
'无论...还是...', '不但...而且...', '不仅...还...', '虽然...但是...', '尽管...可是...',
'即使...也...',
'哪怕...也...', '宁可...也不...', '与其...不如...', '因为...所以...', '由于...因此...',
'既然...那么...',
'只有...才...', '只要...就...', '一旦...就...', '如果...那么...', '要是...就...', '假如...就...',
'倘若...就...', '除非...否则...', '不是...而是...', '不但不...反而...', '连...也...',
'甚至...也...',
'尚且...何况...', '何况', '况且', '再说', '此外', '另外', '而且', '并且', '同时', '尤其', '特别',
'特别是', '主要是', '关键是', '首先', '其次', '再次', '最后', '总之', '综上所述', '由此可见',
'看来', '看样子', '显然', '明显', '确实', '的确', '真的', '非常', '极其', '特别', '格外', '尤其',
'更加', '越发', '越来越', '越...越...', '多么', '何等', '多么地', '何等的', '这么', '那么', '这样',
'那样', '如此', '这般', '怎么', '怎样', '如何', '为什么', '什么', '哪里', '何时', '谁', '哪个',
'哪些', '所有', '一切', '全部', '整个', '任何', '每个', '各自', '分别', '独自', '一起', '共同',
'互相', '彼此', '相互', '都', '全', '总', '共', '一共', '总共', '仅仅', '只', '才', '就', '已经',
'刚刚', '马上', '立刻', '即将', '就要', '快要', '马上就要', '即将要', '就要到', '快要到', '到',
'在', '当', '于', '趁', '随着', '自从', '直到', '在...之前', '在...之后', '在...时候', '在...期间',
'在...中', '在...里', '在...之上', '在...之下', '在...左边', '在...右边', '在...前面', '在...后面',
'在...旁边', '在...附近', '在...周围', '在...中间', '在...内部', '在...外部', '在...里面',
'在...外面',
'向', '朝', '往', '到', '去', '来', '回', '从', '自', '由', '由...组成', '由...构成', '根据',
'按照',
'依照', '遵照', '为了', '为', '替', '给', '对', '对于', '关于', '至于', '和', '跟', '与', '同',
'及',
'以及', '并', '并且', '而', '而且', '或', '或者', '还是', '既...又...', '一边...一边...',
'一面...一面...'])
# 情感词汇分类
emotion_categories = {
'positive': {
'游戏体验': ['好玩', '精彩', '刺激', '欢乐', '沉浸', '上瘾', '爽快', '震撼', '舒服', '过瘾', '享受'],
'游戏质量': ['优秀', '出色', '惊艳', '神作', '经典', '完美', '流畅', '优质', '独特', '创新', '无可挑剔'],
'推荐程度': ['推荐', '喜欢', '爱', '棒', '超值', '值得', '买爆', '良心', '诚意', '佳作', '精品', '诚意之作'],
'满意度': ['满意', '好评', '支持', '惊喜', '感动', '满意', '无可挑剔']
},
'negative': {
'游戏体验': ['无聊', '卡顿', '掉线', '闪退', '浪费时间', '体验差', '操作烂', '无法玩', '弃游', '退游', '避雷'],
'游戏质量': ['垃圾', '烂', '差劲', '垃圾游戏', '垃圾制作', '垃圾优化', '垃圾画质', '太烂', '极差', '优化差',
'bug多'],
'不推荐': ['差评', '失望', '糟糕', '后悔', '不值', '别买', '千万别买', '坑爹', '不推荐', '弃坑', '卸载'],
'问题反馈': ['坑', '骗钱', 'bug', '问题', '骗局', '毁了', '垃圾玩法', '垃圾内容', '垃圾策划', '平衡差']
}
}
def chinese_word_segmentation(text):
"""使用结巴分词进行中文分词"""
if pd.isnull(text):
return []
# 使用结巴分词
words = jieba.cut(text)
# 过滤停用词和单字
stopwords = set(
['的', '了', '和', '是', '我', '在', '有', '也', '都', '不', '就', '很', '着', '到', '上', '下', '来', '去',
'得', '地', '把', '被', '对', '对于', '关于', '啊', '呀', '哇', '呢', '吗', '吧', '啊', '呀', '哇', '哦', '哟',
'哎', '唉', '哼', '哈', '嘿', '哎呀', '哎哟', '啊呀', '哇塞', '天哪', '哎呀妈呀', '我的天', '真的', '确实',
'的确', '非常', '极其', '特别', '格外', '尤其', '更加', '越发', '越来越', '越', '多', '少', '大', '小', '高',
'低', '长', '短', '好', '坏', '快', '慢', '前', '后', '左', '右', '上', '下', '里', '外', '中', '间', '内',
'外', '旁', '边', '附近', '周围', '中间', '内部', '外部', '里面', '外面', '向', '朝', '往', '到', '去', '来',
'回', '从', '自', '由', '由', '组成', '构成', '根据', '按照', '依照', '遵照', '为了', '为', '替', '给', '对',
'对于', '关于', '至于', '和', '跟', '与', '同', '及', '以及', '并', '并且', '而', '而且', '或', '或者', '还是',
'既', '又', '一边', '一边', '一面', '一面', '不是', '而是', '不但', '而且', '不仅', '还', '虽然', '但是',
'尽管', '可是', '即使', '也', '哪怕', '也', '宁可', '也不', '与其', '不如', '因为', '由于', '所以', '因此',
'因而', '可见', '既然', '那么', '就', '则', '于是', '从而', '但是', '可是', '然而', '不过', '只是', '尽管',
'虽然', '即使', '哪怕', '就算', '不管', '无论', '不论', '都', '也', '还', '再', '又', '也', '才', '就', '只有',
'只要', '一旦', '如果', '要是', '假如', '倘若', '除非', '否则', '不然', '否则就', '要不然', '要么', '或者',
'还是', '是', '还是', '不管', '还是', '无论', '还是', '不但', '而且', '不仅', '还', '虽然', '但是', '尽管',
'可是', '即使', '也', '哪怕', '也', '宁可', '也不', '与其', '不如', '因为', '由于', '所以', '因此', '因而',
'可见', '既然', '那么', '就', '则', '于是', '从而', '但是', '可是', '然而', '不过', '只是', '尽管', '虽然',
'即使', '哪怕', '就算', '不管', '无论', '不论', '都', '也', '还', '再', '又', '也', '才', '就', '只有', '只要',
'一旦', '如果', '要是', '假如', '倘若', '除非', '否则', '不然', '否则就', '要不然', '要么', '或者', '还是',
'是', '还是', '不管', '还是', '无论', '还是'])
return [word for word in words if len(word) > 1 and word not in stopwords]
def extract_emotion_words(text):
"""提取文本中的情感词汇并分类"""
if pd.isnull(text):
return []
words = jieba.cut(text)
found_words = []
for word in words:
for sentiment in emotion_categories:
for category in emotion_categories[sentiment]:
if word in emotion_categories[sentiment][category]:
found_words.append({
'word': word,
'sentiment': sentiment,
'category': category
})
return found_words
def analyze_sentiment_with_details(text):
"""
增强版情感分析:
1. 情感词精确匹配 + 权重计算
2. 结合文本整体倾向(标点、否定词修正)
3. 输出更详细的情感标签和分数
"""
if pd.isnull(text):
return {
'sentiment': 'neutral',
'emotion_words': [],
'pos_count': 0,
'neg_count': 0,
'sentiment_score': 0 # 新增情感分数(-1~1)
}
# 1. 基础分词与情感词提取
words = jieba.lcut(text)
emotion_words = extract_emotion_words(text) # 复用原提取逻辑
# 2. 情感词权重计算(可自定义权重)
pos_weight = 1.0
neg_weight = -1.0
sentiment_score = 0
# 3. 否定词修正(比如“不推荐” vs “推荐”)
negation_words = {'不', '没', '无', '非', '未', '别', '莫', '勿', '毋'}
degree_words = {
'极其': 1.5, '非常': 1.3, '特别': 1.3, '十分': 1.2, '很': 1.1,
'有点': 0.8, '稍微': 0.7, '些许': 0.6, '还算': 0.9
}
for i, word in enumerate(words):
# 检查是否为情感词
is_emotion_word = False
for ew in emotion_words:
if word == ew['word']:
is_emotion_word = True
# 检查情感词前一个词是否为否定词或程度词
if i > 0:
prev_word = words[i - 1]
# 处理否定词
if prev_word in negation_words:
if ew['sentiment'] == 'positive':
sentiment_score += neg_weight
else:
sentiment_score += pos_weight
continue # 已处理,跳过后续权重计算
# 处理程度词
if prev_word in degree_words:
if ew['sentiment'] == 'positive':
sentiment_score += pos_weight * degree_words[prev_word]
else:
sentiment_score += neg_weight * degree_words[prev_word]
continue # 已处理,跳过后续权重计算
# 普通情感词权重
if ew['sentiment'] == 'positive':
sentiment_score += pos_weight
else:
sentiment_score += neg_weight
# 特殊情感词组合(如“垃圾优化”)
if i < len(words) - 1:
combo = word + words[i + 1]
if combo in negative_words:
sentiment_score += neg_weight * 1.2 # 组合词权重更高
elif combo in positive_words:
sentiment_score += pos_weight * 1.2
# 4. 标点符号强化(!、!!、??? 增强情感)
punctuation_scores = {
'!': 0.2,
'!!': 0.4,
'!!!': 0.6,
'?': 0.1,
'??': 0.2,
'???': 0.3,
'~': 0.1,
'~~': 0.2
}
# 查找连续标点
punc_pattern = re.compile(r'([!?.~])\1*')
for match in punc_pattern.finditer(text):
punc_seq = match.group(0)
if punc_seq in punctuation_scores:
# 根据情感极性调整标点影响
sentiment_score += punctuation_scores[punc_seq] * np.sign(sentiment_score)
# 5. 情感分类判定
pos_count = len([w for w in emotion_words if w['sentiment'] == 'positive'])
neg_count = len([w for w in emotion_words if w['sentiment'] == 'negative'])
# 情感词数量差距较大时,直接按数量判断
if abs(pos_count - neg_count) >= 2:
sentiment = 'positive' if pos_count > neg_count else 'negative'
else:
# 优先用情感分数判断,分数相近时 fallback 到词数
if abs(sentiment_score) > 0.1:
sentiment = 'positive' if sentiment_score > 0 else 'negative'
else:
if pos_count > neg_count:
sentiment = 'positive'
elif neg_count > pos_count:
sentiment = 'negative'
else:
sentiment = 'neutral'
# 规范化分数到 [-1, 1] 区间
normalized_score = max(-1, min(1, sentiment_score))
return {
'sentiment': sentiment,
'emotion_words': emotion_words,
'pos_count': pos_count,
'neg_count': neg_count,
'sentiment_score': normalized_score # 新增分数用于验证
}
def evaluate_recommendation(row):
"""评估推荐判断是否合理"""
actual = row['是否推荐']
predicted = row['sentiment_analysis']
if actual == '推荐' and predicted == 'positive':
return '正确推荐'
elif actual == '不推荐' and predicted == 'negative':
return '正确不推荐'
elif actual == '推荐' and predicted == 'negative':
return '错误推荐'
elif actual == '不推荐' and predicted == 'positive':
return '错误不推荐'
else:
return '中性评价'
def filter_by_sentiment(words, sentiment):
"""根据情感过滤词语"""
if sentiment == 'positive':
return [word for word in words if word in positive_words]
elif sentiment == 'negative':
return [word for word in words if word in negative_words]
else:
return words
def word_frequency_analysis(df, by_recommendation=None):
"""词频统计分析"""
if by_recommendation is not None:
df = df[df['是否推荐'] == by_recommendation]
all_words = []
for words_list in df['分词结果']:
# 检查是否为NaN或None
if pd.isna(words_list) or words_list is None:
continue
# 确保words_list是一个列表
if isinstance(words_list, str):
try:
words_list = eval(words_list)
except:
continue
if isinstance(words_list, list):
# 根据推荐类型过滤词语
if by_recommendation == '推荐':
words_list = filter_by_sentiment(words_list, 'positive')
elif by_recommendation == '不推荐':
words_list = filter_by_sentiment(words_list, 'negative')
all_words.extend([word for word in words_list if isinstance(word, str)])
word_counts = Counter(all_words)
return pd.DataFrame(word_counts.most_common(), columns=['word', 'count'])
# 3. 可视化函数
def plot_daily_review_users(df):
"""绘制每日评价人数曲线"""
daily_counts = df.groupby(df['发布时间(2024年)'].dt.date).size().reset_index(name='count')
plt.figure(figsize=(12, 6))
sns.lineplot(data=daily_counts, x='发布时间(2024年)', y='count')
plt.title('每日评价人数曲线')
plt.xlabel('日期')
plt.ylabel('评价人数')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
def plot_recommendation_pie(df, selected_date=None):
"""绘制推荐/不推荐饼图"""
if selected_date and selected_date != 'All':
filtered_df = df[df['发布时间(2024年)'].dt.date == pd.to_datetime(selected_date).date()]
else:
filtered_df = df.copy()
rec_counts = filtered_df['是否推荐'].value_counts().reset_index()
rec_counts.columns = ['推荐状态', '数量']
rec_counts['推荐状态'] = rec_counts['推荐状态'].map({'推荐': '推荐', '不推荐': '不推荐'})
fig = px.pie(rec_counts, values='数量', names='推荐状态',
title=f'推荐状态分布 ({selected_date if selected_date and selected_date != "All" else "全部日期"})')
fig.show()
def plot_hours_recommendation(df):
"""绘制游戏时长与推荐关系分析"""
plt.figure(figsize=(12, 6))
# 将 '大圣游戏时长' 列转换为数值类型,非数值的转换为 NaN
df['大圣游戏时长'] = pd.to_numeric(df['大圣游戏时长'], errors='coerce')
# 过滤掉非数值的行
filtered_df = df.dropna(subset=['大圣游戏时长'])
# 过滤异常值(假设超过500小时为异常)
filtered_df = filtered_df[filtered_df['大圣游戏时长'] <= 500]
sns.boxplot(data=filtered_df, x='是否推荐', y='大圣游戏时长')
plt.title('游戏时长与推荐关系分析')
plt.xlabel('推荐状态')
plt.ylabel('游戏时长(小时)')
plt.xticks([0, 1], ['不推荐', '推荐'])
plt.show()
# 添加统计信息
print("\n游戏时长统计信息:")
print(filtered_df.groupby('是否推荐')['大圣游戏时长'].describe())
def generate_wordcloud(word_freq_df, title, sentiment):
"""生成词云图"""
# 根据情感类型进一步过滤
if sentiment == 'positive':
word_freq_df = word_freq_df[word_freq_df['word'].isin(positive_words)]
elif sentiment == 'negative':
word_freq_df = word_freq_df[word_freq_df['word'].isin(negative_words)]
if len(word_freq_df) == 0:
print(f"\n没有足够的情感词数据生成{title}词云")
return
word_dict = dict(zip(word_freq_df['word'], word_freq_df['count']))
wc = WordCloud(
font_path='simhei.ttf', # 使用中文字体,需确保字体文件存在
width=800,
height=600,
background_color='white',
max_words=100,
colormap='viridis' if sentiment == 'positive' else 'inferno'
).generate_from_frequencies(word_dict)
plt.figure(figsize=(10, 8))
plt.imshow(wc, interpolation='bilinear')
plt.title(title)
plt.axis('off')
plt.show()
def plot_emotion_category_distribution(df):
"""绘制情感词汇分类分布"""
# 提取所有情感词汇
all_emotion_words = []
for words in df['emotion_words']:
all_emotion_words.extend(words)
# 创建DataFrame
if not all_emotion_words:
print("未提取到情感词汇")
return
emotion_df = pd.DataFrame(all_emotion_words)
# 正面情感词汇分类分布
pos_df = emotion_df[emotion_df['sentiment'] == 'positive']
if not pos_df.empty:
pos_counts = pos_df['category'].value_counts().reset_index()
pos_counts.columns = ['分类', '数量']
fig1 = px.bar(pos_counts,
x='分类', y='数量',
title='正面情感词汇分类分布',
labels={'分类': '情感分类', '数量': '出现次数'})
fig1.show()
# 负面情感词汇分类分布
neg_df = emotion_df[emotion_df['sentiment'] == 'negative']
if not neg_df.empty:
neg_counts = neg_df['category'].value_counts().reset_index()
neg_counts.columns = ['分类', '数量']
fig2 = px.bar(neg_counts,
x='分类', y='数量',
title='负面情感词汇分类分布',
labels={'分类': '情感分类', '数量': '出现次数'})
fig2.show()
def plot_emotion_words_distribution(df):
"""绘制情感词汇分布图(分积极和消极)"""
# 提取所有情感词汇
all_emotion_words = []
for words in df['emotion_words']:
all_emotion_words.extend(words)
if not all_emotion_words:
print("未提取到情感词汇")
return
# 创建DataFrame
emotion_df = pd.DataFrame(all_emotion_words)
# 统计每个词汇的出现次数
word_stats = emotion_df.groupby(['word', 'sentiment']).size().reset_index(name='count')
# 分离积极和消极词汇
pos_words = word_stats[word_stats['sentiment'] == 'positive'].sort_values('count', ascending=False).head(20)
neg_words = word_stats[word_stats['sentiment'] == 'negative'].sort_values('count', ascending=False).head(20)
# 创建子图
plt.figure(figsize=(15, 10))
# 积极词汇分布
plt.subplot(2, 1, 1)
sns.barplot(data=pos_words, x='count', y='word', palette='Greens_d')
plt.title('Top 20 积极情感词汇分布')
plt.xlabel('出现次数')
plt.ylabel('词汇')
# 消极词汇分布
plt.subplot(2, 1, 2)
sns.barplot(data=neg_words, x='count', y='word', palette='Reds_d')
plt.title('Top 20 消极情感词汇分布')
plt.xlabel('出现次数')
plt.ylabel('词汇')
plt.tight_layout()
plt.show()
def generate_emotion_words_table(df):
"""生成情感词汇提取统计表"""
# 提取所有情感词汇
all_emotion_words = []
for words in df['emotion_words']:
all_emotion_words.extend(words)
if not all_emotion_words:
print("未提取到情感词汇")
return None
# 创建DataFrame
emotion_df = pd.DataFrame(all_emotion_words)
# 统计每个词汇的出现次数
word_stats = emotion_df.groupby(['word', 'sentiment', 'category']).size().reset_index(name='count')
word_stats = word_stats.sort_values(by='count', ascending=False)
# 添加情感强度标记
word_stats['强度'] = word_stats['count'].apply(
lambda x: '高频' if x >= 10 else ('中频' if x >= 5 else '低频'))
# 重新排列列顺序
word_stats = word_stats[['word', 'sentiment', 'category', 'count', '强度']]
return word_stats
def display_sample_reviews(df, n=5):
"""展示样本评论及其分析结果"""
if len(df) == 0:
print("没有可用的评论数据")
return
# 确保样本数量不超过数据行数
n = min(n, len(df))
sample_df = df.sample(n)[
['评论', '是否推荐', 'sentiment_analysis', 'pos_count', 'neg_count', 'recommendation_evaluation',
'sentiment_score']]
print("\n随机样本评论分析结果:")
print(sample_df.to_string(index=False))
# 展示情感词汇示例
print("\n情感词汇示例:")
for idx, row in df[df['emotion_words'].apply(len) > 0].sample(
min(n, len(df[df['emotion_words'].apply(len) > 0]))).iterrows():
print(f"\n评论: {row['评论'][:80]}...")
print(f"情感分数: {row['sentiment_score']:.2f}")
print("情感词汇:")
for word in row['emotion_words']:
print(f" {word['word']} ({word['sentiment']} - {word['category']})")
# 新增:原评论情感分类展示功能
def display_sentiment_categories(df, n=5):
"""展示不同情感类别的评论示例"""
print("\n=== 原评论情感分类展示 ===")
# 按情感分类分组并获取示例
positive_samples = df[df['sentiment_analysis'] == 'positive']
negative_samples = df[df['sentiment_analysis'] == 'negative']
neutral_samples = df[df['sentiment_analysis'] == 'neutral']
# 确保有足够的样本
if len(positive_samples) == 0:
print("\n没有找到积极情感的评论")
else:
print("\n1. 积极情感评论示例:")
display_positive = positive_samples.sample(min(n, len(positive_samples)))[
['评论', '是否推荐', 'pos_count', 'neg_count', 'sentiment_score']]
display_positive.columns = ['评论内容', '用户是否推荐', '积极词汇数', '消极词汇数', '情感分数']
print(display_positive.to_string(index=False))
if len(negative_samples) == 0:
print("\n没有找到消极情感的评论")
else:
print("\n2. 消极情感评论示例:")
display_negative = negative_samples.sample(min(n, len(negative_samples)))[
['评论', '是否推荐', 'pos_count', 'neg_count', 'sentiment_score']]
display_negative.columns = ['评论内容', '用户是否推荐', '积极词汇数', '消极词汇数', '情感分数']
print(display_negative.to_string(index=False))
if len(neutral_samples) == 0:
print("\n没有找到中性情感的评论")
else:
print("\n3. 中性情感评论示例:")
display_neutral = neutral_samples.sample(min(n, len(neutral_samples)))[
['评论', '是否推荐', 'pos_count', 'neg_count', 'sentiment_score']]
display_neutral.columns = ['评论内容', '用户是否推荐', '积极词汇数', '消极词汇数', '情感分数']
print(display_neutral.to_string(index=False))
def analyze_sentiment_categories(df):
"""分析情感分类分布并展示详细数据"""
# 统计情感分类数量
sentiment_counts = df['sentiment_analysis'].value_counts().reset_index()
sentiment_counts.columns = ['情感类别', '评论数量']
sentiment_counts['比例(%)'] = (sentiment_counts['评论数量'] / len(df) * 100).round(2)
print("\n=== 评论情感分类统计 ===")
print(sentiment_counts.to_string(index=False))
# 计算不同推荐状态下的情感分布
recommendation_sentiment = df.groupby(['是否推荐', 'sentiment_analysis']).size().unstack(fill_value=0)
recommendation_sentiment['总计'] = recommendation_sentiment.sum(axis=1)
recommendation_sentiment['积极比例(%)'] = (
recommendation_sentiment['positive'] / recommendation_sentiment['总计'] * 100).round(2)
recommendation_sentiment['消极比例(%)'] = (
recommendation_sentiment['negative'] / recommendation_sentiment['总计'] * 100).round(2)
recommendation_sentiment['中性比例(%)'] = (
recommendation_sentiment['neutral'] / recommendation_sentiment['总计'] * 100).round(2)
print("\n=== 推荐状态与情感分类交叉分析 ===")
print(recommendation_sentiment.to_string())
# 新增:情感分析验证可视化
def plot_sentiment_score_distribution(df):
"""
绘制情感分数分布图,验证情感分析的合理性:
- 积极评论应集中在正分
- 消极评论应集中在负分
- 中性评论应接近0分
"""
plt.figure(figsize=(12, 6))
sns.histplot(
data=df,
x='sentiment_score',
hue='sentiment_analysis',
multiple='layer',
palette={'positive': '#2ecc71', 'negative': '#e74c3c', 'neutral': '#95a5a6'},
bins=30,
kde=True
)
plt.title('情感分数分布与情感标签验证')
plt.xlabel('情感分数 (-1~1)')
plt.ylabel('评论数量')
plt.legend(title='情感标签')
plt.show()
# 输出统计描述
print("\n情感分数统计描述:")
print(df.groupby('sentiment_analysis')['sentiment_score'].describe())
# 新增:推荐判断准确性分析
def plot_recommendation_accuracy(df):
"""绘制推荐判断准确性分析图"""
eval_counts = df['recommendation_evaluation'].value_counts().reset_index()
eval_counts.columns = ['评估结果', '数量']
fig = px.pie(eval_counts, values='数量', names='评估结果',
title='推荐判断准确性分析')
fig.show()
# 计算准确率
correct_count = len(df[(df['recommendation_evaluation'] == '正确推荐') |
(df['recommendation_evaluation'] == '正确不推荐')])
total_count = len(df)
accuracy = correct_count / total_count * 100
print(f"\n推荐判断准确率: {accuracy:.2f}%")
# 4. 主执行流程
def main():
# 1. 加载数据
print("正在加载数据...")
reviews_df = load_review_data()
if len(reviews_df) == 0:
print("没有找到评论数据,程序退出")
return
print(f"成功加载 {len(reviews_df)} 条评论数据")
# 2. 提取用户ID
print("正在进行数据预处理...")
reviews_df = extract_user_id(reviews_df)
# 3. 可视化基本图表
print("\n1. 用户每日评价人数曲线:")
plot_daily_review_users(reviews_df)
print("\n2. 游戏时长与推荐关系分析:")
plot_hours_recommendation(reviews_df)
# 4. 中文分词与词频分析
print("\n3. 正在进行中文分词处理...")
reviews_df['分词结果'] = reviews_df['评论'].apply(chinese_word_segmentation)
# 将分词结果转换为字符串表示,便于后续处理
reviews_df['分词结果'] = reviews_df['分词结果'].apply(lambda x: str(x) if isinstance(x, list) else x)
print("\n4. 词频统计分析:")
word_freq_all = word_frequency_analysis(reviews_df)
print("所有评论中的Top 20词:")
print(word_freq_all.head(20))
word_freq_recommended = word_frequency_analysis(reviews_df, by_recommendation='推荐')
print("\n推荐评论中的Top 20词:")
print(word_freq_recommended.head(20))
word_freq_not_recommended = word_frequency_analysis(reviews_df, by_recommendation='不推荐')
print("\n不推荐评论中的Top 20词:")
print(word_freq_not_recommended.head(20))
# 5. 生成词云图
print("\n5. 生成推荐评论词云图:")
generate_wordcloud(word_freq_recommended, '推荐评论词云图', 'positive')
print("\n6. 生成不推荐评论词云图:")
generate_wordcloud(word_freq_not_recommended, '不推荐评论词云图', 'negative')
# 6. 情感分析
print("\n7. 正在进行情感分析...")
sentiment_results = reviews_df['评论'].apply(analyze_sentiment_with_details)
reviews_df['sentiment_analysis'] = sentiment_results.apply(lambda x: x['sentiment'])
reviews_df['pos_count'] = sentiment_results.apply(lambda x: x['pos_count'])
reviews_df['neg_count'] = sentiment_results.apply(lambda x: x['neg_count'])
reviews_df['emotion_words'] = sentiment_results.apply(lambda x: x['emotion_words'])
reviews_df['sentiment_score'] = sentiment_results.apply(lambda x: x['sentiment_score'])
reviews_df['recommendation_evaluation'] = reviews_df.apply(evaluate_recommendation, axis=1)
# 7. 情感分析可视化
print("\n8. 情感分析可视化:")
# 情感分布饼图
fig1 = px.pie(reviews_df, names='sentiment_analysis', title='评论情感分布')
fig1.show()
# 推荐判断结果饼图
plot_recommendation_accuracy(reviews_df)
# 情感词汇分类分布
plot_emotion_category_distribution(reviews_df)
# 情感词汇分布图
plot_emotion_words_distribution(reviews_df)
# 情感分数分布验证
plot_sentiment_score_distribution(reviews_df)
# 8. 生成情感词汇统计表
print("\n9. 生成情感词汇统计表...")
emotion_words_table = generate_emotion_words_table(reviews_df)
if emotion_words_table is not None:
# 保存到CSV
emotion_words_table.to_csv('emotion_words_statistics.csv', index=False, encoding='utf_8_sig')
print("情感词汇统计表已保存到 emotion_words_statistics.csv")
# 显示高频词汇
print("\n高频情感词汇:")
print(emotion_words_table[emotion_words_table['强度'] == '高频'].head(20))
# 9. 展示样本分析结果
display_sample_reviews(reviews_df, n=10)
# 新增:原评论情感分类展示
print("\n10. 原评论情感分类展示:")
analyze_sentiment_categories(reviews_df)
display_sentiment_categories(reviews_df, n=5) # 每个类别展示5条示例
# 10. 保存分析结果
reviews_df.to_csv('analyzed_reviews_with_emotions.csv', index=False, encoding='utf_8_sig')
print("\n分析结果已保存到 analyzed_reviews_with_emotions.csv")
# 11. 交互式饼图(在Jupyter Notebook中运行效果最佳)
print("\n11. 准备交互式推荐比例饼图...")
unique_dates = reviews_df['发布时间(2024年)'].dt.date.unique()
date_options = [str(date) for date in sorted(unique_dates)]
# 在Jupyter Notebook中取消下面注释以使用交互功能
# interact(plot_recommendation_pie, df=widgets.fixed(reviews_df),
# selected_date=widgets.Dropdown(options=['All'] + date_options, value='All', description='Select Date'))
print("\n所有分析已完成!")
if __name__ == "__main__":
main()