Anders Wang


我所认识的每个人都是榜样,都有值得我去尊敬和学习的地方。


COVID-19疫情数据动态排行

很久之前,我在抖音app上看到有用动态的数据排行效果来展示各种经济,人口增长等数据,非常震撼又很有视觉直观性。而2020年疫情爆发期时的每日疫情数据又是大家最关心的。所以我就想着自己仿造类似的效果。

网上的动态数据排行在我了解之后主要发现是用javascript写出来的,但是基于对javascript没有那么深入了解,我找到了其它可替代方案,就是使用matplotlib的animation方法来绘制动图。

效果展示

最终的效果还不错,可以看下GIF效果动图。

covid19_animation

设计逻辑

对于整个代码的设计逻辑主要就是两方面,数据 和 动态展示。在实际编写前首先需要思考下如何获取数据和如何使数据动起来达到自己的设想。

对于获取数据而言,本身打算自己去用爬虫获取数据,但是鉴于各个信息源的数据都比较杂乱,我搜索发现已经有人做了相关的数据汇总并开放使用,可以说已经有了半成品的疫情数据集,那就可以直接拿来用,只需要对数据做基于自己场景下的数据清洗就可以了。

数据问题已经解决,接下来就是考虑如何达到最终目的使数据动态化。按照文章开头提到的,只需要使用matplotlib的animation方法就可以绘制动图。

代码分解

开始的部分主要是做数据清洗的部分,尽管我们获取的公开数据集已经很好的汇总了国内以及全球的疫情每日信息,但是由于制作动图的所需数据格式不一样,所以还是需要做一定的数据清洗和整理。

# 完整代码
import os  
import pandas as pd  
import matplotlib as mpl  
import matplotlib.pyplot as plt  
import matplotlib.ticker as ticker  
import matplotlib.animation as animation  
from IPython.display import HTML

# 数据源地址
url='https://raw.githubusercontent.com/canghailan/Wuhan-2019-nCoV/master/Wuhan-2019-nCoV.csv'  
df_csv = pd.read_csv(url, parse_dates=['date'], low_memory=False)  
df_csv.sample(10)  

完成导入数据后,随机抽取10行数据查看内容如下。

covid19_001

由于数据里既包含了国家又包含了国内各省市的数据,而我们只需要罗列各国的数据,那就需要做数据分类处理。经过观察,可以看到上面的内容里country字段包含了各个国家的信息,而只要是以国家为统计单位的话province这一列的字段是Nan值,所以为了方便处理这里做数据填充,统一把Nan空值改为0。

# 由于原始数据包含的外国数据是不存在省份信息,所以统一设置为0
df_csv['province'].fillna(0, inplace=True)

# 只要省份信息为0,说明它属于一个国家。同时country字段筛选出'中国'
china_data = df_csv[(df_csv['country']=='中国') & (df_csv['province']==0)]  
non_china_data = df_csv[df_csv['country']!='中国']

为了验证数据是否正确,使用head()方法查看前5行数据的内容如下。

china_data.head()  

covid19_002

non_china_data.head()  

covid19_003

我们发现上面chinadata和nonchina_data两个变量中储存的中国和非中国的疫情数据集完全符合预期的格式结果。接下来需要去掉一些不相关的字段列,并且将这两大数据集合并起来。

# 为了后续的数据叠加,只需要各个国家以及确诊,死亡等相关信息,其它删除。
china_data.drop(['countryCode', 'cityCode', 'province', 'provinceCode', 'city'], axis=1, inplace=True)  
non_china_data.drop(['countryCode', 'cityCode', 'province', 'provinceCode', 'city'], axis=1, inplace=True)

# 将中国和其它外国数据叠加
all_countries_data = china_data.append(non_china_data)  
all_countries_data.reset_index(drop=True, inplace=True)  

数据叠加后,养成好习惯再次查验下内容是否是我们想要的。

all_countries_data.sample(10)  

格式精简过的数据看起来十分干净并且很正确。

covid19_004

接着,开始进入可视化环节。为了更好的区分各个国家,所以用不同颜色来表示,世界上的国家不少,这里只罗列出疫情数据相对排名突出的一些国家。

