Contents

番剧《间谍过家家》的评论分析

目的:分析间谍过家家的评价,了解用户对该番剧的评价 , ,数据源:kaggle上的Bilibili Spy X Family数据集

#导入所需库
,import pandas as pd
,# pd.plotting.register_matplotlib_converters()
,import matplotlib.pyplot as plt
,#%matplotlib inline#可省,但保留可提高可移植性,以养成规范化习惯
,import seaborn as sns
,print("Setup Complete")
,
,# 设置中文字体显示
,plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体显示中文
,plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号
,
,
,#画图结束,代码单元格结尾,以养成规范化习惯
,plt.show()  # 保留以确保代码可移植

输出结果

Setup Complete
filepath="data/spyxfamily.csv"
,# 读取CSV文件
,df = pd.read_csv(filepath,index_col="id")
,# 数据检查
,df.head()
,

输出结果

,1. 缺失值处理:找到comment列有的缺失值,可以直接删除该行数据,以保证评论内容的完整性。

# 检查数据集中是否存在空值
,print("数据集中空值的数量:")
,print(df.isnull().sum())
,print("\n总空值数量:", df.isnull().sum().sum())
,# 删除comment列中的空值行
,df = df.dropna(subset=['comment'])
,df.reset_index(drop=True, inplace=True)
,# 检查author列的重复值数量
,duplicate_authors = df['author'].duplicated().sum()
,print(f"作者名重复的数量: {duplicate_authors}")

输出结果

数据集中空值的数量:
,author        0
,comment       1
,stars1        0
,stars2        0
,stars3        0
,stars4        0
,stars5        0
,unlike     2849
,like       3138
,dtype: int64
,
,总空值数量: 5988
,作者名重复的数量: 0
  1. 提取评分信息
# 将评分数据中的星星图标替换为数字1
,df = df.replace('icon-star icon-star-light', '1')
,# 将评分数据中的星星图标替换为数字0
,df = df.replace('icon-star', '0')
,# 创建score列,计算每行星级评分的总和
,columns_to_convert = [f'stars{i}' for i in range(1, 6)]  # 生成['stars1', 'stars2', ..., 'stars5']
,# 2. 循环转换每一列(处理可能的非数值内容)
,for col in columns_to_convert:
,    # errors='coerce'表示无法转换的值会被设为NaN,方便后续处理
,    df[col] = pd.to_numeric(df[col], errors='coerce')
,df[columns_to_convert] = df[columns_to_convert].fillna(0)
,score = df['stars1'] + df['stars2'] + df['stars3'] + df['stars4'] + df['stars5']
,df['score'] = score
,# 1. 定义需要删除的列名
,columns_to_drop = ['stars1', 'stars2', 'stars3', 'stars4', 'stars5']
,
,# 2. 删除指定列(两种方式可选)
,# 方式1:原地删除(直接修改原df,不返回新对象)
,#df.drop(columns=columns_to_drop, axis=1, inplace=True)
,
,# 方式2:不原地删除(返回删除列后的新df,原df保持不变)
,df = df.drop(columns=columns_to_drop, axis=1)
,#检查星级转换评分是否成功
,df.head()

输出结果

,1. 基于评分的分类,将评论分为正面评价(4,5),负面评价(0,1,2),中性评价(3)

# 根据评分添加情感倾向列
,def get_sentiment(score):
,    if score >= 4:
,        return "正面"
,    elif score <= 2:
,        return "负面"
,    else:
,        return "中性"
,
,df['倾向'] = df['score'].apply(get_sentiment)
,# 检查情感倾向列是否添加成功
,df.head()

输出结果

# 统计各种评论倾向的占比
,tendency_counts = df['倾向'].value_counts()
,tendency_percentages = tendency_counts / len(df) * 100
,
,# 创建饼图展示倾向分布
,plt.figure(figsize=(10, 8))
,plt.pie(tendency_percentages, 
,        labels=tendency_counts.index,
,        autopct='%1.1f%%',
,        colors=['lightgreen', 'lightcoral', 'lightskyblue'])
,plt.title('评论倾向分布')
,plt.axis('equal')
,plt.show()
,
,# 打印具体数值
,print("\n各倾向评论数量及占比:")
,for tendency, count in tendency_counts.items():
,    percentage = tendency_percentages[tendency]
,    print(f"{tendency}: {count}条评论 ({percentage:.1f}%)")

