前言
这个春节,大家都在密切关注着疫情的进展。不少人每天醒来打开手机的第一件事,便是查看家乡的疫情图。你所看到的可能是这样的:
又或者是这样的:
疫情进展牵动着我们的心。作为一名开发者,我们闭门在家为抗击疫情做贡献的同时,也可以继续深耕自己的技术。此文章旨在向大家介绍疫情地图可视化的原理,帮助大家深入理解echart。
核心思路
疫情图的核心在于疫情数据整理以及疫情数据可视化。
疫情数据整理
本文疫情数据是由网易新闻的公开数据整理而成,仅用于demo 展示。数据的具体地址已在代码中说明:此地址是一个 Get 请求,大家可以先拷贝地址到浏览器中查看数据格式。请求成功后服务端响应的数据格式如下(文中仅罗列出我们需要的数据。
data 里面的参数
Object 里面的参数
疫情数据可视化
地图是数据可视化的一种常用工具,我们用地图来展示疫情的具体分布。本文采用的是开源的 pyecharts项目,方便开发者用于地图展示。其中,pyecharts 是一个帮助生成 Echarts 图表的类库;而 Echarts 则是百度开源的数据可视化 JS 库,支持折线图、柱状图、散点图、K线图、饼图、雷达图、和弦图、力导向布局图、地图、仪表盘、漏斗图、事件河流图等12类图表,并可以在 PC 和移动设备上流畅地运行,兼容当前绝大部分浏览器。pyecharts 是在 Python 的基础上对 Echarts 所进行的扩展。
原理详解
接下来,本文将为大家详细说明如何搭建环境、整合数据、使用 pyecharts 来做数据可视化以及如何调试项目。
环境搭建
为了快速开发此功能并且尽可能地缩减代码量,此 demo 选择使用 Python 来进行开发。为此,我们应该准备好Python 的开发环境并导入python 基础库。
安装 Python 环境
Mac 上面自带了 Python2.7 ,其他机型的电脑可以参考网上相关的安装教程
安装 pip
pip 是 Python 包管理工具,使用该工具可以快速地对Python 包予以查找、下载、安装、卸载等。如果你是在 python.org上下载的最新版本的安装包,则系统已经自带该工具。此外, Python 2.7.9 + 和 Python 3.4+ 以上版本都自带 pip 工具。可以使用“pip –version”命令行来查看当前 pip 的版本。
如果本机没有 配置Python 环境的,我们可以在线安装,只需要在终端输入以下 2 行命令即可。
$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
$ sudo python get-pip.py
安装工程所需要的插件
在代码中大家不难发现我们导入了一些开源的库:
import math
import time
from fake_useragent import UserAgent
from pyecharts.charts import Map
from pyecharts import options as opts
import requests
import json
import sys
请求网络数据需要用到的插件:
pip install fake_useragent # 伪装请求,随机生成UserAgent
pip install requests # HTTP请求库。
地图展示需要用到的插件:
pip install echarts-countries-pypkg # 世界地图
pip install echarts-china-provinces-pypkg # 中国省级地图
pip install echarts-china-cities-pypkg # 中国城市地图
我们把上述的命令行复制到终端,逐行执行即可。
数据整合& 过滤
代码依然简洁明了,我们可以直接使用 requests 库构建一个 GET 请求,服务器响应的数据即为“ 全国所有城市的疫情情况”。
ua = UserAgent(verify_ssl=False)
headers = {'User-Agent': ua.random}
url = "https://c.m.163.com/ug/api/wuhan/app/index/feiyan-data-list?t=1580469818264"
def getEpidemicInfo(url):
try:
response = requests.get(url, headers=headers)
print(response.status_code)
if response.status_code == 200:
content_field = json.loads(response.text)
epidemicInfo = content_field['data']['list']
return epidemicInfo
else:
print('请求错误码:' + response.status_code)
return None
except Exception as e:
print('此页有问题!', e)
return None
请求地址里面的 t 代表时间戳。我们输入上述代码,计算机便会输出前文所提及格式的数据。注意:拿到数据后还要进行过滤,我们仅需获得某个省份、自治州所包含的地级市或者是某个直辖市所包含的下属区县的疫情信息即可。
通过以下代码,我们对有关数据予以筛选:
# 生成本省疫情列表
def makedict(list):
cityList = {}
for item in list:
for k, v in item.items():
# 1
if v == sys.argv[1]:
#2
if str(item["confirm"]).isdigit():
# 3
if v == "北京" or v == "上海" or v == "天津":
cityList[item['name'] + '区'] = int(item["confirm"])
elif "自治州" in v:
continue
else:
cityList[item['name'] + '市'] = int(item["confirm"])
return cityList
- sys.argv[1] 是一个传参,代表我们手动输入的省份、自治区、直辖市或特别行政区,比如浙江、新疆、北京、香港等;
- “confirm” 关键字用于匹配响应结果的 value 值,在上文疫情数据整合里有提及,代表当前城市的疫情人数;
- pyecharts 是根据城市的全称来适配的,此处需对数据格式中的地级市或者是下属区县进行排查,如果有城市采用简称的,需要进行调试,(如接口返回的城市名是恩施,我们则需要适配成恩施土家族苗族自治州),从而防止地图展示异常。
举例来讲,当我们输入浙江时,计算机最后输出的数据格式为:
{'湖州市': 9, '丽水市': 16, '舟山市': 7, '衢州市': 15, '金华市': 47, '嘉兴市': 30, '绍兴市': 33, '宁波市': 126, '台州市': 124, '杭州市': 151, '温州市': 396}
数据可视化
这是最核心的一步。makeEpidemicInfoMap方法里面的 dict 对应我们过滤得到的数据:
def makeEpidemicInfoMap(dict):
# 省和直辖市
province_distribution = dict
value = province_distribution.values()
print(province_distribution)
title = str(int(time.strftime("%Y%m%d")) - 1) + sys.argv[1] + "疫情地图"
epidemicCount = []
for k, v in dict.items():
epidemicCount.append(v)
# 1
epidemicCount.sort(reverse=True)
maxEpidemic = handle(epidemicCount.pop(0))
maxEpidemic = int(maxEpidemic)
# 2
map = Map()
# 3
map.set_global_opts(
title_opts=opts.TitleOpts(title=title),
visualmap_opts=opts.VisualMapOpts(max_=200, is_piecewise=True,
pieces=[
{"max": 9999999, "min": maxEpidemic, "label": ">" + str(maxEpidemic),
"color": "#780707"}, # 数据范围分段,分颜色,可以根据数据大小具体分配大小
{"max": int(maxEpidemic), "min": int(maxEpidemic / 8) * 7,
"label": str(int(maxEpidemic / 8) * 7) + "-" + str(int(maxEpidemic)),
"color": "#B40404"},
{"max": int(maxEpidemic / 8) * 7, "min": int(maxEpidemic / 8) * 4,
"label": str(int(maxEpidemic / 8) * 4) + "-" + str(
int(maxEpidemic / 8) * 7 - 1), "color": "#CD1111"},
{"max": int(maxEpidemic / 8) * 4, "min": int(maxEpidemic / 8),
"label": str(int(maxEpidemic / 8)) + "-" + str(
int(maxEpidemic / 8) * 4 - 1), "color": "#F68181"},
{"max": int(maxEpidemic / 8), "min": 1,
"label": "1-" + str(int(maxEpidemic / 8)), "color": "#F5A9A9"},
{"max": 0, "min": 0, "label": "0", "color": "#FFFFFF"},
], ) # 最大数据范围,分段
)
# 4
map.add(title, data_pair=province_distribution.items(), maptype=sys.argv[1], is_roam=True)
map.render(sys.argv[1] + '疫情地图.html')
- 根据所选省份各城市的确诊人数对所有城市进行降序并得到当前省份确诊人数最多的城市名称。maxEpidemic 是最接近该城市确诊人数的高位数,比如当前省份疫情最为严重的城市的确诊数量为“357”,则 maxEpidemic=300 ,引入此参数的目的是让地图呈现效果更加清晰直观。
- 用PyEcharts绘制地图需要对Map对象进行初始化,以用于地理区域数据的可视化。
- 以建造者模式对 map 进行设值,其中,VisualMapOpts 是 PyEcharts 的视觉映射配置项。
# 指定 visualMapPiecewise 组件的最大值。
max =100
# 是否为分段型
is_piecewise: bool = False,
# 自定义的每一段的范围,以及每一段的文字,以及每一段的特别的样式。例如:
# pieces: [
# {"min": 1500}, // 不指定 max,表示 max 为无限大(Infinity)。
# {"min": 900, "max": 1500},
# {"min": 310, "max": 1000},
# {"min": 200, "max": 300},
# {"min": 10, "max": 200, "label": '10 到 200(自定义label)'},
# {"value": 123, "label": '123(自定义特殊颜色)', "color": 'grey'}, //表示 value 等于 123 的情况
# {"max": 5} // 不指定 min,表示 min 为无限大(-Infinity)。
# ]
详细配置可以见 PyEcharts 官网。此处的范围分为6 段,每一段的范围均是根据上述计算出来的 maxEpidemic 进行动态调整的,目的是为了保证疫情图的视觉效果,这里面我仅是做了非常简略的范围模型,仅供参考
- 使用 PyEcharts 在当前目录下面生成一个网页
调试
执行 python map.py [省份],如:
python /Users/xxx/map.py 浙江
会在当前目录下面得到一份名字为 浙江疫情地图.html 的文件,直接使用浏览器打开即可,最后的展示效果是不是很酷。
总结
读完此篇文章,希望大家可以对数据可视化有个初步的了解。全民战“疫”期间,个推服务的脚步不会停歇,我们将一如既往地为开发者提供技术支持。此外,个推“防灾速报”小程序还上线了 “新型肺炎疫情实时动态”新功能,为大家进行疫情防护提供可靠的数据支持。