alili 2020-02-21
前言
这个春节,大家都在密切关注着疫情的进展。不少人每天醒来打开手机的第一件事,便是查看家乡的疫情图。你所看到的可能是这样的:
又或者是这样的:
疫情进展牵动着我们的心。作为一名开发者,我们闭门在家为抗击疫情做贡献的同时,也可以继续深耕自己的技术。此文章旨在向大家介绍疫情地图可视化的原理,帮助大家深入理解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
举例来讲,当我们输入浙江时,计算机最后输出的数据格式为:
{'湖州市': 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')
# 指定 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 进行动态调整的,目的是为了保证疫情图的视觉效果,这里面我仅是做了非常简略的范围模型,仅供参考
调试
执行 python map.py [省份],如:
python /Users/xxx/map.py 浙江
会在当前目录下面得到一份名字为 浙江疫情地图.html 的文件,直接使用浏览器打开即可,最后的展示效果是不是很酷