COVID-19数据分析实战:用Python绘制动态排名视频

简介: COVID-19数据分析实战:用Python绘制动态排名视频

前言


第一篇第二篇第三篇我们对数据进行了重新布局,布局后的数据结构方便我们进行柱状图可视化以及弹道分析。


今天我们来学习使用该数据集执着更酷炫的动态排名视频。

先看效果:


640.gif


一如既往,直奔代码。


640.png


准备数据源


数据源就是我们一直分析的COVID19 data 数据,可以去kaggle 下载。


导入我们所需的库,相比于之前的文章,我们本次分析会用到animation模块,重点是里面会提供FuncAnimation 类,帮助我们实现动态图。


# coding: utf-8
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import matplotlib.animation as animation
from datetime import datetime, timedelta
import numpy as np


pandas 读取数据,这是每篇分析的第一步操作。简单处理数据,采用groupby 函数将一些国家的各个省份信息合并成该国家的总和。前一篇文章有详细介绍,此处不再说明。


# read data
covid19_data_file = 'data/COVID_19_data.csv'
covid19_data_df = pd.read_csv(covid19_data_file)
# handle the countries data
df_country = covid19_data_df.groupby(
    ['ObservationDate', 'Country/Region']).sum()
df_country.reset_index(inplace=True)


动态视频思路-FuncAnimation


大家都知道,视频就是一堆堆图像(或者称为帧 frame)在时间轴上连续起来形成的。所以我们的思路也很简单,制作一个画面,改变画面的内容,重复制作这个画面。


matplotlib 已经有这个类:FuncAnimation,它用来重复调用一个函数进行画图。我们来研究一下它的主要参数,更详细的请参考官方文档。


class matplotlib.animation.FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)[source]¶


其中主要的参数:


  • fig:就是matplotlib的Figure 对象。
  • func:就是需要重复调用的函数。对于我们这个案例来说,需要重复的事情就是“画(水平)柱状图”。所以我们需要定义个画水平柱状图的函数。这也是本文的重点。
  • frames:就是可迭代的对象,假如我们赋值为整数n,就是用range(n)来创造迭代对象
  • init_func:类似于func,如果你的第一帧画面需要调用不同的函数,可选此参数
  • fargs:func 函数的其他参数(除去frames 必须作为第一个位置参数)
  • 其他参数:略


为了调用这个函数,我们需要准备好各个参数。


  • 采用subplots 创建Figure 对象,命名为fig。
  • 调用datetime,设置需要动态显示的起止日期,并且计算出delta 时间。该值我们将作为frames 参数传递给FuncAnimation函数。
  • 剩下就是重中之重,func 函数以及fargs 参数


fig, ax = plt.subplots(figsize=(15, 8))
start_date = datetime(2020, 1, 22)
end_date = datetime(2020, 5, 13)
dates_delta = (end_date - start_date).days


每一帧画面的绘制函数func


先上代码,再做解释。


def mini_bar_chart_frame(
        delta,
        df=None,
        start_date=None,
        date_col=None,
        cat_col=None,
        observe_col=None,
        top_k=10,
        ax=None):
    if start_date is None:
        start_date = datetime(2020, 2, 22)
    date_show = timedelta(days=delta) + start_date
    date_str = date_show.strftime('%m/%d/%Y')
    top_k_df = df[df[date_col].eq(date_str)].sort_values(
        by=observe_col, ascending=False).head(top_k)
    ax.clear()
    # plot horizon bar
    ax.barh(
        top_k_df[cat_col],
        top_k_df[observe_col],
        log=False)
    ax.invert_yaxis()  # to make the biggest in the top
    #dx = np.log(top_k_df[observe_col].max()) / 200
    for i, (value, name) in enumerate(
            zip(top_k_df[observe_col], top_k_df[cat_col])):
        ax.text(
            value - 20,
            i,
            name,
            size=10,
            weight=600,
            ha='right',
            va='center')
        ax.text(
            value + 0.1,
            i,
            f'{value:,.0f}',
            size=10,
            ha='left',
            va='center')
    ax.text(
        1,
        0.1,
        date_str,
        transform=ax.transAxes,
        size=40,
        ha='right',
        weight=800)
    ax.set_yticks([])  # we have label on the top of bar


640.png


代码中我们主要实现一下内容:


  • 整理数据,选出每天top10的国家,并且降序排列
  • 绘制barh,水平绘制时,需要反转y轴,使得最大值排在第一位。也就是上图中第1部分内容绘制完毕
  • 添加国家名称以及对应的确诊数据。也就是上图中第2 和第3部分内容
  • 添加大写的日期,放在右下角,也就是图中第4部分
  • 里面还有一些细节,比如取消掉y轴的标签


函数准备好了,下面我们就将函数的对应的参数传递给FuncAnimation。


