社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

实时追踪,用Python画新型肺炎疫情地图

AI科技大本营 • 4 年前 • 332 次点击  

作者 | 天元浪子
编辑 | 胡巍巍

近日,在笔者的微信群里,白垩老师问如何用Python画武汉肺炎疫情地图。白垩老师是研究海洋生态与地球生物的学者,国家重点实验室成员,于不惑之年学习Python,实为我等学习楷模。
先前我并没有关注武汉肺炎的具体数据,也没有画过类似的数据分布图。于是就拿了两个小时,专门研究了一下,遂成此文。

数据下载

网上一搜,首先搜到的是腾讯的疫情实时追踪,那就用这个数据源吧。
有了网址怎么抓数据呢?这里,我送大家一双火眼金睛,可以从纷乱中找到最靠谱的下载方式。我习惯用Firefox浏览器,下面的讲解就以Firefox为例(其他浏览器基本类似)。
打开菜单,点击“Web开发者”,在递进菜单中选择"网络":
刷新页面,我们很快就能发现,应答类型为JSON格式的这个请求,最有可能包含我们需要的数据了:
深入分析,我们就得到了URL地址、请求方法、参数、应答格式等信息。查询参数中,Callback是回调函数名,我们可以尝试置空,而“_”应该是以毫秒为单位的当前时间戳。
有了这些信息,分分钟就可以抓到数据了。我们先在IDLE中以交互方式抓一下看看效果:
>>> import time, json, requests
>>> url = 'https://view.inews.qq.com/g2/getOnsInfo?name=wuwei_ww_area_counts&callback=&_=%d'%int(time.time()*1000)
>>> data = json.loads(requests.get(url=url).json()['data'])
>>> print(len(data))
301
>>> print(data[0])
{'country''中国''area''湖北''city''武汉''confirm'698'suspect'0'dead'63'heal'42}
>>> print(data[-1])
{'country''中国''area''山东''city''枣庄''confirm'2 'suspect'0'dead'0'heal'0}


只要两行代码,就可以抓到数据了。怎么样,是不是超级简单?

数据处理

以省为单位画疫情图,我们只需要统计同属一个省的所有地市的确诊数据即可。最终的数据抓取代码如下:
import time, json, requests