输出结果

<Figure size 1000x800 with 1 Axes>

,各倾向评论数量及占比:
,正面: 3059条评论 (97.5%)
,中性: 46条评论 (1.5%)
,负面: 32条评论 (1.0%)
  1. 基于评论内容的分类
# 定义关键词字典,用于匹配不同类别
,category_keywords = {
,    "剧情": ["剧情", "情节", "故事线", "发展", "漏洞", "设定"],
,    "角色": ["角色", "人物", "性格", "可爱", "喜欢", "讨厌"],
,    "画风": ["画风", "画面", "色彩", "绘制"],
,}
,
,
,def classify_comment(comment):
,    """
,    根据评论内容和关键词字典对评论进行分类
,    """
,    for category, keywords in category_keywords.items():
,        for keyword in keywords:
,            if keyword in str(comment):
,                return category
,    return "其他"
,
,
,# 对数据集中的评论进行分类
,df['category'] = df['comment'].apply(classify_comment)
,df.head()

输出结果

            author                                            comment  unlike  \
,0             琴-格雷                                   我只能说,非常好,次瓜真的很好磕   149.0   
,1           憨Sir_r  整个第一季很好看,但看久了不难发现现有设定是支撑不起继续往下走了。纯当一部搞笑日常番还是很棒...    65.0   
,2       K_YokoHama                                多少年没看到这种正经又轻松的番了呜呜呜    47.0   
,3  physicalwilling                   轻喜剧中交织着家庭、爱、子女、国家与责任,喻重于轻,罕见的佳作!     4.0   
,4            迷之小依然      当做搞笑休闲番来看就好了,感觉剧情发展太过于慢而拖,总体上还是不错了(超喜欢可爱的阿妮娅)     2.0   
,
,   like  score  倾向 category  
,0   NaN      5  正面       其他  
,1   NaN      4  正面       剧情  
,2   NaN      5  正面       其他  
,3   NaN      5  正面       其他  
,4   NaN      4  正面       剧情  
# 统计各类评论的占比
,tendency_counts = df['category'].value_counts()
,tendency_percentages = tendency_counts / len(df) * 100
,
,# 创建饼图展示评论类型分布
,plt.figure(figsize=(10, 8))
,plt.pie(tendency_percentages, 
,        labels=tendency_counts.index,
,        autopct='%1.1f%%',
,        colors=['lightgreen', 'lightcoral', 'lightskyblue','orange'])
,
,plt.title('评论类型分布')
,plt.axis('equal')
,plt.show()
,
,# 打印具体数值
,print("\n各类别评论数量及占比:")
,for tendency, count in tendency_counts.items():
,    percentage = tendency_percentages[tendency]
,    print(f"{tendency}: {count}条评论 ({percentage:.1f}%)")

输出结果

<Figure size 1000x800 with 1 Axes>

,各类别评论数量及占比:
,其他: 2537条评论 (80.9%)
,角色: 451条评论 (14.4%)
,剧情: 143条评论 (4.6%)
,画风: 6条评论 (0.2%)
# 按category分组并找出每组like数最多的5条评论
,top_comments = df.sort_values('unlike', ascending=False).groupby('category').head(3)
,
,# 展示结果
,print("各类别下获赞最多的评论:")
,print("-" * 50)
,for idx, row in top_comments.iterrows():
,    print(f"类别: {row["category"]}")
,    print(f"评论内容: {row['comment']}")
,    print(f"获赞数: {row['unlike']}")
,    print("-" * 50)

输出结果

