你好,我是尹会生。

在上一讲中,我们学习了怎么使用Seaborn来生成图片格式的图表。事实上,图片格式的图表也被称作静态图表,它能通过数据来更直观地展示结果。

不过很多时候,我们不仅要通过图片直观地展示数据,还要让图片容纳更多种类、更丰富的数据信息。这个时候,静态图表能展示的结果就十分有限了。比如你希望能给领导和同事在会议上演示数据的分析结果时,需要通过一张图来容纳更多的数据。

别担心,这时候我们可以采用动态图表的方法,来增强图片的表现力。因为动态图表展示的结果,相当于静态图表和数据这两者的混合,所以容纳的内容信息也就更丰富。

举个例子,我希望用一张图片来展示全国新冠确诊病例的分布。如果采用动态图,我就可以把鼠标移动到我需要查看的省份上面,显示该地区的确诊人数等相关信息。

就像下面这张截图一样。这张分布图不但基于颜色深浅显示了确诊人数的变化,还能通过鼠标悬停来显示具体的数据。使用起来是不是很方便?

这张动态图表是使用HTML网页文件格式来展示的。同时,它也采用了Python的库pyecharts进行了绘制,其中的图形、数据都可以基于你的需要进行调整。最重要的是,绘制这样一张图片,操作起来和seaborn生成静态图表一样简单。

那接下来,我就为你具体讲解一下怎么使用pyecharts来绘制疫情实时地图,并以此为例,让你掌握怎么通过pyecharts来绘制其他的动态图

要想使用pyecharts绘制动态图,必须要先对它进行安装,再为pyecharts加载数据,最后才能进行绘制动态图。所以我们参考静态图表的学习方法,我来先带你从安装开始学习pyecharts。

安装:使用pip安装pyecharts

pyecharts库和它的安装包同名,因此你依旧可以使用pip命令进行安装。不过在这一步,我们需要验证pyecharts库是否被成功安装。这是非常重要的一步。

因为和之前安装的软件包最大的不同是,pyecharts库的依赖包非常多。这里我要对依赖包的概念多做些补充。

依赖包是指软件为了支持某些功能,而这一功能刚好有其他的第三方库已经实现了,那么该软件就不必再次编写该功能。但是你在使用pyecharts库时,它所依赖的库也必须被安装在你的电脑上面,你才能正常使用它。

这就像你操作Excel在你的脚本使用了“xlwt”库,而其他人使用你的脚本,必须也在他的电脑上安装这个库是相同的道理。因此当你对pyecharts进行安装的时候,会有很多被依赖的安装包一起被安装在你的电脑上。不过不用担心,依赖包并不影响pyecharts库的安装。

由于安装pyecharts的过程,pip命令需要同时安装很多依赖包,你大概率还会在安装过程看到“Requirement already satisfied”这样的提示,这个提示是指你安装的库已经被安装在你的计算机中,有可能是你之前使用pip命令安装的这些被依赖的包,也有可能是因为其他库的依赖,它们被安装到了你的计算机中,但是这些提示都不影响pyecharts库的安装。这些提示也并非安装错误,你可以直接忽略该提示即可。

不过依赖包过多,虽然不影响安装,但是会带来一个主要的问题。那就是在安装的终端界面,会显示很多提示信息,如果提示信息超过一个屏幕的长度,会把安装成功或失败的安装结果覆盖掉,导致你很难确认pyecharts是否安装成功,因此我们需要一种可以确认pyecharts乃至任何一个第三方库是否被成功安装在当前计算机的方法。

想要验证pyecharts库是否被成功安装,可以在命令行执行下面这个命令,来帮助你验证:

SHELL$ pip3 freeze | grep pyecharts
pyecharts==1.9.0

在这条命令中,有三个地方需要你格外注意:

这就是查看某一个库是否被成功安装在当前计算机,以及查看被安装版本的命令,我经常使用这一命令来确认依赖关系较多的库是否被成功安装。

不过你肯定会有疑问了,查看库是否安装这一做法是为了避免有些库存在不兼容,导致安装失败。那为什么还要查看安装的版本呢?

主要是考虑到版本兼容问题。我以pyecharts举例,pyecharts是Python和Echarts的结合体(Echarts是由百度开源的交互式可视化图表工具,基于JavaScript脚本实现)。因此Python提供的接口更新和Echarts工具更新,都会导致使用pyecharts的函数不同。而pyecharts 分为 v0.5.X 和 v1 两个大版本,且v0.5.X 和 v1 不兼容,v1 又是一个全新的版本,这两个版本支持的Python最低版本也不同。简而言之:

如果基于公司规定,你必须使用默认的Python3.4版本的话,可以使用如下命令安装0.5版本的pyecharts:

pip install pyecharts==0.5

解决完pyecharts的版本兼容问题后,相信你的pyecharts的正常运行肯定不在话下了,那接下来我就带你学习怎么给pyecharts加载数据。

数据:为pyecharts加载数据

在给pyecharts加载数据前,我们还要确认数据的格式和数据来源。这样做是为了把从网站中得到的数据转换为符合pyecharts绘图的数据。