def catch_distribution():
    """抓取行政区域确诊分布数据"""

    data = dict()
    url = 'https://view.inews.qq.com/g2/getOnsInfo?name=wuwei_ww_area_counts&callback=&_=%d'%int(time.time()*1000)
    for item in json.loads(requests.get(url=url).json()['data']):
        if item['area'not in data:
            data.update({item['area']:0})
        data[item['area']] += int(item['confirm'])

    return data


数据可视化

数据可视化,我习惯使用Matplotlib模块。Matplotlib有很多扩展工具包(Toolkits),比如,画3D需要mplot3d工具包,画地图的话,则需要Basemap工具包,以及处理地图投影的Pyproj模块。
另外画海陆分界线、国界线、行政分界线等还需要Shape数据。所需模块请自行安装,Shape文件可以从这里(https://github.com/dongli/china-shapefiles)。
绘图用到的矢量字库可以从自己的电脑上随便找一个(我用的是simsun.ttf)。我的主程序是2019nCoV.py,Shape文件下载下来之后,是这样保存的:
以下为全部代码,除了疫情地图,还包括了全国每日武汉肺炎确诊数据的下载和可视化。
# -*- coding: utf-8 -*-

import time
import json
import requests
from datetime import datetime
import numpy as np
import matplotlib
import matplotlib.figure
from matplotlib.font_manager import FontProperties
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.rcParams['font.sans-serif'] = ['FangSong']  # 设置默认字体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像时'-'显示为方块的问题

def catch_daily():
    """抓取每日确诊和死亡数据"""

    url = 'https://view.inews.qq.com/g2/getOnsInfo?name=wuwei_ww_cn_day_counts&callback=&_=%d'%int(time.time()*1000)
    data = json.loads(requests.get(url=url).json()['data'])
    data.sort(key=lambda x:x['date'])

    date_list = list() # 日期
    confirm_list = list() # 确诊
    suspect_list = list() # 疑似
    dead_list = list() # 死亡
    heal_list = list() # 治愈
    for item in data:
        month, day = item['date'].split('.')
        date_list.append(datetime.strptime('2020-%s-%s'%(month, day), '%Y-%m-%d'))
        confirm_list.append(int(item['confirm']))
        suspect_list.append(int(item['suspect']))
        dead_list.append(int(item['dead']))
        heal_list.append(int(item['heal']))

    return date_list, confirm_list, suspect_list, dead_list, heal_list

def catch_distribution():
    """抓取行政区域确诊分布数据"""

    data = {'西藏':0}
    url = 'https://view.inews.qq.com/g2/getOnsInfo?name=wuwei_ww_area_counts&callback=&_=%d'%int(time.time()*1000)
    for item in json.loads(requests.get(url=url).json()['data']):
        if item['area'not in data:
            data.update({item['area']:0})
        data[item['area']] += int(item['confirm'])

    return data

def  plot_daily():
    """绘制每日确诊和死亡数据"""

    date_list, confirm_list, suspect_list, dead_list, heal_list = catch_daily() # 获取数据

    plt.figure('2019-nCoV疫情统计图表', facecolor='#f4f4f4', figsize=(108))
    plt.title('2019-nCoV疫情曲线', fontsize=20)

    plt.plot(date_list, confirm_list, label='确诊')
    plt.plot(date_list, suspect_list, label='疑似')
    plt.plot(date_list, dead_list, label='死亡')
    plt.plot(date_list, heal_list, label='治愈')

    plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m-%d')) # 格式化时间轴标注
    plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
    plt.grid(linestyle=':'# 显示网格
    plt.legend(loc='best'# 显示图例
    plt.savefig('2019-nCoV疫情曲线.png'# 保存为文件
    #plt.show()

def plot_distribution():
    """绘制行政区域确诊分布数据"""

    data = catch_distribution()

    font = FontProperties(fname='res/simsun.ttf', size=14)
    lat_min = 0
    lat_max = 60
    lon_min = 70
    lon_max = 140

    handles = [
            matplotlib.patches.Patch(color='#ffaa85', alpha=1, linewidth=0),
            matplotlib.patches.Patch(color='#ff7b69', alpha=1, linewidth=0),
            matplotlib.patches.Patch(color='#bf2121', alpha=1, linewidth=0),
            matplotlib.patches.Patch(color='#7f1818', alpha=1, linewidth=0),
]
    labels = [ '1-9人''10-99人''100-999人''>1000人']

    fig = matplotlib.figure.Figure()
    fig.set_size_inches(108# 设置绘图板尺寸
    axes = fig.add_axes((0.10.120.80.8)) # rect = l,b,w,h
    m = Basemap(llcrnrlon=lon_min, urcrnrlon=lon_max, llcrnrlat=lat_min, urcrnrlat=lat_max, resolution='l', ax=axes)
    m.readshapefile('res/china-shapefiles-master/china''province', drawbounds=True)
    m.readshapefile('res/china-shapefiles-master/china_nine_dotted_line''section', drawbounds=True)
    m.drawcoastlines(color='black'# 洲际线
    m.drawcountries(color='black')  # 国界线
    m.drawparallels(np.arange(lat_min,lat_max,10), labels=[1,0,0,0]) #画经度线
    m.drawmeridians(np.arange(lon_min,lon_max,10), labels=[0,0,0,1]) #画纬度线

    for info, shape in zip(m.province_info, m.province):
        pname = info['OWNER'].strip('\x00')
        fcname = info['FCNAME'].strip('\x00')
        if pname != fcname: # 不绘制海岛
            continue

        for key in data.keys():
            if key in pname:
                if data[key] == 0:
                    color = '#f0f0f0'
                elif data[key] 10:
                    color = '#ffaa85'
                elif data[key] <100:
                    color = '#ff7b69'
                elif  data[key] 1000:
                    color = '#bf2121'
                else:
                    color = '#7f1818'
                break

        poly = Polygon(shape, facecolor=color, edgecolor=color)
        axes.add_patch(poly)

    axes.legend(handles, labels, bbox_to_anchor=(0.5-0.11), loc='lower center', ncol=4, prop=font)
    axes.set_title("2019-nCoV疫情地图", fontproperties=font)
    FigureCanvasAgg(fig)
    fig.savefig('2019-nCoV疫情地图.png')

if __name__ == '__main__':
    plot_daily()
    plot_distribution()


2019-nCoV疫情曲线


2019-nCoV疫情地图:
上图为圆柱投影,这也是Basemap默认的投影模式,我们还可以换用其他投影模式,比如兰勃托等角投影,只需要将97行代码改为:
m = Basemap(projection='lcc', width=5000000, height=5000000, lat_0=36, lon_0=102, resolution='l', ax=axes)


兰勃托投影效果如下


还可以使用正射投影:
m = Basemap(projection='ortho', lat_0=30, lon_0=105, resolution='l', ax=axes)


正射投影效果如下:


本文为CSDN博主「天元浪子」的原创文章。原文链接:

https://blog.csdn.net/xufive/article/details/104093197


(*本文为AI科技大本营转载文章,转载请联系原作者)



精彩推荐


1、评选进行中,参与投票即有机会参与抽奖,60+公开课免费学习



2、【Python Day——北京站】现已正式启动,「新春早鸟票」火热开抢!2020年,我们还将在全国多个城市举办巡回活动,敬请期待!活动咨询,可扫描下方二维码加入官方交流群~

CSDN「Python Day」咨询群 🔽
来~一起聊聊Python

如果群满100人,无法自动进入,可添加会议小助手微信:婷婷,151 0101 4297(电话同微信)



推荐阅读

    你点的每个“在看”,我都认真当成了AI

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/53930
 
332 次点击