# 疫情中主要出现的一些国家的相应颜色
colors = dict(zip(  
    ['美国', '德国', '伊朗', '瑞士', '荷兰', '韩国','比利时', '奥地利', '土耳其',
     '葡萄牙', '加拿大', '挪威', '澳大利亚', '巴西', '以色列', '瑞典',
     '捷克', '丹麦', '马来西亚', '爱尔兰', '日本','巴基斯坦', '俄罗斯', '泰国', 
     '沙特阿拉伯', '南非', '芬兰', '印度尼西亚', '菲律宾', '希腊',
     '冰岛', '印度', '新加坡', '巴拿马', '墨西哥', '意大利', '钻石公主号邮轮', '阿根廷',
     '法国','塞尔维亚', '伊拉克', '巴林','新西兰', '黎巴嫩', '阿尔及利亚', '阿联酋', 
     '乌克兰', '拉脱维亚', '西班牙', '越南', '中国', '英国'],
    ['#FFB7DD', '#FF88C2','#FF44AA','#FF0088','#C10066','#A20055','#8C0044',
    '#FFCCCC','#FF8888','#FF3333','#FF0000','#CC0000','#AA0000','#880000',
    '#FFC8B4','#FFA488','#FF7744','#FF5511','#E63F00','#C63300','#A42D00',
    '#FFDDAA','#FFBB66','#FFAA33','#FF8800','#EE7700','#CC6600','#BB5500',
    '#FFEE99','#FFDD55','#FFCC22','#FFBB00','#DDAA00','#AA7700','#886600',
    '#FFFFBB','#FFFF77','#FFFF33','#FFFF00','#EEEE00','#BBBB00','#888800']))

如下代码主要创建了一个构建图例的函数,这个函数我们会在之后通过matplotlib下的animation函数不停的循环传入日期参数以此达到动态效果。

fig, ax = plt.subplots(figsize=(15, 8))

# 定义画横向柱状图函数
def draw_barchart(current_date):  
# 只筛选排名前10的国家
    df = all_countries_data[all_countries_data['date']==pd.to_datetime(current_date)].sort_values(by='confirmed', ascending=False).head(10)

  # 因为横向显示,所以数据多的国家考前排名 
    df = df[::-1]

    ax.clear()

    # 开始画柱状图,并根据之前颜色字典分配指定国家颜色,由于使用了setdefault方法那么字典中不存在就使用默认颜色。
    ax.barh(df['country'], df['confirmed'], color=[colors.setdefault(x,'#90d595') for x in df['country']])

    dx = df['confirmed'].max() / 200
    for i, (country, confirmed) in enumerate(zip(df['country'], df['confirmed'])):
        ax.text(x=confirmed-dx+300, y=i+0.02, s=country, size=12, weight=600, ha='right', va='center')  # Tokyo: 名字
        ax.text(x=confirmed+dx, y=i, s=f'{confirmed:,.0f}', size=16, ha='left', va='center')

    # 对样式的修改
    ax.text(0.8, 0.15, current_date, transform=ax.transAxes, color='#777777', size=30, weight=800)
    ax.text(0, 1.05, '确认感染病例', transform=ax.transAxes, size=12, color='#777777')
    ax.text(0.97, 0.1, 'code by Anders', transform=ax.transAxes, size=12, color='#777777', ha='right')
    ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))
    ax.xaxis.set_ticks_position('top')

    # 将x轴确诊病例的最大范围设置为所有国家最高的确诊病例的1.1倍
    plt.xlim(0, all_countries_data['confirmed'].max()*1.1)
    ax.tick_params(axis='x', colors='#777777', labelsize=12)
    ax.set_yticks([])
    ax.margins(0, 0.01)
    ax.grid(which='major', axis='x', linestyle='-')
    ax.set_axisbelow(True)
    ax.text(0, 1.1, '世界COVID-19确诊病例排行榜',
            transform=ax.transAxes, size=24, weight=600, ha='left')
    plt.box(False)