各类别下获赞最多的评论:
,--------------------------------------------------
,类别: 其他
,评论内容: 期待十月的第二季!
,获赞数: 220.0
,--------------------------------------------------
,类别: 其他
,评论内容: 我只能说,非常好,次瓜真的很好磕
,获赞数: 149.0
,--------------------------------------------------
,类别: 其他
,评论内容: 在粗制滥造的穿越动漫横生的时期,这部动漫的制作挺不错。这部动漫很适合放松,让人愉快。不要拿来和那些经典神作比,能有什么好比的。
,获赞数: 90.0
,--------------------------------------------------
,类别: 剧情
,评论内容: 我觉得动画做的挺好的,本身这个就是这种比较平淡的日常,但是又莫名其妙的有趣,而且评分高不代表神作啊,只能说大部分人觉得剧情没问题,有看点,给高分又惹到谁了?
,获赞数: 70.0
,--------------------------------------------------
,类别: 角色
,评论内容: 好可爱啊阿妮亚好可爱
,获赞数: 70.0
,--------------------------------------------------
,类别: 剧情
,评论内容: 整个第一季很好看,但看久了不难发现现有设定是支撑不起继续往下走了。纯当一部搞笑日常番还是很棒的,但只要一往间谍情报方面想,就显得干瘪有漏洞了。
,获赞数: 65.0
,--------------------------------------------------
,类别: 角色
,评论内容: 整体很好,但只适合当搞笑番,像是上帝视角看菜鸡互啄的感觉。情感不随人物变化,含义并不深刻。不过应该算是符合当今时代潮流了。
,获赞数: 44.0
,--------------------------------------------------
,类别: 剧情
,评论内容: 剧情一般
,获赞数: 7.0
,--------------------------------------------------
,类别: 角色
,评论内容: 超能力女儿阿尼亚可可爱爱,与间谍父亲、杀手母亲一起开始过家家的旅行,一个搞笑、感动、日常的故事非常轻松和治愈。最后一集有点仓促不算作结束,但可以期待十月的第二季!哇库哇库!表情帝阿尼亚实在太可爱啦!
,获赞数: 5.0
,--------------------------------------------------
,类别: 画风
,评论内容: 感觉制作不太好,画面太单调,其他的还行
,获赞数: 2.0
,--------------------------------------------------
,类别: 画风
,评论内容: 虽然这部作品有点偏低龄化,我也是抱着当乐子看的,不过这部番确实带给了我不是乐趣,动画风格也很像是一部喜剧,动画制作质量也算得上一部佳作
,获赞数: 2.0
,--------------------------------------------------
,类别: 画风
,评论内容: 画风精致的日常番
,获赞数: nan
,--------------------------------------------------
  1. 负面评价分析
import re
,import jieba
,from collections import Counter
,from wordcloud import WordCloud
,import seaborn as sns
,
,# 定义负面评论的判断规则(基于星级标识,stars5为单星时视为负面)
,negative_comments = df[df['score'] < 4]['comment'].dropna()  # 过滤空值
,
,
,# 加载中文停用词表
,with open('data/stopwords-zh.txt', 'r', encoding='utf-8') as f:
,    stopwords = set([line.strip() for line in f])
,
,
,# 中文文本预处理(清洗+分词+去停用词)
,def preprocess_text(text):
,    # 去除特殊字符和数字
,    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z]', ' ', str(text))
,    # 分词
,    words = jieba.cut(text)
,    # 去除停用词和空字符串
,    words = [word for word in words if word.strip() and word not in stopwords]
,    return words
,
,# 对负面评论进行预处理
,processed_comments = []
,for comment in negative_comments:
,    processed_comments.extend(preprocess_text(comment))
,
,# 统计高频词(问题点)
,word_counts = Counter(processed_comments)
,top_issues = word_counts.most_common(15)  # 取前15个高频问题点
,
,# 输出结果
,# print("负面评论中高频出现的问题点:")
,# for issue, count in top_issues:
,#     print(f"{issue}: {count}次")
,
,
,# 使用wordcloud生成词云图
,# 导入所需库
,import numpy as np
,
,
,# 创建数据
,words = [item[0] for item in top_issues]
,frequencies = [item[1] for item in top_issues]
,
,# 将数据重塑成矩阵形式
,data_matrix = np.array(frequencies).reshape(3, 5)  # 3x5的矩阵布局
,
,
,# 创建热力图
,plt.figure(figsize=(12, 6))
,sns.heatmap(data_matrix, 
,            annot=np.array(words).reshape(3, 5),  # 显示关键词文本
,            fmt='',     # 文本格式
,            cmap='YlOrRd',  # 使用红色系配色
,            cbar_kws={'label': '出现频次'})
,
,plt.title('负面评论关键词热力图')
,plt.xlabel('关键词')
,plt.ylabel('关键词')
,plt.tight_layout()
,plt.show()
,
,# 将词频转换为字典格式
,word_freq_dict = dict(top_issues)
,
,# 配置词云图参数
,wc = WordCloud(
,    font_path=r"C:\Windows\Fonts\msyh.ttc",  # 使用黑体字体以正确显示中文
,    width=800,
,    height=400,
,    background_color='white',
,    max_words=100,
,    max_font_size=100
,)
,
,# 生成词云
,wc.generate_from_frequencies(word_freq_dict)
,
,# 显示词云图
,plt.figure(figsize=(10, 6))
,plt.imshow(wc, interpolation='bilinear')
,plt.axis('off')
,plt.title('负面评论关键词云图')
,plt.show()