fargs = (df_country,
         start_date,
         'ObservationDate',
         'Country/Region',
         'Confirmed',
         10,
         ax)
animator = animation.FuncAnimation(
    fig,
    mini_bar_chart_frame,
    frames=dates_delta,
    fargs=fargs,
    interval=1000,
    repeat=False)


我们也可以使用以下代码将其保存为本地mp4格式。


writer = animation.writers['ffmpeg']
writer = writer(fps=1)
animator.save('mini_covid_bar_race.mp4', writer=writer)


我们看一下上述代码的输出结果,这里我将视频转成gif以做演示。基本效果已经成型,应该算是很经典的动态排名了。


640.png

来点更炫的(彩色+动态文字+xkcd)


彩色柱状图


给柱状图添加颜色,应该很好处理。barh 函数带有color 参数,这里仅仅需要注意传入的颜色需要是类数组的格式。小技巧:


  • 由于我们无法为所有的国家定义颜色,因此这里我们采用定义一个dict颜色集,里面定义主要国家的颜色,然后对于没有定义在dict中的国家,颜色采用默认。颜色代码的获取可以从很多网站查询和复制。


color_dict = {'Mainland China': '#e63946',
              'US': '#ff006e',
              'Italy': '#02c39a',
              'Span': '#f4a261',
              'UK': '#3a86ff',
              'Germany': '#370617',
              'France': '#3a86ff',
              'Japan': '#d8e2dc',
              'Iran': '#fec89a',
              'Russia': '#dc2f02'}
# barh 中的color 参数为:
# color=[
#                color_dict.get(
#                    x,
#                    "#f8edeb") for x in top_k_df[cat_col]],


添加动态文字


这里我添加了一些文字来给视频做注释。比如3月15日,中国捐给西班牙50万个口罩。


  • 之所以用英文,是因为最初这个视频是我放在facebook上给老外看的。
  • 第二个原因,是因为中文需要一些字体支持。

实现动态文字添加的思路很简单,就是ax.text 函数。实现方法类似于我们的国家标签以及确诊数的标签。


640.png


timeline_event = {
    '01/30/2020': 'WuHan declared lockdown.',
    '01/31/2020': 'Italian suspended all flights from China',
    '02/02/2020': 'Trump restricts on any foreigners from entering the U.S',
    '03/13/2020': 'China sent medical supplies to Italy',
    '03/15/2020': 'China donated 500,000 facemasks to Spain',
    '03/19/2020': 'USA suspended visa services worldwide.',
    '05/12/2020': 'America first(LOL).'
}


添加xkcd 效果


xkcd 是啥?只不过一个漫画名称而已,不好发音,也不是缩写。对于matplotlib 来说,xkcd 就指的类似于漫画的的效果。通俗讲就是“线条抖啊抖啊抖~~~~” 代码很简单就一行:


with plt.xkcd():
        把所有plt相关的代码放在这个with 里面


完整的func 函数


除了添加颜色,动态文字以及“抖啊抖”的效果,我们还做了一些细节处理,比如调整字体颜色,字号等小细节。


def xkcd_bar_chart_frame(
        delta,
        df=None,
        start_date=None,
        date_col=None,
        cat_col=None,
        observe_col=None,
        top_k=10,
        color_dict=None,
        ax=None):
    if start_date is None:
        start_date = datetime(2020, 2, 22)
    date_show = timedelta(days=delta) + start_date
    date_str = date_show.strftime('%m/%d/%Y')
    top_k_df = df[df[date_col].eq(date_str)].sort_values(
        by=observe_col, ascending=False).head(top_k)
    with plt.xkcd():
        ax.clear()
        # plot horizon bar
        ax.barh(
            top_k_df[cat_col],
            top_k_df[observe_col],
            color=[
                color_dict.get(
                    x,
                    "#f8edeb") for x in top_k_df[cat_col]],
            log=False,
            left=1)
        ax.invert_yaxis()  # to make the biggest in the top
        #dx = np.log(top_k_df[observe_col].max()) / 200
        for i, (value, name) in enumerate(
                zip(top_k_df[observe_col], top_k_df[cat_col])):
            ax.text(
                value - 20,
                i,
                name,
                size=10,
                weight=600,
                ha='right',
                va='center')
            ax.text(
                value + 0.1,
                i,
                f'{value:,.0f}',
                size=10,
                ha='left',
                va='center')
        ax.text(
            1,
            0.1,
            date_str,
            transform=ax.transAxes,
            color='#f8edeb',
            size=40,
            ha='right',
            weight=800)
        ax.text(
            0.5,
            1.1,
            'Covid-19',
            transform=ax.transAxes,
            size=14,
            color='#f8edeb')
        ax.text(
            0.2,
            0.05,
            timeline_event.get(date_str, ''),
            transform=ax.transAxes,
            size=20,
            color='#06d6a0')
        ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))
        ax.xaxis.set_ticks_position('top')
        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)
        plt.box(False)