接下来我们要用到animation.FuncAnimation,而函数FuncAnimation(fig,func,frames,init_func,interval,blit)是绘制动图的主要函数,其参数如下:

  • fig 绘制动图的画布名称
  • func自定义动画函数,即下边程序定义的函数update
  • frames动画长度,一次循环包含的帧数,在函数运行时,其值会传递给函数update(n)的形参“n”。
  • init_func自定义开始帧,即传入刚定义的函数init,初始化函数
  • interval更新频率,以ms计。
  • blit选择更新所有点,还是仅更新产生变化的点。应选择True,但mac用户请选择False,否则无法显。

最后我们只需要设置一个时间列表,并传入时间列表给FuncAnimation方法,即可以html的形式显示我们的动态可视化数据排行。

# 调用图例表生成函数
date_time = ['2020-03-25', '2020-03-26', '2020-03-27', '2020-03-28',  
             '2020-03-29', '2020-03-30', '2020-03-31', '2020-04-01']


animator = animation.FuncAnimation(fig, draw_barchart, frames=date_time)  
HTML(animator.to_jshtml())  

covid19_005

完整代码

# 完整代码
import os  
import pandas as pd  
import matplotlib as mpl  
import matplotlib.pyplot as plt  
import matplotlib.ticker as ticker  
import matplotlib.animation as animation  
from IPython.display import HTML

# 数据源地址
url='https://raw.githubusercontent.com/canghailan/Wuhan-2019-nCoV/master/Wuhan-2019-nCoV.csv'  
df_csv = pd.read_csv(url, parse_dates=['date'], low_memory=False)

# 由于原始数据包含的外国数据是不存在省份信息,所以统一设置为0
df_csv['province'].fillna(0, inplace=True)

# 只要省份信息为0,说明它属于一个国家。同时country字段筛选出'中国'
china_data = df_csv[(df_csv['country']=='中国') & (df_csv['province']==0)]  
non_china_data = df_csv[df_csv['country']!='中国']

# 为了后续的数据叠加,只需要各个国家以及确诊,死亡等相关信息,其它删除。
china_data.drop(['countryCode', 'cityCode', 'province', 'provinceCode', 'city'], axis=1, inplace=True)  
non_china_data.drop(['countryCode', 'cityCode', 'province', 'provinceCode', 'city'], axis=1, inplace=True)

# 将中国和其它外国数据叠加
all_countries_data = china_data.append(non_china_data)  
all_countries_data.reset_index(drop=True, inplace=True)

# 疫情中主要出现的一些国家的相应颜色
colors = dict(zip(  
    ['美国', '德国', '伊朗', '瑞士', '荷兰', '韩国','比利时', '奥地利', '土耳其',
     '葡萄牙', '加拿大', '挪威', '澳大利亚', '巴西', '以色列', '瑞典',
     '捷克', '丹麦', '马来西亚', '爱尔兰', '日本','巴基斯坦', '俄罗斯', '泰国', 
     '沙特阿拉伯', '南非', '芬兰', '印度尼西亚', '菲律宾', '希腊',
     '冰岛', '印度', '新加坡', '巴拿马', '墨西哥', '意大利', '钻石公主号邮轮', '阿根廷',
     '法国','塞尔维亚', '伊拉克', '巴林','新西兰', '黎巴嫩', '阿尔及利亚', '阿联酋', 
     '乌克兰', '拉脱维亚', '西班牙', '越南', '中国', '英国'],
    ['#FFB7DD', '#FF88C2','#FF44AA','#FF0088','#C10066','#A20055','#8C0044',
    '#FFCCCC','#FF8888','#FF3333','#FF0000','#CC0000','#AA0000','#880000',
    '#FFC8B4','#FFA488','#FF7744','#FF5511','#E63F00','#C63300','#A42D00',
    '#FFDDAA','#FFBB66','#FFAA33','#FF8800','#EE7700','#CC6600','#BB5500',
    '#FFEE99','#FFDD55','#FFCC22','#FFBB00','#DDAA00','#AA7700','#886600',
    '#FFFFBB','#FFFF77','#FFFF33','#FFFF00','#EEEE00','#BBBB00','#888800',
    '#EE9A00','#EE9572','#EE82EE','#EE8262','#EE7AE9','#EE799F','#EE7942',
      '#EE7621','#EE7600','#EE6AA7']))