例如网站中的数据包含了省、市、区的确诊人数,以及成功被治愈的人数,而我们只需要每个省被确诊的人数。因此数据格式和来源都需要经过你的精心处理,才能被pyecharts展示给使用者。

确认数据的格式和来源

pyecharts的数据格式,要基于不同的图形类型,使用不同的格式。但是一般情况下,是多行多列组成的类似Excel表格的格式,这种格式在Python中一般使用嵌套元组的形式进行保存。

以绘制疫情地图数据为例,我们需要每个省的名称以及现有确诊人数,那么我们可以把省的名称和人数放在一个元组中,并把多个省的数据再组成一个更大的元组,作为pyecharts的源数据。

这种并列数据,你可能第一时间想到的不是元组,而是列表,但是我要告诉你的是,列表的查询效率要远远低于元组。为了让你的图形在展示数据时能够更加流畅,我更建议你使用元组,具体方法是在把列表作为源数据使用前,使用“tuple()”函数把列表类型转换为元组。

确定数据格式之后,我们还需要一个数据来源,为了确保数据的准确性和实时性,我们得从腾讯新闻的网站引入外部数据,数据地址的链接我先贴出来,接下来我再给你讲解一下怎么得到数据地址。

为了得到数据的地址,我分析了网站加载数据的过程,找到了数据接口的地址。这个分析方法你学会以后,也可以应用到其他需要抓取网页数据的工作中。我把抓取的步骤分成四步,分别是开启浏览器调试、请求网页、确认接口和确认返回数据。

先看第一步,开启浏览器调试。这一步骤是为了在请求过程中,能记录网页都请求了哪些数据接口。

以Chrome浏览器为例,使用快捷键F12可以打开调试模式,把选项卡调整至“Network”,调整后就进入接口的监听状态了。

第二步,请求网页。以腾讯疫情实时网页为例,可以在地址栏输入“https://news.qq.com/zt2020/page/feiyan.htm#/”,输入后,调试界面会显示该网页都请求了哪些地址。我把调试页面放在下方供你参考。

截图的左侧就是请求的所有数据接口的地址,从最开始请求的地址向下找,除了JS、JPG等网页图片和样式数据外,其他的请求接口就是我们要重点查看的接口。

第三步,确认接由于网页中包含了多次从“getOnsInfo”接口取得数据,所以要逐个查看接口的返回数据。你可以通过鼠标点击接口的链接,然后再点击“Response”按钮,最后根据返回接口的返回内容,查看是否为“疫情实时数据”。

第四部,确认返回数据。当你从接口初步确认了该数据是“疫情实时数据”后,可以把该地址复制到浏览器中,进行访问。访问的具体办法就是通过模拟网页请求接口,这样就可以得到以下数据了。我将数据放在截图中,供你参考。

截图中的数据,可以通过查找省份和数据来确认数据的正确性。如果数据不正确,你需要回到第二步,再重新找下一个接口。

调整数据格式

当你确认了数据格式和数据源之后,接下来就需要把数据源的格式转换为pyecharts需要的嵌套元组格式。

首先,我先来分析通过网页直接请求数据接口之后的格式,请求后,你会看到网页上面的格式类似于Python的多个字典嵌套在一起,这种格式被称作JSON格式。在Python中你可以通过“json”库去解析这种格式,并把它转换为Python中的字典。解析的方法如下:

import requests
import json
url = 'https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5'
data = requests.get(url)
alldata = json.loads(data.json()['data']

在这段代码中,我使用了requests.get()方法获取了接口的内容,并使用json.loads()方法把接口的数据转换成了字典。由于所有的数据都在下标为“data”的字典中,通过“字典['data']”取得字典值的用法,把所有数据存放到alldata变量中。

接下来,要把alldata变量中的字典转换为嵌套元组形式,这个转换过程需要遍历字典,来取得省份名称和对应的确诊人数。不过为了保存多个省份,我们还需要使用一个新的列表来存储多个省份的数据。最后再把列表转换为元组,作为pyecharts的绘图数据使用。我们依次来看一下:

chinadata = []
for province in alldata['areaTree'][0]['children']:
    provincedata = (
        province['name'],
        province['total']['nowConfirm']
    )
    chinadata.append(provincedata)

在代码的第2行,我使用了for循环,从alldata字典的多层嵌套结构中取出省份和该省份的确诊、累计、新增等人数信息。

在代码的第3行,我使用了一个新的元组,provincedata变量只保存每次遍历时得到的省份和确诊人数。

在代码的第7行,我把每次遍历生成的元组增加到chinadata列表中。这个列表就是最终处理好的数据内容,可以把chinadata数据直接作为pyecharts的源数据来进行绘图。

这段代码比较简单,不过我还是有两个小的使用建议提供给你。

第一个是,我在代码的第3行定义了一个provincedata的元组,从代码的正确执行角度来说,这个变量可以不用定义,完全可以把元组直接写入到chinadata.append() 函数的参数中。

但是从方便阅读代码的角度来看,这样书写代码不利于理解,因为append()函数的参数中包含了较为复杂的类型。因此我建议你在某个函数的参数中,如果传入了多种数据类型时,不妨增加一个临时变量。

另一个小建议是,在数据量较多的时候,应当尽量把列表转换为元组,加快查询效率。例如:我在代码的第7行先使用了列表对象用来存储变化的数据,然后通过for循环迭代alldata变量修改chinadata列表。最后,直到chindata不再需要改变内容时,我立即使用了tuple()把chinadata列表强制转换为了元组,那么后续的查询操作就都可以使用元组了。

通过对数据的抓取、分析和处理,源数据的格式和内容就准备完成了。接下来我需要将源数据加载到pyecharts中,并指定图形的类型和样式。

绘图:使用pyecharts绘制动态图表

当你准备好数据源并处理格式之后,就可以进行绘图了,主要有三个步骤,分别是确定图表类型加载数据和设置图表样式。那我们先来看看怎么确定图表类型。

和我们学习seaborn类似,你可以参考图例,也可以参考分类来学习pyecharts支持的动态图表。与seaborn不同的是,pyecharts的官方文档没有图例,不过不要忘了,pyecharts是基于Echarts编写的,因此图例可以参考Echarts的官方网站

Echarts的图表指定函数和pyecharts相同,找到你需要的图例函数之后,就可以拿到pyecharts中直接使用。

那针对老手的图表分类和API可以参考这个地址。以最常用的图表,折线图为例,你可以打开地址,其中会包括图表的完整调用代码、测试数据和图例,通过参考示例可以让你掌握更多类型的图表。折线图的截图如下:

再让我们回到疫情地图的案例中,由于我们需要绘制中国地图,因此直接使用pyecharts库的Map()类,它是绘制动态地图的类。它的官方网站链接我贴在这里。

通过参考官方网站的案例代码,我们可知在将Map()类实例化之后,进行绘图时,调用了add()、set_global_opts()和render()三个方法,它们分别是增加数据、设置样式和渲染。我们来依次学习一下。

执行render()方法之后,动态图就会以网页的形式保存至“covid19_map.html”文件中,你可以在浏览器里查看,并通过鼠标移动展示不同省份确诊人数的具体信息。

最后,我把pyecharts绘制图形的完整代码贴在下方供你参考,你可以直接将代码复制到自己的计算机执行。

import requests
import json
from pyecharts.charts import Map
from pyecharts import options as opts

url = 'https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5'
data = requests.get(url)
alldata = json.loads(data.json()['data'])

chinadata = []
for province in alldata['areaTree'][0]['children']:
    provincedata = (
        province['name'],
        province['total']['nowConfirm']
    )
    chinadata.append(provincedata)

map_chart = Map()
map_chart.add(
        "全国确诊病例分布图",
        tuple(chinadata),
        "china",
        is_map_symbol_show=False
    )

map_chart.set_global_opts(
    title_opts=opts.TitleOpts(
        title=f"全国疫情地图( {alldata['lastUpdateTime']} )"),
        visualmap_opts=opts.VisualMapOpts(
            is_piecewise=True,
            pieces=[
                {"min": 1, "max": 9, "label": "1-9人", "color": "#FFE6BE"},
                {"min": 10, "max": 99, "label": "10-99人", "color": "#FFB769"},
                {"min": 100, "max": 499, "label": "100-499人", "color": "#FF8F66"},
                {"min": 500, "max": 999, "label": "500-999人", "color": "#ED514E"},
                {"min": 1000, "max": 9999, "label": "1000-9999人", "color": "#CA0D11"},
                {"min": 10000, "max": 100000, "label": "10000人以上", "color": "#A52A2A"}
            ]))

map_chart.render('covid19_map.html')


小结

今天的主要内容就这么多,我来为你最后总结一下本讲的主要内容。

在本讲中,我通过pyecharts展示了疫情实时信息的动态图表,并为你介绍了图表的制作方法。对比静态图表,动态图表的制作方法要复杂,但是一张图能容纳的信息也要比静态图表多,你需要根据自己的工作场景合理的选择图表种类。

同时,你还会发现,图表的绘制难点在数据格式的处理上,通过网络采集数据往往要经过从JSON格式到字典再到元组的嵌套;图表绘制过程可以重复利于的部分就是图表的创建过程,包括数据添加、样式设置和渲染。掌握这些通用的使用原则,可让让你从熟练的操作中,加快自动办公的效率。

而你掌握的图表越丰富,在进行工作汇报和演示时,能够通过图形表达的信息就越清晰。这是我建议你能够掌握更多图表类型的原因。

思考题

今天的思考题是个开放的问题,在你使用pyecharts绘制的图表需要每天更新时,如何自动删除上一个生成的文件?那有没有办法让网页自动更新呢?

欢迎你把思考和想法分享在留言区,我们一起交流讨论。如果今天的内容对你展示工作成果有帮助,也欢迎你把课程分享给你的同事和朋友,我们一起做职场上的效率人。