重新调用这个新的func来制作动画。


fargs = (df_country,
         start_date,
         'ObservationDate',
         'Country/Region',
         'Confirmed',
         10,
         color_dict,
         ax)
animator = animation.FuncAnimation(
    fig,
    xkcd_bar_chart_frame,
    frames=dates_delta,
    fargs=fargs,
    interval=1000,
    repeat=False)


最后我们来看一下我们更新后的动画效果。ps. 眼看着中国从top10中消失,眼看着America First。


640.png


Tips


保存为MP4格式需要电脑安装ffmep 编码/解码器,安装好的ffmpeg_path需要添加到matplotlibrc 参数下。


# add ffmpeg path to matplotlibrc
plt.rcParams['animation.ffmpeg_path'] = r'your_path\ffmpeg-20200323-ba698a2-win64-static\ffmpeg-20200323-ba698a2-win64-static\bin\ffmpeg.exe'


总结


本文中我们继续使用covid19的数据来进行可视化分析。我们采用python 制作了酷炫的动态排名。定义的函数可以套用在其他数据集中用于制作动态排名。通过本文我们可以学会:


  • 如何制作动态排名(barh race) 图,以及保存为视频
  • 如何给bar 不同类别赋予不同的颜色
  • 如果给画面添加文字
  • 如何是画面显得“抖一抖”
相关文章
|
1天前
|
网络协议 Python
Python 网络编程实战:构建高效的网络应用
【5月更文挑战第18天】Python在数字化时代成为构建网络应用的热门语言,因其简洁的语法和强大功能。本文介绍了网络编程基础知识,包括TCP和UDP套接字,强调异步编程、数据压缩和连接池的关键作用。提供了一个简单的TCP服务器和客户端代码示例,并提及优化与改进方向,鼓励读者通过实践提升网络应用性能。
18 6
|
1天前
|
算法 Java Python
【Python 的内存管理机制专栏】Python 内存管理实战:性能优化与内存泄漏检测
【5月更文挑战第18天】Python内存管理关乎程序性能与稳定性。优化包括避免过多临时对象,如优化列表推导式减少对象创建。警惕循环引用造成的内存泄漏,如示例中的Node类。使用`gc`模块检测泄漏,通过`gc.set_debug(gc.DEBUG_LEAK)`和`gc.collect()`获取信息。实践中需持续分析内存使用,优化算法、数据结构和资源释放,以提升程序质量与效率。
【Python 的内存管理机制专栏】Python 内存管理实战:性能优化与内存泄漏检测
|
2天前
|
机器学习/深度学习 数据挖掘 Python
Python数据分析 | 泰坦尼克逻辑回归(下)
Python数据分析 | 泰坦尼克逻辑回归
7 1
|
2天前
|
机器学习/深度学习 数据挖掘 BI
Python数据分析 | 泰坦尼克逻辑回归(上)
Python数据分析 | 泰坦尼克逻辑回归
15 0
|
2天前
|
数据采集 数据挖掘 Python
Python数据分析 | 线性回归
Python数据分析 | 线性回归
10 1
|
2天前
|
机器学习/深度学习 数据采集 自然语言处理
10个 Python 小技巧,覆盖了90%的数据分析需求!_countries_lat_lon
10个 Python 小技巧,覆盖了90%的数据分析需求!_countries_lat_lon
|
2天前
|
数据采集 人工智能 数据挖掘
「一行分析」利用12000条招聘数据分析Python学习方向和就业方向
「一行分析」利用12000条招聘数据分析Python学习方向和就业方向
|
3天前
|
存储 API 数据库
使用Python和Twilio通过短信通知主持视频办公时间。
使用Python和Twilio通过短信通知主持视频办公时间。
|
4天前
|
存储 缓存 监控
利用Python和Flask构建RESTful API的实战指南
在当今的软件开发中,RESTful API已成为前后端分离架构中的核心组件。本文将带你走进实战,通过Python的Flask框架,一步步构建出高效、安全的RESTful API。我们将从项目初始化、路由设置、数据验证、错误处理到API文档生成,全方位地探讨如何构建RESTful API,并给出一些实用的最佳实践和优化建议。
|
Python
用 Python 动态模拟太阳系运转
提到太阳系,大家可能会想到哥白尼和他的日心说,或是捍卫、发展日心说的斗士布鲁诺,他们像一缕光一样照亮了那个时代的夜空,对历史感兴趣的小伙伴可以深入了解一下,这里就不多说了。 太阳以巨大的引力使周边行星、卫星等绕其运转,构成了太阳系,它主要包括太阳、8 个行星、205 个卫星以及几十万个小行星等,本文我们使用 Python 来简单的动态模拟一下太阳系的运转。
254 0
用 Python 动态模拟太阳系运转