B站评论数据清洗与可视化:从原始数据到洞察呈现的完整指南
当我们需要分析B站视频的观众反馈时,评论数据是最直接的信息来源。但原始评论数据往往杂乱无章,包含大量需要清洗和转换的信息。本文将带你从零开始,构建一个完整的B站评论数据处理流程,涵盖数据清洗、质量检查到可视化呈现的全过程。
1. 数据获取与初步探索
在开始清洗之前,我们需要先获取原始数据。B站评论通常包含以下关键字段:
- 用户名(uname)
- 性别(sex)
- IP属地(ip)
- 评论时间(time)
- 点赞数(like)
- 评论内容(content)
假设我们已经通过API获取了原始数据并保存为CSV文件,让我们先加载数据并查看其结构:
import pandas as pd # 加载原始数据 df = pd.read_csv('bilibili_comments_raw.csv', names=['uname', 'sex', 'sign', 'ip', 'time', 'like', 'content'], encoding='utf-8') # 查看前5行数据 print(df.head())常见的数据质量问题包括:
- 性别字段可能包含"男"、"女"、"保密"或空值
- IP属地字段可能包含"IP属地:"前缀
- 点赞数可能为字符串类型而非数值
- 时间戳可能需要转换
2. 数据清洗与预处理
2.1 处理缺失值与异常值
首先处理缺失值和异常值:
# 填充缺失性别为"保密" df['sex'] = df['sex'].fillna('保密') # 处理IP属地字段 df['ip'] = df['ip'].str.replace('IP属地:', '') # 转换点赞数为数值类型 df['like'] = pd.to_numeric(df['like'], errors='coerce').fillna(0) # 转换时间戳为datetime格式 df['time'] = pd.to_datetime(df['time'], format='%Y-%m-%d %H:%M:%S')2.2 数据标准化
为了后续分析,我们需要对数据进行标准化处理:
# 性别标准化 gender_mapping = {'male': '男', 'female': '女', 'unknown': '保密'} df['sex'] = df['sex'].map(gender_mapping).fillna('保密') # IP属地标准化(提取省份) df['province'] = df['ip'].str.extract(r'([\u4e00-\u9fa5]+)')[0]2.3 数据质量检查清单
在继续分析前,建议执行以下质量检查:
完整性检查:
- 是否有缺失的关键字段?
- 是否有明显错误的数值?
一致性检查:
- 性别字段是否只有三种有效值?
- IP属地格式是否统一?
有效性检查:
- 点赞数是否为非负数?
- 时间戳是否在合理范围内?
可以通过以下代码快速检查:
# 检查性别分布 print(df['sex'].value_counts()) # 检查点赞数分布 print(df['like'].describe()) # 检查时间范围 print(f"最早评论: {df['time'].min()}") print(f"最新评论: {df['time'].max()}")3. 数据聚合与分析
3.1 按省份和性别聚合点赞数
为了分析不同地区、不同性别用户的互动情况,我们可以进行如下聚合:
# 按省份和性别聚合点赞数 agg_df = df.groupby(['province', 'sex'])['like'].sum().reset_index() # 查看聚合结果 print(agg_df.head())3.2 处理不完整的数据
某些省份可能只有部分性别的数据,为了可视化需要,我们需要补全所有组合:
# 获取所有省份和性别的组合 from itertools import product all_provinces = df['province'].unique() all_genders = ['男', '女', '保密'] all_combinations = pd.DataFrame(list(product(all_provinces, all_genders)), columns=['province', 'sex']) # 合并并填充缺失值为0 agg_df = all_combinations.merge(agg_df, on=['province', 'sex'], how='left').fillna(0)4. 数据可视化
4.1 使用PyECharts创建堆叠条形图
堆叠条形图适合展示不同省份下各性别的点赞分布:
from pyecharts.charts import Bar from pyecharts import options as opts # 准备数据 provinces = agg_df['province'].unique().tolist() male_data = agg_df[agg_df['sex'] == '男']['like'].tolist() female_data = agg_df[agg_df['sex'] == '女']['like'].tolist() unknown_data = agg_df[agg_df['sex'] == '保密']['like'].tolist() # 创建图表 bar = Bar() bar.add_xaxis(provinces) bar.add_yaxis("男性", male_data, stack="stack1") bar.add_yaxis("女性", female_data, stack="stack1") bar.add_yaxis("保密", unknown_data, stack="stack1") # 设置全局选项 bar.set_global_opts( title_opts=opts.TitleOpts(title="各省份各性别点赞数分布"), xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45)), datazoom_opts=[opts.DataZoomOpts()] ) # 渲染图表 bar.render("stacked_bar_chart.html")4.2 创建3D条形图展示多维数据
3D条形图可以同时展示省份、性别和点赞数三个维度的信息:
from pyecharts.charts import Bar3D # 准备3D数据 data = [] for idx, row in agg_df.iterrows(): data.append([row['province'], row['sex'], row['like']]) # 创建3D图表 bar3d = Bar3D() bar3d.add( "", data, xaxis3d_opts=opts.Axis3DOpts(provinces, type_="category"), yaxis3d_opts=opts.Axis3DOpts(['男', '女', '保密'], type_="category"), zaxis3d_opts=opts.Axis3DOpts(type_="value"), ) bar3d.set_global_opts( visualmap_opts=opts.VisualMapOpts(max_=agg_df['like'].max()), title_opts=opts.TitleOpts(title="各省份性别点赞数3D分布"), ) # 渲染图表 bar3d.render("3d_bar_chart.html")5. 高级分析与洞察挖掘
5.1 时间趋势分析
分析评论和点赞的时间分布可以帮助我们了解用户的活跃时段:
# 按小时聚合 df['hour'] = df['time'].dt.hour hourly_stats = df.groupby('hour').agg({ 'like': 'sum', 'uname': 'count' }).rename(columns={'uname': 'comment_count'}) # 绘制时间趋势图 from pyecharts.charts import Line line = Line() line.add_xaxis(hourly_stats.index.tolist()) line.add_yaxis("评论数", hourly_stats['comment_count'].tolist()) line.add_yaxis("点赞数", hourly_stats['like'].tolist()) line.set_global_opts(title_opts=opts.TitleOpts(title="评论与点赞时间分布")) line.render("hourly_trend.html")5.2 热门关键词提取
通过分析评论内容,我们可以提取热门关键词:
import jieba from collections import Counter # 提取评论内容并分词 all_comments = ' '.join(df['content'].dropna().tolist()) words = [word for word in jieba.cut(all_comments) if len(word) > 1 and word not in ['哈哈', '啊啊']] word_counts = Counter(words).most_common(20) # 创建词云 from pyecharts.charts import WordCloud wordcloud = WordCloud() wordcloud.add("", word_counts, word_size_range=[20, 100]) wordcloud.set_global_opts(title_opts=opts.TitleOpts(title="评论关键词词云")) wordcloud.render("wordcloud.html")6. 构建可复用的数据处理管道
为了便于重复使用,我们可以将整个流程封装为函数和类:
class BiliCommentAnalyzer: def __init__(self, filepath): self.df = self.load_data(filepath) self.clean_data() def load_data(self, filepath): return pd.read_csv(filepath, names=['uname', 'sex', 'sign', 'ip', 'time', 'like', 'content'], encoding='utf-8') def clean_data(self): # 数据清洗逻辑 pass def analyze_gender_distribution(self): # 性别分析逻辑 pass def visualize(self, chart_type='bar'): # 可视化逻辑 pass # 使用示例 analyzer = BiliCommentAnalyzer('bilibili_comments_raw.csv') analyzer.visualize(chart_type='3d')通过这样的结构化设计,我们可以轻松地对不同视频的评论数据进行分析比较。