fig, ax = plt.subplots(figsize=(15, 8))

# 定义画横向柱状图函数
def draw_barchart(current_date):

    df = all_countries_data[all_countries_data['date']==pd.to_datetime(current_date)].sort_values(by='confirmed', ascending=False).head(10)
    df = df[::-1]

    ax.clear()

    # 开始画柱状图,并根据之前颜色字典分配指定国家颜色,由于使用了setdefault方法那么字典中不存在就使用默认颜色。
    ax.barh(df['country'], df['confirmed'], color=[colors.setdefault(x,'#90d595') for x in df['country']])

    dx = df['confirmed'].max() / 200
    for i, (country, confirmed) in enumerate(zip(df['country'], df['confirmed'])):
        # 设置国家名字
        ax.text(x=confirmed-dx+300, y=i+0.02, s=country, size=12, weight=600, ha='right', va='center') 
        # 设置确诊数值
        ax.text(x=confirmed+dx, y=i, s=f'{confirmed:,.0f}', size=16, ha='left', va='center')

    # 对样式的修改
    ax.text(0.8, 0.15, current_date, transform=ax.transAxes, color='#777777', size=30, weight=800)
    ax.text(0, 1.05, '确认感染病例', transform=ax.transAxes, size=12, color='#777777')
    ax.text(0.97, 0.1, 'code by Anders', transform=ax.transAxes, size=12, color='#777777', ha='right')
    ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))
    ax.xaxis.set_ticks_position('top')

    # 将x轴确诊病例的最大范围设置为所有国家最高的确诊病例的1.1倍
    plt.xlim(0, all_countries_data['confirmed'].max()*1.1)
    ax.tick_params(axis='x', colors='#777777', labelsize=12)
    ax.set_yticks([])
    ax.margins(0, 0.01)
    ax.grid(which='major', axis='x', linestyle='-')
    ax.set_axisbelow(True)
    ax.text(0, 1.1, '世界COVID-19确诊病例排行榜',
            transform=ax.transAxes, size=24, weight=600, ha='left')
#     ax.text(1, 0, ',by QIML',, transform=ax.transAxes, ha=',right',,
#             color=','#777777',, bbox=dict(facecolor=',white',, alpha=0.8, edgecolor=',white',))
    plt.box(False)


date_time = [  '2020-03-25', '2020-03-26', '2020-03-27', '2020-03-28',  
               '2020-03-29', '2020-03-30', '2020-03-31', '2020-04-01']


animator = animation.FuncAnimation(fig, draw_barchart, frames=date_time)  
# 在jupyter上显示出动态效果
HTML(animator.to_jshtml())  

最后可以使用FFmpeg视频解码器,将我们的动态效果一帧帧转为指定的mp4视频文件。

# 输出mp4动画,由于我们使用了ffmpeg来解码,所以需要指定系统中ffmpeg执行文件的位置
ffmpegpath = os.path.abspath("/Users/Anders/Documents/Jupyter/ffmpeg")  
plt.rcParams["animation.ffmpeg_path"] = ffmpegpath  
writer = animation.FFMpegWriter(extra_args=['-vcodec', 'libx264'])  
animator.save('covid19_animation.mp4', writer=writer, dpi=180)  
最近的文章

COVID-19疫情简要可视化分析

2020年全球遭遇了新冠肺炎疫情,各大门户网站和主流App其实都有多维度的疫情数据分析。但是我还是打算尝试做一些简单的数据分析展示,同时会将数据以地图的形式可视化展示。 整个数据文件一共有两个分别为d…

Python, 技术博文, 数据分析详细阅读
更早的文章

Kaggle - Rossmann Store Sales 销量预测项目

本篇内容大纲目录如下[支持页内跳转]: I. 问题的定义 项目概述 问题陈述 评价指标 II. 分析 数据的探索 探索性可视化 算法和技术 基准和模型 III. 方法 数据预处理 执行过程 完善 IV…

机器学习, 技术博文, 数据分析, Python详细阅读
comments powered by Disqus