前言 第一篇 ,、第二篇 ,、第三篇 我們對(duì)數(shù)據(jù)進(jìn)行了重新布局,布局后的數(shù)據(jù)結(jié)構(gòu)方便我們進(jìn)行柱狀圖可視化以及彈道分析,。
今天我們來(lái)學(xué)習(xí)使用該數(shù)據(jù)集執(zhí)著更酷炫的動(dòng)態(tài)排名視頻,。
先看效果:
一如既往,直奔代碼,。
準(zhǔn)備數(shù)據(jù)源 數(shù)據(jù)源就是我們一直分析的COVID19 data 數(shù)據(jù),,可以去kaggle 下載。
導(dǎo)入我們所需的庫(kù),,相比于之前的文章,,我們本次分析會(huì)用到animation模塊,重點(diǎn)是里面會(huì)提供FuncAnimation 類(lèi),,幫助我們實(shí)現(xiàn)動(dòng)態(tài)圖,。
# 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 讀取數(shù)據(jù),這是每篇分析的第一步操作,。簡(jiǎn)單處理數(shù)據(jù),,采用groupby 函數(shù)將一些國(guó)家的各個(gè)省份信息合并成該國(guó)家的總和。前一篇文章有詳細(xì)介紹,,此處不再說(shuō)明,。
# 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)
動(dòng)態(tài)視頻思路-FuncAnimation 大家都知道,視頻就是一堆堆圖像(或者稱為幀 frame)在時(shí)間軸上連續(xù)起來(lái)形成的,。所以我們的思路也很簡(jiǎn)單,,制作一個(gè)畫(huà)面,改變畫(huà)面的內(nèi)容,,重復(fù)制作這個(gè)畫(huà)面,。
matplotlib 已經(jīng)有這個(gè)類(lèi):FuncAnimation,它用來(lái)重復(fù)調(diào)用一個(gè)函數(shù)進(jìn)行畫(huà)圖,。我們來(lái)研究一下它的主要參數(shù),,更詳細(xì)的請(qǐng)參考官方文檔。
class matplotlib.animation.FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)[source]?
其中主要的參數(shù):
fig:就是matplotlib的Figure 對(duì)象,。 func:就是需要重復(fù)調(diào)用的函數(shù),。對(duì)于我們這個(gè)案例來(lái)說(shuō),需要重復(fù)的事情就是“畫(huà)(水平)柱狀圖”,。所以我們需要定義個(gè)畫(huà)水平柱狀圖的函數(shù),。這也是本文的重點(diǎn),。 frames:就是可迭代的對(duì)象,假如我們賦值為整數(shù)n,,就是用range(n)來(lái)創(chuàng)造迭代對(duì)象 init_func:類(lèi)似于func,,如果你的第一幀畫(huà)面需要調(diào)用不同的函數(shù),可選此參數(shù) fargs:func 函數(shù)的其他參數(shù)(除去frames 必須作為第一個(gè)位置參數(shù)) 為了調(diào)用這個(gè)函數(shù),,我們需要準(zhǔn)備好各個(gè)參數(shù),。
采用subplots 創(chuàng)建Figure 對(duì)象,命名為fig,。 調(diào)用datetime,,設(shè)置需要?jiǎng)討B(tài)顯示的起止日期,并且計(jì)算出delta 時(shí)間,。該值我們將作為frames 參數(shù)傳遞給FuncAnimation函數(shù),。 剩下就是重中之重,func 函數(shù)以及fargs 參數(shù) 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
每一幀畫(huà)面的繪制函數(shù)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
代碼中我們主要實(shí)現(xiàn)一下內(nèi)容:
整理數(shù)據(jù),選出每天top10的國(guó)家,,并且降序排列 繪制barh,,水平繪制時(shí),需要反轉(zhuǎn)y軸,,使得最大值排在第一位,。也就是上圖中第1部分內(nèi)容繪制完畢 添加國(guó)家名稱以及對(duì)應(yīng)的確診數(shù)據(jù)。也就是上圖中第2 和第3部分內(nèi)容 添加大寫(xiě)的日期,,放在右下角,,也就是圖中第4部分 里面還有一些細(xì)節(jié),比如取消掉y軸的標(biāo)簽 函數(shù)準(zhǔn)備好了,,下面我們就將函數(shù)的對(duì)應(yīng)的參數(shù)傳遞給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)
我們看一下上述代碼的輸出結(jié)果,,這里我將視頻轉(zhuǎn)成gif以做演示,。基本效果已經(jīng)成型,,應(yīng)該算是很經(jīng)典的動(dòng)態(tài)排名了,。
來(lái)點(diǎn)更炫的(彩色+動(dòng)態(tài)文字+xkcd) 彩色柱狀圖 給柱狀圖添加顏色,應(yīng)該很好處理,。barh 函數(shù)帶有color 參數(shù),這里僅僅需要注意傳入的顏色需要是類(lèi)數(shù)組的格式,。小技巧:
由于我們無(wú)法為所有的國(guó)家定義顏色,,因此這里我們采用定義一個(gè)dict顏色集,,里面定義主要國(guó)家的顏色,然后對(duì)于沒(méi)有定義在dict中的國(guó)家,,顏色采用默認(rèn),。顏色代碼的獲取可以從很多網(wǎng)站查詢和復(fù)制。 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 參數(shù)為: # color=[ # color_dict.get( # x, # "#f8edeb") for x in top_k_df[cat_col]],
添加動(dòng)態(tài)文字 這里我添加了一些文字來(lái)給視頻做注釋,。比如3月15日,,中國(guó)捐給西班牙50萬(wàn)個(gè)口罩。
之所以用英文,,是因?yàn)樽畛踹@個(gè)視頻是我放在facebook上給老外看的,。
第二個(gè)原因,是因?yàn)橹形男枰恍┳煮w支持,。
實(shí)現(xiàn)動(dòng)態(tài)文字添加的思路很簡(jiǎn)單,,就是ax.text 函數(shù)。實(shí)現(xiàn)方法類(lèi)似于我們的國(guó)家標(biāo)簽以及確診數(shù)的標(biāo)簽,。
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 是啥,?只不過(guò)一個(gè)漫畫(huà)名稱而已,不好發(fā)音,,也不是縮寫(xiě),。對(duì)于matplotlib 來(lái)說(shuō),xkcd 就指的類(lèi)似于漫畫(huà)的的效果,。通俗講就是“線條抖啊抖啊 抖~~~~”
代碼很簡(jiǎn)單就一行:
with plt.xkcd(): 把所有plt相關(guān)的代碼放在這個(gè)with 里面
完整的func 函數(shù) 除了添加顏色,,動(dòng)態(tài)文字以及“抖啊抖”的效果,我們還做了一些細(xì)節(jié)處理,,比如調(diào)整字體顏色,,字號(hào)等小細(xì)節(jié)。
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)
重新調(diào)用這個(gè)新的func來(lái)制作動(dòng)畫(huà),。
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)
最后我們來(lái)看一下我們更新后的動(dòng)畫(huà)效果,。ps. 眼看著中國(guó)從top10中消失,眼看著America First,。
Tips 保存為MP4格式需要電腦安裝ffmep 編碼/解碼器,,安裝好的ffmpeg_path需要添加到matplotlibrc 參數(shù)下。
# 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'
總結(jié) 本文中我們繼續(xù)使用covid19的數(shù)據(jù)來(lái)進(jìn)行可視化分析,。我們采用python 制作了酷炫的動(dòng)態(tài)排名,。定義的函數(shù)可以套用在其他數(shù)據(jù)集中用于制作動(dòng)態(tài)排名。通過(guò)本文我們可以學(xué)會(huì):
如何制作動(dòng)態(tài)排名(barh race) 圖,,以及保存為視頻 八卦 聽(tīng)說(shuō)Youtuber 使用Bar race 制作視頻,,月入50萬(wàn)。