疫情综合看板
对于数据维度较多的大项目,绘制一张看板并动态查看各要素信息是非常有必要的。在此问题领域 Pyecharts 包是较好的解决方案,本实验以 1 -7 月份国内外新冠疫情数据为基础,详细地讲解了看板各要素设计的步骤和需要注意的问题。
屏蔽 Pyecharts 版本相关的报警。
import warnings
warnings.filterwarnings("ignore")
数据集介绍
本数据集来自 GitHub项目 ,本实验对项目中 xlsx 文件夹中国内数据和全球数据进行了相关字段数据的合并和清洗。导入数据并查看前 5 行。
import pandas as pd
df = pd.read_excel('https://labfile.oss.aliyuncs.com/courses/3023/China_world_cov.xlsx')
df['date']=df['date'].dt.date
df.head()
定义条形图函数
!pip install pyecharts==1.7.1
Pyecharts 看板的绘制是相对复杂的过程,如果各子图本身存在代码问题,则进行渲染时,将无法显示渲染结果,因此强烈建议在进行整体看板图设计时,先对各子图的代码逐一检查,看各子图是否能独立绘制完成。
以下定义了本实验中涉及条形图绘制的函数,需要注意的是:
当绘制单张条形图时,Bar 对象会自动分配各系列的 x 坐标轴索引号(xaxis_index),和 y 坐标轴索引号(yaxis_index);
xaxis_index 和 yaxis_index 索引号默认都是以 0 开始;
当条形图与其他带坐标轴的对象(折线图、条形图等)同时作为子图被加入画布时,需要注意索引号不能重复且不能断号;
定义函数时,对于全局变量(set_global_opts)相关参数,尽量不在函数中进行配置,以增加对象的灵活性。
from pyecharts import options as opts
from pyecharts.charts import Bar
def building_bar(data, xaxis_index=0, yaxis_index=0):
bar = Bar()
bar.add_xaxis(list(data.index))
bar.add_yaxis(" ", list(data), xaxis_index=xaxis_index, yaxis_index=yaxis_index,
label_opts=opts.LabelOpts(is_show=False))
return bar
绘制全国各省确诊人数趋势图
通过函数创建条形图并进行全局变量的配置,本实验可视化了 4 月全国各省新冠确诊人数并降序排列。
# 构建数据集
month = 4
data = df
china = data.loc[data['countryName'] == '中国']
china_p_confirm = china.loc[china['month'] == month].groupby(['provinceShortName'])[
'confirmedIncr'].sum().sort_values(ascending=False)
# 构建条形图
china_p_bar = building_bar(china_p_confirm)
# 修饰条形图
china_p_bar.set_global_opts(
# 标题的修饰
title_opts=opts.TitleOpts(
title='中国各省%d月确诊人数' % month),
# 图例的修饰
legend_opts=opts.LegendOpts(is_show=False),
# x 坐标轴的修饰
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=18)),
# y 坐标轴的修饰
yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=18)),
# 数据缩放的设置
datazoom_opts=opts.DataZoomOpts(range_start=0, range_end=30),
)
china_p_bar.render_notebook()
绘制全球各国确诊人数趋势图
与实验「绘制全国各省确诊人数趋势图」相同的过程,可视化了全球各国家 4 月份确诊人数并降序排列。
month = 4
data = df
world = data.loc[data['countryName'] != '中国']
world
county_confirm = world.loc[world['month'] == month].groupby(['countryName'])[
'confirmedIncr'].sum().sort_values(ascending=False)
county_bar = building_bar(county_confirm)
county_bar.set_global_opts(
title_opts=opts.TitleOpts(title='全球各国%d月确诊人数' % month),
legend_opts=opts.LegendOpts(is_show=False),
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=18)),
yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=18)),
datazoom_opts=opts.DataZoomOpts(range_start=0, range_end=10),
)
county_bar.render_notebook()
定义多系列折线图函数
Pyecharts 折线图对象可以实现多系列不同量纲数据的同时显示,在进行接口调用时需要注意以下几个方面:
若多系列是 y 轴方向上的数据,则每个系列对应的 x 数据必须是唯一的;
x 轴数据通过接口 .add_xaxis() 进行添加,y 轴数据通过 .add_yaxis() 接口添加;
当折线图作为子图与其他对象合并时,添加 x 轴数据时须明确索引(xaxis_index),添加 y 轴数据时须明确各系列的名称(series_name)及索引(yaxis_index);
通过 .add_yaxis() 接口添加的第 1 个序列为主序列,其 y 轴坐标轴通过 .set_global_opts() 接口进行设置;
剩下的序列为次坐标序列,y 轴坐标轴通过 .extend_axis() 接口进行设置。
from pyecharts.charts import Line
import matplotlib.colors as cs
def building_line(seriess, data):
line = Line()
# 设置多系列的颜色顺序
colors = [value for value in cs.TABLEAU_COLORS.values()]
# 添加X轴数据
line.add_xaxis(xaxis_data=data.index)
# 添加多系列Y轴数据
for i, series in enumerate(seriess):
line.add_yaxis(
series_name=series,
y_axis=data[series],
symbol="emptyCircle",
is_symbol_show=True,
xaxis_index=0, # 将所有折线图的X轴索引设置为0
yaxis_index=i, # 新增的Y轴数据逐次设置索引值
label_opts=opts.LabelOpts(is_show=False),
linestyle_opts=opts.LineStyleOpts(width=3, color=colors[i]),
)
# 次坐标Y轴的设置
for j, series_y in enumerate(seriess[1:]):
line.extend_axis(
# x数据
xaxis_data=data.index,
# 次坐标轴,主要是y轴的设置
yaxis=opts.AxisOpts(
type_="value",
name=series_y,
offset=j*100-70,
axisline_opts=opts.AxisLineOpts(
linestyle_opts=opts.LineStyleOpts(color=colors[j+1])
),
name_textstyle_opts=opts.LabelOpts(),
axislabel_opts=opts.LabelOpts(color=colors[j+1])
)
)
# 主坐标Y轴的设置
line.set_global_opts(
xaxis_opts=opts.AxisOpts(type_="category",),
yaxis_opts=opts.AxisOpts(
type_="value",
name=seriess[0],
axistick_opts=opts.AxisTickOpts(is_show=True),
splitline_opts=opts.SplitLineOpts(is_show=True),
axisline_opts=opts.AxisLineOpts( # 轴刻度线的设置
linestyle_opts=opts.LineStyleOpts(color=colors[0])),
axislabel_opts=opts.LabelOpts(
is_show=True, color=colors[0]), # 轴标签的设置
)
)
return line
绘制全国某月死亡/确诊/治愈人数
调用折线图函数,进行某月死亡/确诊/治愈人数趋势图的绘制。
month = 3
data = df
china = data.loc[data['countryName'] == '中国']
line_china = china.loc[china['month'] == month].groupby(
['date'])['deadIncr', 'confirmedIncr', 'curedIncr'].sum()
line = building_line(seriess=['deadIncr', 'confirmedIncr',
'curedIncr'], data=line_china)
line.set_global_opts(
legend_opts=opts.LegendOpts(pos_top="15%", pos_right="20%"),
title_opts=opts.TitleOpts(
title='中国各省%d月死亡/确诊/治愈人数' % month, pos_top="5%", pos_left="35%"),
)
line.render_notebook()
定义全国各省确诊人数地图
在前几次课程中,我们已经多次用到过 Map 对象,但都是单张图或简单拼接图,当需要多子图拼接时,Map 地图有 2 个参数尤其关键:
参数 zoom 。 用以调节地图的缩放比例,默认为1,当子图较多时,可通过调节 zoom 减小地图的尺寸;
参数 center 。用以调节地图中心的经纬度,默认情况下,中国地图的正中心(大约湖北省的某个位置)会出现在画布中心,但有时我们希望地图显示在左上角,可通过调节 center 经纬度至台湾省附近的某个经纬度实现。
from pyecharts.charts import Map
def building_map(data_pair, center=None, zoom=1):
m = Map()
m.add(
series_name="",
data_pair=data_pair,
maptype='china',
label_opts=opts.LabelOpts(is_show=False),
is_map_symbol_show=False,
center=center,
zoom=zoom,
)
return m
绘制全国各省确诊人数地图
本实验采用默认参数绘制了全国 3 月份各省确诊人数热力地图,并进行了相关热力图参数的设置。
month = 3
data = df
china = data.loc[data['countryName'] == '中国']
china_p_confirm = china.loc[china['month'] == month].groupby(['provinceShortName'])[
'confirmedIncr'].sum().sort_values(ascending=False)
data_pair = [[province, value]
for province, value in zip(china_p_confirm.index, china_p_confirm)]
visualmap_opts = opts.VisualMapOpts(
is_calculable=True,
pos_top="20%",
pos_left="5%",
min_=china_p_confirm.min(), # 最小数字
max_=china_p_confirm[1]*1.1, # 第二大数字+10%
range_text=["High", "Low"],
range_color=[cs.TABLEAU_COLORS['tab:blue'], # 颜色渐变范围最小值为蓝色,依次为橘色,最大值为红色
cs.TABLEAU_COLORS['tab:orange'],
cs.TABLEAU_COLORS['tab:red']],
textstyle_opts=opts.TextStyleOpts(color="black"),
)
m = building_map(data_pair=data_pair)
m.set_global_opts(
title_opts=opts.TitleOpts(
title='中国各省%d月确诊人数' % month, pos_top="5%", pos_left="35%"),
visualmap_opts=visualmap_opts
)
m.render_notebook()
合并以上各子图
通过 Grid 对象将以上绘图对象进行合并,合并时需要注意以下方面问题:
通过 .add() 接口添加各子图时,需要注意各子图的 xaxis_index 和 yaxis_index(前序实验已做仔细介绍)的顺序号,需要从索引号为 0 的图依次添加;
第一个通过 .add() 接口添加的子图,该子图负责调用 .set_global_opts() 接口对影响全局的相关参数进行设置,例如 visualmap_opts、 datazoom_opts 等参数;
其余被添加的子图除以上参数无法设置(或设置无效)外,可自行设置 title_opts、legend_opts 等参数。
from pyecharts.charts import Grid
month = 3
# ------------ 构建折线图 --xaxis_index=0,yaxis_index=0,1,2------------#
line_china = china.loc[china['month'] == month].groupby(
['date'])['deadIncr', 'confirmedIncr', 'curedIncr'].sum()
line = building_line(seriess=['deadIncr', 'confirmedIncr',
'curedIncr'], data=line_china)
# ------------ 构建全球条形图 --x=1,y=3------------#
county_confirm = world.loc[world['month'] == month].groupby(['countryName'])[
'confirmedIncr'].sum().sort_values(ascending=False)
county_bar = building_bar(county_confirm, xaxis_index=1, yaxis_index=3)
# ------------ 构建中国条形图 --x=2,y=4------------#
china_p_confirm = china.loc[china['month'] == month].groupby(['provinceShortName'])[
'confirmedIncr'].sum().sort_values(ascending=False)
china_p_bar = building_bar(china_p_confirm, xaxis_index=2, yaxis_index=4)
# ------------ 构建地图------------#
data_pair = [[province, value]
for province, value in zip(china_p_confirm.index, china_p_confirm)]
m = building_map(data_pair=data_pair, center=[140, 20], zoom=0.6)
# ---------------- 全局属性 ------------------#
visualmap_opts = opts.VisualMapOpts(
is_calculable=True,
pos_top="20%",
pos_left="45%",
min_=china_p_confirm.min(), # 最小数字
max_=china_p_confirm[1]*1.1, # 第二大数字+10%
range_text=["High", "Low"],
range_color=[cs.TABLEAU_COLORS['tab:blue'], # 颜色渐变范围最小值为蓝色,依次为橘色,最大值为红色
cs.TABLEAU_COLORS['tab:orange'],
cs.TABLEAU_COLORS['tab:red']],
textstyle_opts=opts.TextStyleOpts(color="black"),
)
## ------------ 全局修饰,添加的第一个对象负责对所有对象的全局参数设置------------##
line.set_global_opts(
legend_opts=opts.LegendOpts(pos_top="58%", pos_right="60%"),
title_opts=opts.TitleOpts(
title='中国各省%d月死亡/确诊/治愈人数' % month, pos_top="55%", pos_left="15%"),
visualmap_opts=visualmap_opts,
datazoom_opts=[
opts.DataZoomOpts( # 控制全球条形图
pos_bottom="55%",
range_start=0,
range_end=15,
xaxis_index=[1],
),
opts.DataZoomOpts( # 控制中国条形图
pos_bottom="1%",
range_start=0,
range_end=40,
xaxis_index=[2]
),
opts.DataZoomOpts( # 控制折线图的
pos_bottom="1%",
pos_left="10%",
range_start=0,
range_end=300,
xaxis_index=[0]
)
],
)
# --------------------- 其他子图设置影响其自身的全局变量 --------------------------#
county_bar.set_global_opts(
title_opts=opts.TitleOpts(
title='全球各国%d月确诊人数' % month, pos_top="0%", pos_right="20%"),
legend_opts=opts.LegendOpts(is_show=False)
)
china_p_bar.set_global_opts(
title_opts=opts.TitleOpts(
title='中国各省%d月确诊人数' % month, pos_top="50%", pos_right="20%"),
legend_opts=opts.LegendOpts(is_show=False)
)
m.set_global_opts(
title_opts=opts.TitleOpts(
title='中国各省%d月确诊人数' % month, pos_top="1%", pos_left="20%"),
)
# ---------------------合并-----------------------------#
grid = Grid(init_opts=opts.InitOpts(width="1200px", height="800px"))
# 添加各绘图对象并调节位置
grid.add(line, is_control_axis_index=True, grid_opts=opts.GridOpts(
pos_right="60%", pos_top="65%", pos_bottom="8%"))
grid.add(county_bar, is_control_axis_index=True, grid_opts=opts.GridOpts(
pos_left="60%", pos_top="5%", pos_bottom="65%"))
grid.add(china_p_bar, is_control_axis_index=True, grid_opts=opts.GridOpts(
pos_left="60%", pos_top="55%", pos_bottom="8%"))
grid.add(m, grid_opts=opts.GridOpts(
pos_left="60%", pos_top="55%", pos_bottom="10%"))
grid.render_notebook()
鼠标按住并拖动运行结果图中热力图条的三角形按钮,可以发现,全局相关色块的对象均可被同步调节。
定义子图的看板函数
将合并的画布进一步封装成函数,函数的参数为月份 month 。
def get_month_grid(month):
# ------------ 构建折线图 --x=0,y=0,1,2------------#
line_china = china.loc[china['month'] == month].groupby(
['date'])['deadIncr', 'confirmedIncr', 'curedIncr'].sum()
line = building_line(seriess=['deadIncr', 'confirmedIncr',
'curedIncr'], data=line_china)
# ------------ 构建全球条形图 --x=1,y=3------------#
county_confirm = world.loc[world['month'] == month].groupby(['countryName'])[
'confirmedIncr'].sum().sort_values(ascending=False)
county_bar = building_bar(county_confirm, xaxis_index=1, yaxis_index=3)
# ------------ 构建中国条形图 --x=2,y=4------------#
china_p_confirm = china.loc[china['month'] == month].groupby(['provinceShortName'])[
'confirmedIncr'].sum().sort_values(ascending=False)
china_p_bar = building_bar(
china_p_confirm, xaxis_index=2, yaxis_index=4)
# ------------ 构建地图------------#
data_pair = [[province, value]
for province, value in zip(china_p_confirm.index, china_p_confirm)]
m = building_map(data_pair=data_pair, center=[140, 20], zoom=0.6)
# ---------------- 全局属性 ------------------#
visualmap_opts = opts.VisualMapOpts(
is_calculable=True,
pos_top="20%",
pos_left="45%",
min_=china_p_confirm.min(), # 最小数字
max_=china_p_confirm[1]*1.1, # 第二大数字+10%
range_text=["High", "Low"],
range_color=[cs.TABLEAU_COLORS['tab:blue'], # 颜色渐变范围最小值为蓝色,依次为橘色,最大值为红色
cs.TABLEAU_COLORS['tab:orange'],
cs.TABLEAU_COLORS['tab:red']],
textstyle_opts=opts.TextStyleOpts(color="black"),
)
## ------------ 全局修饰 ------------##
line.set_global_opts(
legend_opts=opts.LegendOpts(pos_top="58%", pos_right="60%"),
title_opts=opts.TitleOpts(
title='中国各省%d月死亡/确诊/治愈人数' % month, pos_top="55%", pos_left="15%"),
visualmap_opts=visualmap_opts, # 设置全局的色块-数据映射
datazoom_opts=[
opts.DataZoomOpts( # 控制全球条形图
pos_bottom="55%",
range_start=0,
range_end=20,
xaxis_index=[1],
),
opts.DataZoomOpts( # 控制中国条形图
pos_bottom="1%",
range_start=0,
range_end=60,
xaxis_index=[2]
),
opts.DataZoomOpts( # 控制折线图
pos_bottom="1%",
pos_left="10%",
range_start=0,
range_end=300,
xaxis_index=[0]
)
],
)
county_bar.set_global_opts(
title_opts=opts.TitleOpts(
title='全球各国%d月确诊人数' % month, pos_top="0%", pos_right="20%"),
legend_opts=opts.LegendOpts(is_show=False)
)
china_p_bar.set_global_opts(
title_opts=opts.TitleOpts(
title='中国各省%d月确诊人数' % month, pos_top="50%", pos_right="20%"),
legend_opts=opts.LegendOpts(is_show=False)
)
m.set_global_opts(
title_opts=opts.TitleOpts(
title='中国各省%d月确诊人数' % month, pos_top="1%", pos_left="20%"),
)
# ---------------------合并-----------------------------#
grid = Grid(init_opts=opts.InitOpts(width="1200px", height="800px"))
grid.add(line, is_control_axis_index=True, grid_opts=opts.GridOpts(
pos_right="60%", pos_top="65%", pos_bottom="8%"))
grid.add(county_bar, is_control_axis_index=True, grid_opts=opts.GridOpts(
pos_left="60%", pos_top="5%", pos_bottom="65%"))
grid.add(china_p_bar, is_control_axis_index=True, grid_opts=opts.GridOpts(
pos_left="60%", pos_top="55%", pos_bottom="8%"))
grid.add(m, grid_opts=opts.GridOpts(
pos_left="60%", pos_top="55%", pos_bottom="10%"))
return grid
添加月度时间线
通过调用 Timeline 对象,实现了带动画的看板绘制,可点击左上角三角形播放按钮播放各月疫情数据变化情况。
from pyecharts.charts import Timeline
import numpy as np
from pyecharts.globals import ThemeType
timeline = Timeline(
init_opts=opts.InitOpts(
width='1200px',
height='750px',
theme=ThemeType.ESSOS, # 设置看板背景主题颜色
)
)
# 调节时间刻度对象的位置
timeline.add_schema(play_interval=1200,
pos_top="5%",
pos_left="1%",
pos_right="65%",
pos_bottom="90%",
)
for month in np.arange(1,8,1):
grid=get_month_grid(month)
timeline.add(grid, "{}月".format(month))
timeline.render_notebook()
文件保存
Pyecharts 中的所有绘图对象可以通过 .render() 接口保存,默认保存格式为 html 网页格式,当保存成网页格式时,可在打开网页后,右键,选择”图片另存为“进行图片的保存。
当我们需要保存图片格式(png,gif,jpeg)时,需要确保以下配置已正确安装( Windwos 环境 ):
phantomjs 的安装。下载地址 ,下载后直接解压到任一目录,并将 bin 所在目录添加到环境变量中;
node.js 的安装。一般能运行 Pyecharts 则说明已完成安装;
python 环境需安装 snapshot-phantomjs 库,运行 pip install snapshot-phantomjs 。 当速度较慢时,可以切换到豆瓣或者清华镜像安装此库。
并通过 pyecharts.render 的 make_snapshot 接口调用 snapshot_phantomjs 的 snapshot 引擎进行图片的生成与保存。
# 本地安装 snapshot_phantomjs
#pip install snapshot_phantomjs
from snapshot_phantomjs import snapshot
from pyecharts.render import make_snapshot
timeline = Timeline(
init_opts=opts.InitOpts(
width='1200px',
height='750px',
theme=ThemeType.ESSOS, # 设置看板背景主题颜色
)
)
# 调节时间刻度对象的位置
timeline.add_schema(play_interval=500,
pos_top="5%",
pos_left="1%",
pos_right="65%",
pos_bottom="90%",
)
for month in np.arange(1, 8, 1):
grid = get_month_grid(month)
timeline.add(grid, "%d月" % month)
# 生成网页并保存各种格式图片
make_snapshot(snapshot, timeline.render("a.html"), "./a.png")
# --------------- 以下文件格式请自行尝试 ------------------------#
# make_snapshot(snapshot, timeline.render(), "./a.jpeg")
# make_snapshot(snapshot, timeline.render(), "./a.pdf")
# make_snapshot(snapshot, timeline.render(), "./a.gif")
# make_snapshot(snapshot, timeline.render(), "./a.eps")
# make_snapshot(snapshot, timeline.render(), "./a.base64")
# 渲染到notebook
timeline.render_notebook()