我用Python把《白蛇2青蛇劫起》的评论做了数据可视化分析
大家好,我是辣条。
终于要开始数据分析系列的文章了,相比爬虫来说技术维度还是上升一个层次的。我的文章输出呢会更新实战项目系列,和知识点详解总结系列,分别在两个专栏,目标是短期实现爬虫和数据分析实战项目100个案例。
涉及到的库:
Pandas — 数据处理 Pyecharts — 数据可视化 jieba — 分词 collections — 数据统计
可视化部分:
折线图 — Line 柱状图 — Bar 饼状图 — Pie 日历图 — Calendar 词云图 — WordCloud 地图 — Geo
白蛇2:青蛇劫起
*剧情简介:*
2021年7月23日, 白蛇2:青蛇劫起在大陆上映,主要讲述南宋末年,小白为救许仙水漫金山,终被法海压在雷峰塔下。小青则意外被法海打入诡异的修罗城幻境。几次危机中小青被神秘蒙面少年所救,小青带着出去救出小白的执念历经劫难与成长,同蒙面少年一起寻找离开办法的故事
执行环节 Notebook
安装第三方包
!pip install pyecharts
!pip install pandas
!pip install numpy
导入第三方包
import pandas as pd
import numpy as np
from pyecharts.charts import *
from PIL import Image
from collections import Counter
from pyecharts import options as opts # 可视化配置项
from pyecharts.commons.utils import JsCode # 用来跑js代码的
from pyecharts.globals import ThemeType,SymbolType,ChartType # 可视化主题样式
读取数据
df = pd.read_excel("./白蛇2.xlsx")
df.head(10) # 查看前 10行
id | 用户名 | 城市 | 评分 | 评论 | 评论时间 | |
---|---|---|---|---|---|---|
0 | 1142669584 | 淇桐糯米饭啦 | 来宾 | 5.0 | 剧情非常有吸引力,看个动画片给了我惊喜 | 2021-08-31 23:56:30 |
1 | 1142662178 | LnV14610189 | 西宁 | 5.0 | 画面感超强! | 2021-08-31 23:36:00 |
2 | 1142666877 | Alo861902585 | 广州 | 5.0 | 和一衔接的很不错,精彩 | 2021-08-31 23:34:41 |
3 | 1142660216 | Y。 | 西安 | 4.0 | 画面人物没的说,你永远可以相信追光。剧情还算顺畅整体节奏也还可以,推荐观看——! | 2021-08-31 23:30:56 |
4 | 1142669423 | 想为你揽月 | 凤台 | 5.0 | 不错哦,虽然小青小白的执念有一丢丢丢丢丢牵强(还好)。如果能细说一下牛魔的执念剧情会更完美,... | 2021-08-31 23:27:28 |
5 | 1142669422 | 啊卡哇啊呀啊呀 | 日照 | 5.0 | 不错不错,感觉越来越有看头了 | 2021-08-31 23:27:12 |
6 | 1142669404 | Lfz9696 | 永州 | 4.5 | 还可以,很不错 | 2021-08-31 23:23:30 |
7 | 1142666812 | Tenacity | 广州 | 4.0 | 电影还行,就是隔壁有个男的一直抖腿。 | 2021-08-31 23:22:23 |
8 | 1142661206 | CQE579669148 | 乌鲁木齐 | 5.0 | 好好看,推荐 | 2021-08-31 23:16:22 |
9 | 1142668420 | 胖胖的唢呐 | 伊犁 | 5.0 | 剧情有点没看懂,但是动画特效非常棒!情节挺感人的。 | 2021-08-31 23:06:36 |
数据清洗
缺失值查看
df.isnull().sum()
id 0
用户名 1
城市 0
评分 0
评论 0
评论时间 0
dtype: int64
查看发现存在一个确实情况
用户名存在一条缺失,其他各列数据完整,用“未知”填充空值:
df['用户名'].fillna('未知', inplace=True)
df.isnull().sum()
Pyecharts数据可视化
评分等级分布
# 线性渐变
color_js = """new echarts.graphic.LinearGradient(0, 0, 1, 0,
[{offset: 0, color: '#009ad6'}, {offset: 1, color: '#ed1941'}], false)"""
df_star = df.groupby('评分')['评论'].count()
df_star = df_star.sort_values(ascending=True)
x_data = [str(i) for i in list(df_star.index)]
y_data = df_star.values.tolist()
b1 = (
Bar()
.add_xaxis(x_data)
.add_yaxis('',y_data,itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_js)))
.reversal_axis()
.set_series_opts(label_opts=opts.LabelOpts(position='right'))
.set_global_opts(
yaxis_opts=opts.AxisOpts(name='评分等级'),
xaxis_opts=opts.AxisOpts(name='人/次'),
title_opts=opts.TitleOpts(title='评分等级分布',pos_left='45%',pos_top="5%"),
legend_opts=opts.LegendOpts(type_="scroll", pos_left="85%",pos_top="28%",orient="vertical")
)
)
df_star = df.groupby('评分')['评论'].count()
x_data = [str(i) for i in list(df_star.index)]
y_data = df_star.values.tolist()
p1 = (
Pie(init_opts=opts.InitOpts(width='800px', height='600px'))
.add(
'',
[list(z) for z in zip(x_data, y_data)],
radius=['10%', '30%'],
center=['65%', '60%'],
label_opts=opts.LabelOpts(is_show=True),
)
.set_colors(["blue", "green", "#800000", "red", "#000000", "orange", "purple", "red", "#000000", "orange", "purple"])
.set_series_opts(label_opts=opts.LabelOpts(formatter='评分{b}: {c} \n ({d}%)'),position="outside")
)
b1.overlap(p1)
b1.render_notebook()
5.0的评分占比达到了56%,超过了半数观众打出了五星好评,四星以上好评更是达到了85%之多,看来大家对这部动漫还是高度认可的。
2021.08.01-2021.08.31 每天评论量分布:
# 设置样式
# 加载的js代码 做样式 主要是 颜色 和主题
color_js = """new echarts.graphic.LinearGradient(0, 1, 0, 0,
[{offset: 0, color: '#009ad6'}, {offset: 1, color: '#ed1941'}], false)"""
area_color_js = (
"new echarts.graphic.LinearGradient(0, 0, 0, 1, "
"[{offset: 0, color: '#eb64fb'}, {offset: 1, color: '#3fbbff0d'}], false)"
)
# 设置参数
linestyle_dic = { 'normal': {
'width': 2,
'shadowColor': '#696969',
'shadowBlur': 10,
'shadowOffsetY': 10,
'shadowOffsetX': 10,
}
}
# 转时间格式
df['评论时间'] = pd.to_datetime(df['评论时间'], format='%Y/%m/%d %H:%M:%S')
# 每日评论量
df['评论时间'] = pd.to_datetime(df['评论时间'], format='%Y/%m/%d %H:%M:%S')
df_day = df.groupby(df['评论时间'].dt.day)['评论'].count() # 根据评论时间 获取评论的数量(count)
day_x_data = [str(i) for i in list(df_day.index)] # x轴
day_y_data = df_day.values.tolist() # 输出成列表 y轴
line1 = (
Line(init_opts=opts.InitOpts(bg_color=JsCode(color_js))) # 线性可视化
.add_xaxis(xaxis_data=day_x_data) # 添加x轴数据
.add_yaxis( # 添加y轴数据
series_name="", # y轴名称
y_axis=day_y_data, # 数据
is_smooth=True,
is_symbol_show=True,
symbol="circle",
symbol_size=6,
linestyle_opts=opts.LineStyleOpts(color="#fff"), # 配置y轴线
label_opts=opts.LabelOpts(is_show=True, position="top", color="white"), # y轴标签
itemstyle_opts=opts.ItemStyleOpts(
color="red", border_color="#fff", border_width=3
),
tooltip_opts=opts.TooltipOpts(is_show=False),
areastyle_opts=opts.AreaStyleOpts(color=JsCode(area_color_js), opacity=1),
)
.set_global_opts(
title_opts=opts.TitleOpts(
title="八月每日评论量",
pos_top="5%",
pos_left="center",
title_textstyle_opts=opts.TextStyleOpts(color="#fff", font_size=16),
),
xaxis_opts=opts.AxisOpts(
type_="category",
boundary_gap=True,
axislabel_opts=opts.LabelOpts(margin=30, color="#ffffff63"),
axisline_opts=opts.AxisLineOpts(is_show=False),
axistick_opts=opts.AxisTickOpts(
is_show=True,
length=25,
linestyle_opts=opts.LineStyleOpts(color="#ffffff1f"),
),
splitline_opts=opts.SplitLineOpts(
is_show=True, linestyle_opts=opts.LineStyleOpts(color="#ffffff1f")
),
),
yaxis_opts=opts.AxisOpts(
type_="value",
position="left",
axislabel_opts=opts.LabelOpts(margin=20, color="#ffffff63"),
axisline_opts=opts.AxisLineOpts(
linestyle_opts=opts.LineStyleOpts(width=2, color="#fff")
),
axistick_opts=opts.AxisTickOpts(
is_show=True,
length=15,
linestyle_opts=opts.LineStyleOpts(color="#ffffff1f"),
),
splitline_opts=opts.SplitLineOpts(
is_show=True, linestyle_opts=opts.LineStyleOpts(color="#ffffff1f")
),
),
legend_opts=opts.LegendOpts(is_show=False),
)
)
line1.render_notebook()
每天评论量在8月1日达到峰值(数据不包含7月份),随着时间的推移评论数量逐渐减少,这也符合一般电影观影规律。
每小时评论量
统计的是2021.08.01-2021.08.31这31天每小时天评论量总和(如果感兴趣可以单独查看某一天24小时影评数量分布,按日期筛选即可)
df_hour = df.groupby(df['评论时间'].dt.hour)['评论'].count()
hours_x_data = [str(i) for i in list(df_hour.index)]
hours_y_data = df_hour.values.tolist()
line1 = (
# Line(init_opts=opts.InitOpts(bg_color=JsCode(color_js)))
Line(init_opts=opts.InitOpts(width='1000px', height='400px'))
.add_xaxis(xaxis_data=hours_x_data)
.add_yaxis(
series_name="",
y_axis=hours_y_data,
is_smooth=True,
is_symbol_show=True,
symbol="circle",
symbol_size=6,
linestyle_opts=opts.LineStyleOpts(color="#fff"),
label_opts=opts.LabelOpts(is_show=True, position="top", color="white"),
itemstyle_opts=opts.ItemStyleOpts(
color="red", border_color="#fff", border_width=3
),
tooltip_opts=opts.TooltipOpts(is_show=False),
areastyle_opts=opts.AreaStyleOpts(color=JsCode(area_color_js), opacity=1),
)
.set_series_opts(
linestyle_opts=linestyle_dic,label_opts=opts.LabelOpts(font_size=12, color='red' ),
markpoint_opts=opts.MarkPointOpts(
data=[opts.MarkPointItem(type_="max",itemstyle_opts=opts.ItemStyleOpts(
color="#06FFD7", border_width=3)),
opts.MarkPointItem(type_="min",itemstyle_opts=opts.ItemStyleOpts(
color="#06FFD7", border_width=3))],
symbol_size=[65, 50],
label_opts=opts.LabelOpts(position="inside", color="red", font_size=10)
),
)
.set_global_opts(
title_opts=opts.TitleOpts(
title="每小时评论量",
pos_top="5%",
pos_left="center",
title_textstyle_opts=opts.TextStyleOpts(color="#EB1934", font_family='STKaiti', font_size=20),
),
xaxis_opts=opts.AxisOpts(
type_="category",
boundary_gap=True,
axislabel_opts=opts.LabelOpts(margin=30, color="#EB1934"),
axisline_opts=opts.AxisLineOpts(
is_show=False,
linestyle_opts=opts.LineStyleOpts(color="#EB1934")
),
axistick_opts=opts.AxisTickOpts(
is_show=False,
length=25,
linestyle_opts=opts.LineStyleOpts(color="#EB1934"),
),
),
yaxis_opts=opts.AxisOpts(
type_="value",
position="left",
axislabel_opts=opts.LabelOpts(is_show=False, margin=20, color="#EB1934"),
axisline_opts=opts.AxisLineOpts(
is_show=False,
linestyle_opts=opts.LineStyleOpts(width=2, color="#EB1934")
),
axistick_opts=opts.AxisTickOpts(
is_show=False,
length=10,
linestyle_opts=opts.LineStyleOpts(color="#EB1934"),
),
splitline_opts=opts.SplitLineOpts(
is_show=False, linestyle_opts=opts.LineStyleOpts(color="#EB1934")
),
),
legend_opts=opts.LegendOpts(is_show=False),
graphic_opts=[
opts.GraphicImage(
graphic_item=opts.GraphicItem(
id_="logo", z=-10, bounding="raw", origin=[50, 100]
),
graphic_imagestyle_opts=opts.GraphicImageStyleOpts(
image="./12.jpg",
width=1000,
height=400,
opacity=0.3,
),
)
],
)
)
# line1.render_notebook()
# 背景图在本地可以显示,但是平台只显示折线图无背景,可以把代码拷贝到本地运行
Image.open("./2.png")
从小时分布来看,大家一般选择在下午到晚上评论的比较多,尤其是在17:00以后,大家在工作时段还都是比较敬业的。第二次评论峰值在22:00,这个时间段是熬夜青年比较活跃的时段,小伙伴们的作息时间都比较靠后。
3.4 一周各天评论量
统计的是2021.08.01-2021.08.31这31天每周各天评论量的总和:
# 增加字段'星期'
dic = {1:'星期一',2:'星期二',3:'星期三',4:'星期四',5:'星期五',6:'星期六',7:'星期日'}
df['星期'] = df['评论时间'].dt.dayofweek+1
df['星期'] = df['星期'].map(dic)
df.head(5
)
id | 用户名 | 城市 | 评分 | 评论 | 评论时间 | 星期 | |
---|---|---|---|---|---|---|---|
0 | 1142669584 | 淇桐糯米饭啦 | 来宾 | 5.0 | 剧情非常有吸引力,看个动画片给了我惊喜 | 2021-08-31 23:56:30 | 星期二 |
1 | 1142662178 | LnV14610189 | 西宁 | 5.0 | 画面感超强! | 2021-08-31 23:36:00 | 星期二 |
2 | 1142666877 | Alo861902585 | 广州 | 5.0 | 和一衔接的很不错,精彩 | 2021-08-31 23:34:41 | 星期二 |
3 | 1142660216 | Y。 | 西安 | 4.0 | 画面人物没的说,你永远可以相信追光。剧情还算顺畅整体节奏也还可以,推荐观看——! | 2021-08-31 23:30:56 | 星期二 |
4 | 1142669423 | 想为你揽月 | 凤台 | 5.0 | 不错哦,虽然小青小白的执念有一丢丢丢丢丢牵强(还好)。如果能细说一下牛魔的执念剧情会更完美,... | 2021-08-31 23:27:28 | 星期二 |
# 一周各天评论量
dic = {1:'星期一',2:'星期二',3:'星期三',4:'星期四',5:'星期五',6:'星期六',7:'星期日'}
df['星期'] = df['评论时间'].dt.dayofweek+1
df1 = df.sort_values('星期',ascending=True)
df_week = df1.groupby(['星期'])['评论'].count()
week_x_data = [dic[i] for i in list(df_week.index)]
week_y_data = df_week.values.tolist()
line1 = (
Line(init_opts=opts.InitOpts(bg_color=JsCode(color_js)))
.add_xaxis(xaxis_data=week_x_data)
.add_yaxis(
series_name="",
y_axis=week_y_data,
is_smooth=True,
is_symbol_show=True,
symbol="circle",
symbol_size=6,
linestyle_opts=opts.LineStyleOpts(color="#fff"),
label_opts=opts.LabelOpts(is_show=True, position="top", color="white"),
itemstyle_opts=opts.ItemStyleOpts(
color="red", border_color="#fff", border_width=3
),
tooltip_opts=opts.TooltipOpts(is_show=False),
areastyle_opts=opts.AreaStyleOpts(color=JsCode(area_color_js), opacity=1),
)
.set_global_opts(
title_opts=opts.TitleOpts(
title="一周各天评论量",
pos_top="5%",
pos_left="center",
title_textstyle_opts=opts.TextStyleOpts(color="#fff", font_size=16),
),
xaxis_opts=opts.AxisOpts(
type_="category",
boundary_gap=True,
axislabel_opts=opts.LabelOpts(margin=30, color="#ffffff63"),
axisline_opts=opts.AxisLineOpts(is_show=False),
axistick_opts=opts.AxisTickOpts(
is_show=True,
length=25,
linestyle_opts=opts.LineStyleOpts(color="#ffffff1f"),
),
splitline_opts=opts.SplitLineOpts(
is_show=True, linestyle_opts=opts.LineStyleOpts(color="#ffffff1f")
),
),
yaxis_opts=opts.AxisOpts(
type_="value",
position="left",
axislabel_opts=opts.LabelOpts(margin=20, color="#ffffff63"),
axisline_opts=opts.AxisLineOpts(
linestyle_opts=opts.LineStyleOpts(width=2, color="#fff")
),
axistick_opts=opts.AxisTickOpts(
is_show=True,
length=15,
linestyle_opts=opts.LineStyleOpts(color="#ffffff1f"),
),
splitline_opts=opts.SplitLineOpts(
is_show=True, linestyle_opts=opts.LineStyleOpts(color="#ffffff1f")
),
),
legend_opts=opts.LegendOpts(is_show=False),
)
)
line1.render_notebook()
从一周各天数据分布来看,每周一和每周天是大家评论的活跃时段,很有意思,一周的开始和一周的结束,在休中开始,在休闲中结束。
3.5 日历图
times = [x.strftime('%Y-%m-%d') for x in list(pd.date_range('20210801', '20210831'))]
data = [[times[index],day_y_data[index]] for index,item in enumerate( day_y_data)]
Cal = (
Calendar(init_opts=opts.InitOpts(width="800px", height="500px"))
.add(
series_name="八月份每日评论量分布情况",
yaxis_data=data,
calendar_opts=opts.CalendarOpts(
pos_top='20%',
pos_left='5%',
range_="2021-08",
cell_size=40,
# 年月日标签样式设置
daylabel_opts=opts.CalendarDayLabelOpts(name_map="cn",
margin=20,
label_font_size=14,
label_color='#EB1934',
label_font_weight='bold'
),
monthlabel_opts=opts.CalendarMonthLabelOpts(name_map="cn",
margin=20,
label_font_size=14,
label_color='#EB1934',
label_font_weight='bold',
is_show=False
),
yearlabel_opts=opts.CalendarYearLabelOpts(is_show=False),
),
tooltip_opts='{c}',
)
.set_global_opts(
title_opts=opts.TitleOpts(
pos_top="2%",
pos_left="center",
title=""
),
visualmap_opts=opts.VisualMapOpts(
orient="horizontal",
max_=2000,
pos_bottom='10%',
is_piecewise=True,
pieces=[{"min": 1200},
{"min": 800, "max": 1200},
{"min": 500, "max": 800},
{"min": 300, "max": 500},
{"min": 80, "max": 300},
{"max": 80}],
range_color=["#F5F5F5", "#FFE4E1", "#FFCC99", "#F08080", "#CD5C5C", "#990000"]
),
legend_opts=opts.LegendOpts(is_show=True,
pos_top='5%',
item_width = 50,
item_height = 30,
textstyle_opts=opts.TextStyleOpts(font_size=16,color='#EB1934'),
legend_icon ='path://path://M621.855287 587.643358C708.573965 540.110571 768 442.883654 768 330.666667 768 171.608659 648.609267 42.666667 501.333333 42.666667 354.057399 42.666667 234.666667 171.608659 234.666667 330.666667 234.666667 443.22333 294.453005 540.699038 381.59961 588.07363 125.9882 652.794383 21.333333 855.35859 21.333333 1002.666667L486.175439 1002.666667 1002.666667 1002.666667C1002.666667 815.459407 839.953126 634.458526 621.855287 587.643358Z'
),
)
)
Cal.render_notebook()
3.6 角色热度
主要人物:小白、小青、许仙、法海、司马、孙姐、牛头帮主、蒙面男子、宝青坊主、书生
roles=['小白','小青','许仙','法海','司马','孙姐','牛头帮主','蒙面男子','宝青坊主','书生']
content=''.join([str(i) for i in list(df['评论'])])
roles_num=[]
for role in roles:
count=content.count(role)
roles_num.append((role,count))
roles_num=pd.DataFrame(roles_num)
roles_num.columns=['名称','出现次数']
roles_num
名称 | 出现次数 | |
---|---|---|
0 | 小白 | 1523 |
1 | 小青 | 2683 |
2 | 许仙 | 239 |
3 | 法海 | 396 |
4 | 司马 | 112 |
5 | 孙姐 | 20 |
6 | 牛头帮主 | 1 |
7 | 蒙面男子 | 3 |
8 | 宝青坊主 | 101 |
9 | 书生 | 4 |
# 线性渐变
color_js = """new echarts.graphic.LinearGradient(0, 1, 0, 0,
[{offset: 0, color: '#FFFFFF'}, {offset: 1, color: '#ed1941'}], false)"""
roles_num=roles_num.sort_values(by='出现次数',ascending=False)
roles_num=roles_num.reset_index(drop=True)
b2 = (
Bar()
.add_xaxis(list(roles_num['名称']))
.add_yaxis('频次', list(roles_num['出现次数']),itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_js)))
.set_global_opts(title_opts=opts.TitleOpts(title='影评角色频次分布',pos_top='2%',pos_left = 'center'),
legend_opts=opts.LegendOpts(is_show=False),
yaxis_opts=opts.AxisOpts(name="频次",name_location='middle',name_gap=50,name_textstyle_opts=opts.TextStyleOpts(font_size=16)))
)
b2.render_notebook()
3.7 观众地域分布
cities = df['城市'].values.tolist()
data = Counter(cities).most_common(80)
geo = (
Geo(init_opts=opts.InitOpts(width="1000px", height="600px", bg_color="#404a59"))
.add_schema(maptype="china",
itemstyle_opts={
'normal': {
'shadowColor': 'rgba(0, 0, 0, .5)',
'shadowBlur': 5,
'shadowOffsetY': 0,
'shadowOffsetX': 0,
'borderColor': '#fff'
}
}
)
.add("评论数量", data,type_=ChartType.HEATMAP,)
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(
title_opts=opts.TitleOpts(title="地理位置分布",pos_top="2%", pos_left="center",
title_textstyle_opts=opts.TextStyleOpts(color="#fff", font_size=16)),
legend_opts=opts.LegendOpts(is_show=False),
visualmap_opts=opts.VisualMapOpts(
is_show=True,
is_piecewise=True,
min_ = 0,
max_ = 500,
split_number = 5,
series_index=0,
pos_bottom='5%',
pos_left='5%',
textstyle_opts=opts.TextStyleOpts(color="#fff"),
pieces=[
{'max':500, 'min':401, 'label':'401-500', 'color': '#990000'},
{'max':400, 'min':301, 'label':'301-400', 'color': '#CD5C5C'},
{'max':300, 'min':201, 'label':'201-300', 'color': '#F08080'},
{'max':200, 'min':101, 'label':'101-200', 'color': '#FFCC99'},
{'max':100, 'min':0, 'label':'0-100', 'color': '#FFE4E1'},
],
),
)
)
geo.render_notebook()
从地域分布图来看,观众主要分布在北京、天津、上海、重庆、四川、广东、云南等地。
行业资料:添加即可领取PPT模板、简历模板、行业经典书籍PDF。
面试题库:历年经典,热乎的大厂面试真题,持续更新中,添加获取。
学习资料:含Python、爬虫、数据分析、算法等学习视频和文档,添加获取
交流加群:大佬指点迷津,你的问题往往有人遇到过,技术互助交流。