一、分析大纲
-
用户行为分析
- 活跃用户分析
- 统计各用户的评论频次,识别最活跃的评论者
- 评分分布分析
- 分析用户给出的评分分布,了解电影的整体评价倾向
- 正负面评论分类
- 利用情感分析等自然语言处理技术,对评论进行正负面分类,计算正负面评论占比
- 活跃用户分析
-
地域性偏好分析
- 城市评分对比
- 分析不同城市用户的平均评分,探索地域性偏好差异
- 热门城市评论热点
- 分析评论量最多的城市,挖掘用户在这些城市的关注点或喜好
- 城市评分对比
-
时间序列分析
- 评论趋势分析
- 根据"评论时间",绘制时间序列图,观察电影发布后的评论增长趋势及波动
- 评分变化分析
- 分析电影发布后不同时间段的平均评分变化,识别评分波动的关键时间点
- 评论趋势分析
-
文本挖掘与主题分析
- 关键词提取
- 从所有评论中提取高频关键词和短语,理解用户讨论的核心话题
- 主题建模
- 利用LDA等主题模型,发现评论中的隐藏主题,深入了解用户对电影的各种看法。
- 关键词提取
二、代码
import jieba
import jieba.posseg as pseg
import gensim
from gensim import corpora
from gensim.corpora import Dictionary
import stylecloud
import pandas as pd
from collections import Counter
from collections import defaultdict
from PIL import Image
from collections import Counter
from pyecharts.charts import Geo
from pyecharts.charts import Scatter
from pyecharts.charts import Line
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode
from pyecharts.globals import ChartType
from scipy.stats import f_oneway
import math
import seaborn as sns
import geopandas as gpd
from wordcloud import WordCloud
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.patches import Wedge, Polygon
from matplotlib.offsetbox import TextArea, AnnotationBbox
plt.rcParams['font.sans-serif']=['Microsoft YaHei']
plt.rcParams['axes.unicode_minus']=False
df = pd.read_excel("独行月球.xlsx")
df.info()
df['城市'].value_counts().reset_index()
df['评分'].value_counts().reset_index()
def analyze_active_users(df, top_n=30):
"""
分析活跃用户,统计各用户的评论频次,并找出最活跃的评论者。
参数:
df (dataframe): 包含用户评论数据的DataFrame。
top_n (int): 输出的最活跃用户数量,默认为30。
返回:
most_active_users: 列表,包含最活跃的top_n个评论者的用户名。
"""
# 计算每个用户的评论次数
user_counts = Counter(df['用户名'])
# 找出评论最频繁的用户
top_users = user_counts.most_common(top_n)
print("\n活跃用户分析:")
print(f"最活跃的评论者: {top_users[0][0]}")
print("各用户评论频次:")
for user, count in top_users:
print(f"{user}: {count}条评论")
return [user for user, _ in top_users]
analyze_active_users(df, top_n=30)
def analyze_movie_ratings(df):
"""
分析电影评分分布,包括统计指标计算和评分分布直方图绘制。
参数:
df (dataframe): 包含电影评分数据的CSV文件路径。
目的:
1. 加载数据,查看数据前几行确保正确加载。
2. 计算评分的平均值、中位数、最小值、最大值及众数,以获取评分概况。
3. 绘制评分分布直方图,直观展示不同评分的频率分布,帮助理解评价倾向。
"""
# 计算评分的统计指标
rating_stats = df['评分'].describe()
print("\n评分统计指标:\n", rating_stats)
# 计算众数评分
mode_rating = df['评分'].mode()[0]
print(f"\n众数评分:{mode_rating}")
# 绘制评分分布直方图
plt.figure(figsize=(10, 6))
sns.histplot(data=df, x='评分', bins=10, kde=False)
plt.title('电影评分分布')
plt.xlabel('评分')
plt.ylabel('频数')
plt.show()
analyze_movie_ratings(df)
def analyze_city_ratings(df):
"""
分析不同城市用户的平均评分,探索地域性偏好差异。
参数:
data_path: str, 数据文件路径,包含用户ID、城市、评分等信息的CSV文件。
功能:
1. 计算每个城市的平均评分。
2. 可视化城市评分差异。
3. 进行ANOVA检验,判断城市间评分是否存在显著差异。
"""
# 计算每个城市的平均评分
city_avg_ratings = df.groupby('城市')['评分'].mean().sort_values(ascending=False)
print("城市平均评分:")
print(city_avg_ratings)
# 可视化城市评分差异
# 设置画布,1行2列的子图布局
fig, axes = plt.subplots(1, 2, figsize=(24, 8))
# 绘制评分最高的10个城市
sns.barplot(ax=axes[0], x=city_avg_ratings.head(26).index, y=city_avg_ratings.head(26).values)
axes[0].set_title('评分最高的10个城市用户平均评分')
axes[0].set_xlabel('城市')
axes[0].set_ylabel('平均评分')
axes[0].tick_params(axis='x', rotation=45)
# 绘制评分最低的10个城市
sns.barplot(ax=axes[1], x=city_avg_ratings.tail(30).index, y=city_avg_ratings.tail(30).values)
axes[1].set_title('评分最低的10个城市用户平均评分')
axes[1].set_xlabel('城市')
axes[1].set_ylabel('平均评分') # 这里可以省略,因为共用y轴标签
axes[1].tick_params(axis='x', rotation=45)
# 调整子图间距
plt.tight_layout()
# 显示图表
plt.show()
# 检验城市间评分差异的显著性
cities = city_avg_ratings.index.tolist()
scores_by_city = [df[df['城市'] == city]['评分'] for city in cities]
f_statistic, p_value = f_oneway(*scores_by_city)
if p_value < 0.05:
print(f"ANOVA Test: 存在显著性差异, F-statistic = {f_statistic}, P-value = {p_value}")
else:
print(f"ANOVA Test: 不存在显著性差异, F-statistic = {f_statistic}, P-value = {p_value}")
analyze_city_ratings(df)
def analyze_city_reviews(df):
"""
分析并可视化城市评论数据
参数:
df (DataFrame): 包含用户评论数据的DataFrame,预期包含'city', 'comment'等列。
功能:
- 统计每个城市的评论数量
- 确定评论最多的城市及其评论数量
- 提取评论最多城市的评论
- 分词并统计词频,找出热点关键词
- 可视化评论最多城市的热点词云
"""
# 统计每个城市的评论数量
city_count = df['城市'].value_counts()
# 找出评论最多的城市
top_city = city_count.idxmax()
top_city_count = city_count[top_city]
print(f"评论最多的城市是{top_city},共有{top_city_count}条评论。")
# 提取该城市的评论
top_city_comments = df[df['城市'] == top_city]['评论']
# 对评论进行分词和词频统计
words = ' '.join(top_city_comments).split()
word_count = Counter(words)
# 找出词频最高的关键词
top_keywords = [k for k, v in word_count.most_common(10)]
print("该城市用户关注的热点主题包括:")
print(', '.join(top_keywords))
# 可视化热点词云
plt.figure(figsize=(8, 8))
sns.set(font_scale=1.2)
wc = WordCloud(width=800, height=800, max_words=50, background_color='white',font_path='STXINWEI.TTF')
wc.generate_from_text(' '.join(top_city_comments)) # 使用 ' '.join 来合并字符串
# Assuming wc is your WordCloud object
plt.imshow(wc, interpolation='bilinear')
#sns.wordcloud(top_city_comments.str.cat(sep=' '), width=800, height=800, max_words=50, background_color='white')
plt.title(f"{top_city} 用户评论词云")
plt.axis('off') # 隐藏坐标轴
plt.show()
analyze_city_reviews(df)
def plot_daily_comment_distribution(df):
"""
根据DataFrame中的评论数据,绘制每日评论量分布折线图,并在每个点上标注数值。
参数:
df (DataFrame): DataFrame格式数据,包含评论数据。
返回:
None,但会显示绘制的每日评论量分布折线图。
"""
# 确保评论时间是datetime类型
df['评论时间'] = pd.to_datetime(df['评论时间'])
# 按日期分组并计算每天的评论数量
daily_comments = df.groupby(df['评论时间'].dt.date)['评论'].count()
# 绘制每日评论量分布折线图
plt.figure(figsize=(14, 7))
plt.plot(daily_comments.index, daily_comments.values, marker='o', linestyle='-', color='skyblue')
# 在每个数据点上添加数值标签
for x, y in zip(daily_comments.index, daily_comments.values):
plt.text(x, y, str(y), ha='center', va='bottom')
plt.title('每日评论量分布图')
plt.xlabel('日期')
plt.ylabel('评论数量')
plt.xticks(rotation=45) # 旋转x轴标签,以便更好地显示日期
plt.tight_layout() # 自动调整子图参数, 使之填充整个图像区域
# 显示图表
plt.show()
# 假设df是已经加载好的DataFrame数据
plot_daily_comment_distribution(df)
def plot_hourly_comment_distribution(df, time_column='评论时间'):
"""
绘制每小时评论分布曲线图,并在每个数据点旁边显示评论数。
参数:
df (pd.DataFrame): 包含评论时间的DataFrame。
time_column (str): 包含时间的列名,默认为'评论时间'。
"""
# 确保时间列是datetime类型
df[time_column] = pd.to_datetime(df[time_column])
# 提取小时数
df['小时'] = df[time_column].dt.hour
# 计算每个小时出现的评论数
hourly_counts = df['小时'].value_counts().sort_index()
# 设置字体以支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei'] # 适用于Windows系统,需要安装SimHei字体
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
# 绘制图表
plt.figure(figsize=(10, 6))
plt.plot(hourly_counts.index, hourly_counts.values, 'o-', color='gray', alpha=0.7) # 使用'o-'同时绘制点和线
# 添加文本标签
for x, y in zip(hourly_counts.index, hourly_counts.values):
# 为了避免标签重叠,可以稍微调整y的位置(这里我们向下移动了一点)
plt.text(x, y - 2, f'{y}', ha='center', va='top')
plt.title('每小时评论分布')
plt.xlabel('小时')
plt.ylabel('评论数')
plt.grid(True)
plt.xticks(range(24), labels=[f'{h}' for h in range(24)]) # 格式化x轴标签
# 显示图表
plt.show()
# ...
plot_hourly_comment_distribution(df)
def plot_weekly_comments_distribution(df):
"""
绘制每个星期几上的评论数据分布图。
参数:
df (pandas.DataFrame): 包含'id', '用户名', '城市', '评分', '评论', '评论时间'等列的DataFrame。
返回:
None(直接在图表中展示结果)
"""
# 确保'评论时间'列是datetime类型
if not pd.api.types.is_datetime64_any_dtype(df['评论时间']):
df['评论时间'] = pd.to_datetime(df['评论时间'])
# 从'评论时间'列中提取星期几的名称
df['weekday_name'] = df['评论时间'].dt.strftime('%A')
# 计算每个星期几的评论数
weekly_counts = df.groupby('weekday_name')['id'].count().reset_index(name='comments_count')
# 对星期几进行排序(可选,取决于你的需要)
weekly_counts.sort_values('weekday_name', inplace=True)
# 绘制条形图
plt.figure(figsize=(10, 6))
plt.bar(weekly_counts['weekday_name'], weekly_counts['comments_count'], color='skyblue')
plt.xlabel('星期几')
plt.ylabel('评论数')
plt.title('每个星期几的评论数分布')
plt.xticks(rotation=45) # 旋转x轴标签,以便更好地显示
plt.tight_layout() # 自动调整子图参数,使之填充整个图像区域
plt.show()
plot_weekly_comments_distribution(df)
# 关键词提取
def extract_keywords(text, topn=10):
"""
从文本中提取高频关键词和短语
参数:
text (str): 输入文本
topn (int): 返回的关键词和短语数量
返回:
list: 关键词和短语列表
"""
words = jieba.cut(text, cut_all=False)
word_freq = Counter(words)
keywords = [k for k, v in word_freq.most_common(topn)]
return keywords
# 提取所有评论的关键词
all_comments = ' '.join(df['评论'])
all_keywords = extract_keywords(all_comments, topn=20)
print("高频关键词:", ', '.join(all_keywords))
# 定义一个函数来加载停用词
def load_stopwords(filepath):
# 初始化一个空列表来存储停用词
stopwords = []
# 使用with语句打开文件,确保文件正确关闭
with open(filepath, 'r', encoding='utf-8') as file:
# 逐行读取文件
for line in file:
# 去除每行末尾的换行符(如果有的话),并将单词添加到列表中
# strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)
stopwords.append(line.strip())
# 返回加载的停用词列表
return stopwords
# 调用函数,加载停用词
stopwords_path = 'stopwords.txt' # 停用词文件的路径
stopwords_list = load_stopwords(stopwords_path)
# 打印停用词列表的前几个元素,以验证是否正确加载
print(stopwords_list[:10]) # 打印前10个停用词,根据需要调整数字
def extract_keywords(text,STOP_WORDS, topn=10):
"""
从文本中提取高频关键词和短语,去除停用词
参数:
text (str): 输入文本
topn (int): 返回的关键词和短语数量
返回:
list: 关键词和短语列表
"""
words = jieba.cut(text, cut_all=False)
# 过滤停用词
filtered_words = (word for word in words if word not in STOP_WORDS)
word_freq = Counter(filtered_words)
keywords = [k for k, v in word_freq.most_common(topn)]
return keywords
# 假设df是包含评论的DataFrame
# 提取所有评论的关键词
all_comments = ' '.join(df['评论'])
all_keywords = extract_keywords(all_comments,stopwords_list, topn=20)
print("高频关键词:", ', '.join(all_keywords))
# 主题建模
def topic_modeling(df, num_topics=5):
"""
使用LDA主题模型发现评论中的隐藏主题
参数:
df (pd.DataFrame): 包含评论数据的DataFrame
num_topics (int): 主题数量
返回:
gensim.models.LdaMulticore: 训练好的LDA主题模型
"""
# 创建词典和语料库
dictionary = Dictionary(df['评论'].str.split(','))
corpus = [dictionary.doc2bow(text.split()) for text in df['评论']]
# 训练LDA主题模型
lda_model = gensim.models.LdaMulticore(corpus=corpus,
id2word=dictionary,
num_topics=num_topics)
return lda_model
# 训练LDA主题模型
lda_model = topic_modeling(df, num_topics=5)
# 打印主题及其词语分布
for topic_id in range(lda_model.num_topics):
print(f"主题 {topic_id}:")
topic_words = lda_model.get_topic_terms(topic_id, topn=5)
for word_id, prob in topic_words:
print(dictionary[word_id], end=", ")
print()