弹幕[dàn mù] (danmaku),中文流行词语,指的是在网络上观看视频时弹出的评论性字幕。源自日本弹幕视频分享网站(niconico动画),国内首先引进为Acfun以及后来的哔哩哔哩网站。
大量吐槽评论从屏幕飘过时效果看上去像是飞行射击游戏里的弹幕,所以NICO网民将这种有大量的吐槽评论出现时的效果做弹幕。
在中国,本来只有大量评论同时出现才能叫弹幕,但是随着误用单条评论也能叫弹幕了。
想法:弹幕量与视频中的高曝光片段出现时间段是否有关?弹幕量与自然时间之间是否有一定关联性?
数据:从哔哩哔哩网站上爬下来的弹幕数据
分析过程:爬下来的Excel数据文件,里面是啥样的?数据是否完整?如何通过视频时间和自然时间分别统计弹幕量?如何以可视化的形式表达?
上述就是整个分析流程,下面开始一步步进行
任务一:对于 '热点事件.xlsx' 文件的Sheet3表单而言,需要统计视频时间中每秒内弹幕的数量并可视化展示
import pandas as pd
import matplotlib.pyplot as plt
①读入数据
fpath = './datas/热点事件.xlsx'
df = pd.read_excel(fpath,sheet_name='Sheet3')
这里,我已知分析所用数据在Sheet3表单中,所以直接调用了。
其实在不知情的情况下,应该先对Excel文件进行“结构性”读取(自己理解的)。
如何“结构性”读取,在后面会用到。
②查看数据的“样子”
df.head()
一看,有用的就前两列,第一列是弹幕发送的视频时间,第二列是数量。
df.shape # 结果为(1048575, 6)
好大的数据量啊,有问题!EXCEL表格文件最多就只有1048575行。
df.dropna(axis="index", how='all', inplace=True)
先删除全部为空值的行试试。
df.shape # 结果为(501, 6)
这里一看,好家伙,全是空值.....还好进行了空值处理。最终需要处理的总共501行数据。
其实这里回头想,少了一步,我们看了开头的五行数据,其实也可以看看结尾的五行数据是啥样子的。
后面重新做的时候我发现,在原始表格第1048575行的数据是统计了有多少行数据= =|||,这里应该是人为操作失误。
③按照视频时间的每秒进行弹幕量统计
df.loc[(0 <= df['时间']) & (df['时间'] < 1),'数量'].sum() # 运行结果为8.0
由于"时间"列中均是精确到秒后三位小数,这里利用df.loc进行区域限定,然后借助"数量"列进行求和。
这样就统计出了0至1秒,也就是1秒内的弹幕量,如法炮制,求出每秒的弹幕量。
# 基于上述方法,写一个for循环,解决战斗
for i in range(501):
count = df.loc[(i <= df['时间']) & (df['时间'] < i+1),'数量'].sum()
print('第{}秒内共有{}条弹幕'.format(i, count))
运行结果如下图所示。
通过上述代码运行结果会发现,在109秒后出现的都是0条弹幕。
为啥是这样?因为虽然是501条,但是要计算每1秒内的弹幕量,这样累加之后,总行数就远远低于501行了。
这样看来,欠考虑。应该基于最后一条弹幕的秒数,给range()里面的参数赋值。
# 按照前面所想,可以看出以秒为单位的时间段最终为109.420秒
# 因此,给range()赋值110即可,为啥不是109?因为range()里面是左闭右开区间,取110意味着[0,110)
for i in range(110):
count = df.loc[(i <= df['时间']) & (df['时间'] < i+1),'数量'].sum()
print('第{}秒内共有{}条弹幕'.format(i, count))
为啥做上述的优化?
其实是我做到可视化部分发现的问题,回过头来在这里优化了。
④可视化展示
# 最后用柱状图可视化展示
# 这里要注意plt.bar()函数默认颜色循环使用,如果这里不指定一个颜色,柱状图会变得五颜六色
for i in range(110):
count = df.loc[(i <= df['时间']) & (df['时间'] < i+1),'数量'].sum()
#print('第{}秒内共有{}条弹幕'.format(i, count))
plt.bar(i, count, color='#008B8B')
plt.show() # 这里相当于在一块画布上每次画一条柱状图,直到for循环结束,输出结果
看看每5秒统计一次的结果。
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 最后,以5秒为一个间隔,看看弹幕的分布情况(有时候精细未必是好事,扩大间隔有助于分析问题)
for i in range(0, 110, 5):
count = df.loc[(i <= df['时间']) & (df['时间'] < i+5),'数量'].sum()
plt.bar(i, count, width=1, color='#008B8B') # 给定个宽度,出图看的紧凑些
plt.xlabel('弹幕时间(秒)', fontsize=12)
plt.ylabel('弹幕量(条)', fontsize=12)
plt.savefig('fix11.svg', dpi=500) # 保存
plt.show() # 这里相当于在一块画布上每次画一条柱状图,直到for循环结束,输出结果
从上图上就能很明显看出:
在视频时间10至25秒、65至75秒、90至95秒以及最后时刻,弹幕量较为集中。
再次观看视频发现,这些弹幕集中的某些视频时间段刚好对应视频中的高潮片段,符合观众观看行为。
任务二:对于 '热点事件.xlsx' 文件的原数据表单而言,需要统计以自然时间每天的弹幕数量并可视化展示
这里,假设我们不清楚拿到的Excel文件内部啥样子,你可以这样打开观察。
先看看文件中有多少个sheet(表单)。
但是按照以往读取Excel文件,你会发现,只能读取到第一个表单的数据(默认的)。
import pandas as pd
import matplotlib.pyplot as plt
file_path = './datas/热点事件.xlsx'
xx = pd.read_excel(file_path)
有同学说,你可以加上表单名称参数啊。但是刚说过了,我们假设拿到的数据,你完全不知道里面啥样。怎么办?
这时候可以用另一种Excel文件读取方式:pd.ExcelFile( )
data = pd.ExcelFile(file_path)
data.sheet_names
这样就可以将读取Excel数据的所有表单名称打印出来了,如下图。
假设我需要的数据在表单名为‘原数据’中,则通过如下代码:
df = data.parse('Sheet5')
这样就回到了我们之前的DataFrame的操作流程上来了。
如上图所示,又出现了新问题。读取时,原文件开始有空行,所以造成读取后,原本的列标签变成了数据。这里只需要设定指定一行为索引即可,代码如下:
df = data.parse('Sheet5', header=1)
完美解决,开始数据分析。
①首先将第一列(日期)作为标签列
df = df.set_index('行标签') # 将“行标签”设置为index
这时又发现,其中columns的值为‘以下项目的计数:数量’,太绕口,写起来太麻烦。
利用rename( )修改列名,以字典的形式传入值,进行修改,代码如下:
df_clearn = df.rename(columns={'以下项目的计数:数量':'counts'})
②以天为单位进行统计
df_clearn.resample('d').sum()['counts']
如果只统计2018年全年的,可以使用如下代码:
df_day = df_clearn.resample('d').sum()['counts']
df_2018 = df_day['2018']
③可视化展示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(10,4))
plt.plot(df_2018.index, df_2018, color='#008B8B')
plt.xlabel('自然时间(天)',fontsize=12)
plt.ylabel('弹幕量(条)',fontsize=12)
plt.savefig('picture.svg', dpi=500)
plt.show()
从上图上就能很明显看出:
随着自然时间的推移,热点事件的评论热度会慢慢降低直至“冷却”,符合常理。
今天的分享到这里就结束了,我最近收获最大的就是拿到一个简单的实际问题,通过自己所学,查漏补缺,这样进步会很快,我还是不建议类似于A到Z的背单词方法,其实背作文何尝不是一种背单词的方法呢?
哈哈,隐喻,自行理解~
END