最近在裸睡的猪上看到一篇关于B站的文章,觉得分析的很透彻,所以小试牛刀,自己也写了一些可视化程序和分析。
数据提供了B站视频排行榜中的各类信息,比如:‘作者’,‘硬币数’,'弹幕数’等共14项信息,很全面。
项目使用python3在Jupyter notebook上开发。开发过程中使用pandas、numpy对数据进行清洗,可视化使用的是pyecharts。
这里是pyecharts中文手册地址,可支持的图形有很多同时提供了不少案例和视图,而且相较于 Matplotlib更加美观。
首先从总体情况进行分析,之后分析综合排名top100的视频类别。
总体情况部分包括:
1. 各分区播放量情况。
2. 各区三连(硬币、收藏、点赞)情况。
3. 弹幕、评论、转发情况。
4. 绘制综合词云图,查看关键词汇。
综合排名top100部分包括:
1. top100类别占比。
2. top100播放量情况。
3. 硬币、收藏、点赞平均人数分布。
4. 各分区平均评论、弹幕、转发量情况。
首先导入pandas、numpy、pyecharts相关包并读取文件
import pandas as pd
import numpy as np
from pyecharts.charts import Pie, Bar, Map, WordCloud,Line,Grid,Scatter,Radar,Page
from pyecharts import options as opts
from pyecharts.globals import SymbolType
from pyecharts.globals import ThemeType
from pyecharts.commons.utils import JsCode
df = pd.read_csv('bilibili.csv')
df.head()
修改列名称为中文,此处修改中文列名仅为便开发,工作中不推荐中文列名。
df.columns=['作者','硬币数','弹幕数','喜欢人数','视频编号',
'点赞数','排名','区类别','评论数','分数',
'转发数', '标签名称','视频名称','播放次数']
查看数据信息、空值、重复值以及数据类型,但由于数据很完整这里不再做过多操作。
df.info()
df.isnull().count()
df.nunique().count()
df.dtypes
对数据进行拆分、聚合,方便之后各项分析,由于“区类别”列中的“全站”是各分类中排名靠前的视频,会出现重复数据,因此对其进行去除。
#剔除全区排名
df_nall=df.loc[df['区类别']!='全站']
df_nall['区类别'].value_counts()
#按分数进行排序asc
df_top100 = df_nall.sort_values(by='分数',ascending=False)[:100]
df_type = df_nall.drop(['作者','视频编号','标签名称','视频名称','排名'],axis=1)
gp_type = df_type.groupby('区类别').sum().astype('int')
type_all = gp_type.index.tolist()
总体数据概览
gp_type
开始使用了柱状图,但显示效果不佳,因此替换成饼图,相较于柱状图与折线图,饼图更能够体现出个体在总体中的占比。
play = [round(i/100000000,2) for i in gp_type['播放次数'].tolist()]
# bar = (Bar()
# .add_xaxis(type_all)
# .add_yaxis("", play)
# .set_global_opts(
# title_opts=opts.TitleOpts(title="各分区播放量情况"),
# yaxis_opts=opts.AxisOpts(name="次/亿"),
# xaxis_opts=opts.AxisOpts(name="分区",axislabel_opts={"rotate":45})
# )
# )
# bar.render_notebook()
pie = (
Pie()
.add(
"",
[list(z) for z in zip(type_all,
play)],
radius=["40%", "75%"],
)
.set_global_opts(
title_opts=opts.TitleOpts(title="各分区播放量情况 单位:亿次"),
legend_opts=opts.LegendOpts(
orient="vertical", pos_top="15%", pos_left="2%"
),
)
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
)
pie.render_notebook()
播放量排名前三的分别是生活类、动画类、鬼畜类。其中动画类和鬼畜类,这两个是B站的特色。
第三、四位是音乐类和科技类。
coin_all = [round(i/1000000,2) for i in gp_type['硬币数'].tolist()]
like_all = [round(i/1000000,2) for i in gp_type['点赞数'].tolist()]
favourite_all = [round(i/1000000,2) for i in gp_type['喜欢人数'].tolist()]
def bar_base() -> Bar:
c = (
Bar()
.add_xaxis(type_all)
.add_yaxis("硬币", coin_all)
.add_yaxis("点赞", like_all)
.add_yaxis("收藏", favourite_all)
.set_global_opts(title_opts=opts.TitleOpts(title="各分区三连情况"),
yaxis_opts=opts.AxisOpts(name="次/百万"),
xaxis_opts=opts.AxisOpts(name="分区",
axislabel_opts={"rotate":45}))
)
return c
bar_base().render_notebook()
虽然生活类投币和点赞数依然是不可撼动的,但是收藏数却排在动画之后,科技类收藏升至第四位。
danmaku_all = [round(i/100000,2) for i in gp_type['弹幕数'].tolist()]
reply_all = [round(i/100000,2) for i in gp_type['评论数'].tolist()]
share_all = [round(i/100000,2) for i in gp_type['转发数'].tolist()]
line = (
Line()
.add_xaxis(type_all)
.add_yaxis("弹幕", danmaku_all,label_opts=opts.LabelOpts(is_show=False))
.add_yaxis("评论", reply_all,label_opts=opts.LabelOpts(is_show=False))
.add_yaxis("转发", share_all,label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(
title_opts=opts.TitleOpts(title="弹幕、评论、转发情况"),
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"),
yaxis_opts=opts.AxisOpts(name="人数 单位:十万"),
xaxis_opts=opts.AxisOpts(name="时间(日)",axislabel_opts={"rotate":45})
)
)
line.render_notebook()
tag_list=','.join(df_nall['标签名称']).split(',')
tags_count=pd.Series(tag_list).value_counts()
wordcloud = (
WordCloud()
.add("",
[list(z) for z in zip(tags_count.index,tags_count)],
word_size_range=[10, 100])
.set_global_opts(title_opts=opts.TitleOpts(title="热门标签"))
)
wordcloud.render_notebook()
搞笑依然是B站热门视频中出现频率最多的标签,鬼畜也依旧在显眼位置,也有不少紧跟时事的词汇。
def pie_rosetype(col) -> Pie:
# v = Faker.choose()
c = (
Pie()
.add(
"",
[list(z) for z in zip(col.index.tolist(),
col.values.tolist())],
radius=["30%", "75%"],
center=["50%", "50%"],
rosetype="radius",
label_opts=opts.LabelOpts(is_show=False),
)
.set_global_opts(title_opts=opts.TitleOpts(title="top100类别占比"))
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
)
return c
pie_rosetype(df_top100['区类别'].value_counts()).render_notebook()
gp_play = df_top100.groupby('区类别')['播放次数'].sum()
gp_play.index.tolist()
gp_play.values.tolist()
c = (
Bar()
.add_xaxis(gp_play.index.tolist())
.add_yaxis("播放量", gp_play.values.tolist(), category_gap="60%")
.set_series_opts(itemstyle_opts={
"normal": {
"color": JsCode("""new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(0, 244, 255, 1)'
}, {
offset: 1,
color: 'rgba(0, 77, 167, 1)'
}], false)"""),
"barBorderRadius": [30, 30, 30, 30],
"shadowColor": 'rgb(0, 160, 221)',
}})
.set_global_opts(title_opts=opts.TitleOpts(title="top100播放量情况"))
)
c.render_notebook()
值得注意的是这段代码中有JS渲染的部分,需要导入
from pyecharts.commons.utils import JsCode,图形颜色可以在color处调,我这里就直接引用官方默认颜色了。
top100播放量与各类占比相呼应。
gp_triple_quality = df_top100.groupby('区类别')[['硬币数','喜欢人数','点赞数',]].mean().astype('int')
gp_index = gp_triple_quality.index.tolist()
gp_coin = gp_triple_quality['硬币数'].values.tolist()
gp_favorite = gp_triple_quality['喜欢人数'].values.tolist()
gp_like = gp_triple_quality['点赞数'].values.tolist()
max_num = max(gp_triple_quality.values.reshape(-1))
def radar_base() -> Radar:
c = (
Radar()
.add_schema(
schema=[
opts.RadarIndicatorItem(name=gp_index[0], max_=600000),
opts.RadarIndicatorItem(name=gp_index[1], max_=600000),
opts.RadarIndicatorItem(name=gp_index[2], max_=600000),
opts.RadarIndicatorItem(name=gp_index[3], max_=600000),
opts.RadarIndicatorItem(name=gp_index[4], max_=600000),
opts.RadarIndicatorItem(name=gp_index[5], max_=600000),
opts.RadarIndicatorItem(name=gp_index[6], max_=600000),
opts.RadarIndicatorItem(name=gp_index[7], max_=600000),
opts.RadarIndicatorItem(name=gp_index[8], max_=600000),]
)
.add("硬币数", [gp_coin],color='#40e0d0')
.add("喜欢人数", [gp_favorite],color='#1e90ff')
.add("点赞数", [gp_like],color='#b8860b')
.set_series_opts(label_opts=opts.LabelOpts(is_show=False),
linestyle_opts=opts.LineStyleOpts(width=3,type_='dotted'),)
.set_global_opts(title_opts=opts.TitleOpts(title="硬币、收藏、点赞平均人数分布"))
)
return c
radar_base().render_notebook()
生活区的平均投币和点赞量依然高于动画区。投币、点赞、收藏最高的分区分别是:生活、影视、时尚。除了时尚区外,其他分区的收藏量均低于投币和点赞,且时尚区的收藏量是远高其点赞和投币量。
gp_keydata = df_top100.groupby('区类别')[['评论数','弹幕数','转发数',]].mean().astype('int')
gp_index = gp_keydata.index.tolist()
reply = [i/10000 for i in gp_keydata['评论数'].tolist()]
danmaku = [i/10000 for i in gp_keydata['弹幕数'].tolist()]
share = [i/10000 for i in gp_keydata['转发数'].tolist()]
grid = Grid()
bar = Bar()
grid.theme = ThemeType.PURPLE_PASSION
line = Line()
bar.add_xaxis(gp_index)
bar.add_yaxis("评论数",reply,label_opts=opts.LabelOpts(is_show=True))
# bar.add_yaxis("弹幕数",danmaku,label_opts=opts.LabelOpts(is_show=False))
bar.extend_axis(yaxis=opts.AxisOpts(type_="value",
name="转发与弹幕量 单位:万",
min_=0,
max_=15,
position="right",
axislabel_opts=opts.LabelOpts(formatter="{value}万人次"),
))
bar.set_global_opts(yaxis_opts=opts.AxisOpts(
name="评论量 单位:万",
type_="value",
min_=0,
max_=15,
axislabel_opts=opts.LabelOpts(formatter="{value}万人次")
),
title_opts=opts.TitleOpts("各分区评论、弹幕、转发量情况"),
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"),
) # 交叉指向工具
line.add_xaxis(gp_index)
line.add_yaxis("转发数",share,yaxis_index = 1,label_opts=opts.LabelOpts(is_show=False))
line.add_yaxis("弹幕数",danmaku,yaxis_index = 1,label_opts=opts.LabelOpts(is_show=False))
# 把line添加到bar上
bar.overlap(line)
# 这里如果不需要grid也可以,直接设置bar的格式,然后显示bar即可
# bar.render_notebook()
grid.add(chart = bar,grid_opts = opts.GridOpts(),is_control_axis_index = True)
grid.render_notebook()
B站从番剧,cosplay,漫画,动画,舞蹈,音乐,鬼畜再到各种纪录片,学习资料,搞笑,电视剧,时尚,直播等等,覆盖了现在年轻人娱乐的各个领域,你可以在B站上找到任何你想要的东西,而且如今B站不光光是年轻人的娱乐场所,还是一个重要的年轻人学习的网站,接近2000万人在B站学习,成为国内最大的自学平台之一 。
如今二次元的业务已经不是B站的主营业务了,毕竟B站是需要赚钱存活的,需要进行转型,游戏、直播、广告这些已经成为了B站的主营业务,流量爆炸的时代,最令人头痛的便是如何变现,拥有庞大流量的B站显然是发现了其中的门路。