输出结果

<Figure size 1200x600 with 2 Axes>
<Figure size 1000x600 with 1 Axes>
  1. 中文情感分析
# 1. 加载情感词典(处理空行和格式错误)
,def load_sentiment_dict(path):
,    sentiment_dict = {}
,    with open(path, 'r', encoding='utf-8') as f:
,        for line_num, line in enumerate(f, 1):  # 记录行号,方便排查错误
,            line = line.strip()
,            # 跳过空行
,            if not line:
,                continue
,            # 按空格分割,确保得到2个元素
,            parts = line.split()
,            if len(parts) != 2:
,                print(f"警告:第{line_num}行格式错误,已跳过 -> {line}")
,                continue
,            word, score = parts
,            try:
,                sentiment_dict[word] = float(score)
,            except ValueError:
,                print(f"警告:第{line_num}行分数格式错误,已跳过 -> {line}")
,    return sentiment_dict
,
,
,# 2. 加载停用词表
,def load_stopwords(path):
,    with open(path, 'r', encoding='utf-8') as f:
,        return set([line.strip() for line in f])
,
,# 3. 文本预处理(清洗+分词+去停用词)
,def preprocess(text, stopwords):
,    # 去除特殊字符和数字
,    text = re.sub(r'[^\u4e00-\u9fa5]', ' ', text)
,    # 分词
,    words = jieba.cut(text)
,    # 过滤停用词
,    return [word for word in words if word.strip() and word not in stopwords]
,
,# 4. 计算情感得分
,def get_sentiment_score(words, sentiment_dict):
,    score = 0.0
,    for word in words:
,        if word in sentiment_dict:
,            score += sentiment_dict[word]  # 正面词加分,负面词减分
,    return score
,
,# 5. 判断情感倾向(自定义阈值)
,def classify_sentiment(score, threshold=0.5):
,    if score > threshold:
,        return "积极", score
,    elif score < -threshold:
,        return "消极", score
,    else:
,        return "中性", score
,sentiment_dict = load_sentiment_dict("data/BosonNLP_sentiment_score.txt")
,
,stopwords = load_stopwords("data/stopwords-zh.txt")  # 使用之前提供的停用词表
,  
,for text in df["comment"]:
,    words = preprocess(text, stopwords)
,    score = get_sentiment_score(words, sentiment_dict)
,    sentiment, score = classify_sentiment(score)
,    # 将分词结果和情感倾向添加到DataFrame中
,    df.at[df[df['comment'] == text].index[0], 'words'] = sentiment
,# 检查情感分析结果
,df.head()
,

输出结果

# 统计情感分布
,sentiment_counts = df['words'].value_counts()
,
,# 创建饼图
,plt.figure(figsize=(8, 8))
,plt.pie(sentiment_counts, labels=sentiment_counts.index, autopct='%1.1f%%')
,plt.title('评论情感分布')
,
,# 添加图例
,plt.legend(loc='best')
,
,# 确保饼图是圆形的
,plt.axis('equal')
,
,# 显示图表
,plt.show()

输出结果

<Figure size 800x800 with 1 Axes>

,这是一部广泛好评的搞笑欢快型日常番.剧情搞笑,角色设计讨喜,是一部值得推荐的动画.