編者按:其實(shí)matplotlib有一個(gè)少有人知的功能animation.FuncAnimation,可以接受你編寫的動(dòng)畫函數(shù)創(chuàng)建動(dòng)圖。Viviane Kakerbeck通過一個(gè)例子展示了這一功能的用法,并介紹了通過增強(qiáng)數(shù)據(jù)和高斯平滑,讓動(dòng)圖更美觀的技巧。
美國的過量服用***致死數(shù),使用seaborn創(chuàng)建
Python的matplotlib和seaborn是非常好用的繪圖庫。但它們創(chuàng)建的都是靜態(tài)圖像,難以通過動(dòng)態(tài)、美觀的方式描述數(shù)據(jù)值的變化。如果你的下一次演示或者下一篇博客文章,能用動(dòng)態(tài)圖形展示數(shù)據(jù)的發(fā)展,該有多好?更妙的是,你可以繼續(xù)使用matplotlib、seaborn或者其他你喜歡用的庫。
我最近為一部關(guān)于美國的阿片樣物質(zhì)危機(jī)的紀(jì)錄片制作了一些動(dòng)態(tài)圖形,所以我會(huì)在這篇文章中使用相關(guān)的數(shù)據(jù)。數(shù)據(jù)來自美國國家藥物濫用研究所和CDC的公開數(shù)據(jù),可以從以下網(wǎng)址下載:https://www.drugabuse.gov/sites/default/files/overdose_data_1999-2015.xls
本文將使用matplotlib和seaborn繪制圖形,同時(shí)使用numpy和pandas處理數(shù)據(jù)。matplotlib提供了一些可以用來制作動(dòng)畫的函數(shù)。閑話少敘,讓我們開始吧,首先,是引入所有依賴。
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
然后我們加載數(shù)據(jù),將其轉(zhuǎn)換成pandas的DataFrame。我還編寫了一個(gè)輔助函數(shù),可以從感興趣的行加載數(shù)據(jù),之后繪圖會(huì)用到。
overdoses = pd.read_excel('overdose_data_1999-2015.xls',sheetname='Online',skiprows =6)
def get_data(table,rownum,title):
data = pd.DataFrame(table.loc[rownum][2:]).astype(float)
data.columns = {title}
return data
準(zhǔn)備就緒,下面是本文主要部分,如何繪制動(dòng)畫。
首先,如果你和我一樣,用的是jupyter notebook,那么我建議你使用%matplotlib notebook指令,這樣可以直接在notebook中查看動(dòng)畫效果,無需等待保存后再查看。
我使用了之前編寫的輔助函數(shù)get_data取得***服用過量數(shù),并將其封裝入一個(gè)兩列的pandas DataFrame,一列表示年份,一列表示服用過量數(shù)。
%matplotlib notebook
title = 'Heroin Overdoses'
d = get_data(overdoses,18,title)
x = np.array(d.index)
y = np.array(d['Heroin Overdoses'])
overdose = pd.DataFrame(y,x)
#XN,YN = augment(x,y,10)
#augmented = pd.DataFrame(YN,XN)
overdose.columns = {title}
接著我們初始化一個(gè)寫入器(writer),基于ffmpeg記錄20 fps(比特率為1800)。當(dāng)然,你可以按照需要調(diào)整這些參數(shù)。
Writer = animation.writers['ffmpeg']
writer = Writer(fps=20, metadata=dict(artist='Me'), bitrate=1800)
接下來創(chuàng)建一個(gè)帶標(biāo)簽的圖形。別忘了限定x軸和y軸的范圍,以免動(dòng)畫在顯示數(shù)據(jù)時(shí)出現(xiàn)跳躍現(xiàn)象。
fig = plt.figure(figsize=(10,6))
plt.xlim(1999, 2016)
plt.ylim(np.min(overdose)[0], np.max(overdose)[0])
plt.xlabel('Year',fontsize=20)
plt.ylabel(title,fontsize=20)
plt.title('Heroin Overdoses per Year',fontsize=20)
制作動(dòng)畫的關(guān)鍵是定義一個(gè)動(dòng)畫函數(shù),指定視頻的每一幀發(fā)生了什么。這里i表示動(dòng)畫幀的索引。你可以選擇在i幀中可見的數(shù)據(jù)范圍。之后我使用seaborn的線圖繪制選定數(shù)據(jù)。最后兩行我調(diào)整了一些尺寸,使圖形看起來更美觀。
def animate(i):
data = overdose.iloc[:int(i+1)] # 選定數(shù)據(jù)范圍
p = sns.lineplot(x=data.index, y=data[title], data=data, color="r")
p.tick_params(labelsize=17)
plt.setp(p.lines,linewidth=7)
定義了動(dòng)畫函數(shù)后,使用matplotlib.animation.FuncAnimation定義動(dòng)畫應(yīng)當(dāng)包含多少幀,也就是說,通過frames參數(shù)定義調(diào)用animate(i)的頻率。
ani = matplotlib.animation.FuncAnimation(fig, animate, frames=17, repeat=True)
之后只需調(diào)用ani.save()就可以將動(dòng)畫保存為mp4文件。如果你想在保存之前先看下效果,那么可以使用plt.show()。
ani.save('HeroinOverdosesJumpy.mp4', writer=writer)
好了,讓我們來看下效果。
看起來效果還可以,但是感覺有點(diǎn)抖動(dòng)。為了緩解抖動(dòng)的現(xiàn)象,我們可以在已有數(shù)據(jù)中插入一些中間值,平滑一下。為此我們定義以下的數(shù)據(jù)增強(qiáng)函數(shù):
def augment(xold,yold,numsteps):
xnew = []
ynew = []
for i in range(len(xold)-1):
difX = xold[i+1]-xold[i]
stepsX = difX/numsteps
difY = yold[i+1]-yold[i]
stepsY = difY/numsteps
for s in range(numsteps):
xnew = np.append(xnew,xold[i]+s*stepsX)
ynew = np.append(ynew,yold[i]+s*stepsY)
return xnew,ynew
之后我們只需在數(shù)據(jù)上應(yīng)用這一增強(qiáng)函數(shù),并相應(yīng)地增加matplotlib.animation.FuncAnimation函數(shù)中的幀數(shù)。這里我調(diào)用augment函數(shù)時(shí)使用了參數(shù)numsteps=10,也就是說,我將數(shù)據(jù)點(diǎn)增加到160個(gè),相應(yīng)地,幀數(shù)設(shè)置為frames=160。增強(qiáng)數(shù)據(jù)之后,結(jié)果看起來平滑了很多,但在數(shù)據(jù)值變動(dòng)處,曲線仍有一些尖角,看起來不是特別美觀。
為了讓圖像看起來更美觀,我們實(shí)現(xiàn)了一個(gè)高斯平滑函數(shù):
def smoothListGaussian(listin,strippedXs=False,degree=5):
window=degree*2-1
weight=np.array([1.0]*window)
weightGauss=[]
for i in range(window):
i=i-degree+1
frac=i/float(window)
gauss=1/(np.exp((4*(frac))**2))
weightGauss.append(gauss)
weight=np.array(weightGauss)*weight
smoothed=[0.0]*(len(listin)-window)
for i in range(len(smoothed)): smoothed[i]=sum(np.array(listin[i:i+window])*weight)/sum(weight)
return smoothe
我在這里不會(huì)討論高斯平滑的細(xì)節(jié),感興趣的讀者可以看這篇文章:https://www.swharden.com/wp/2008-11-17-linear-data-smoothing-in-python/
最后我們略微調(diào)整下顏色和風(fēng)格,就得到了文章開頭的動(dòng)態(tài)圖形:
sns.set(rc={'axes.facecolor':'lightgrey', 'figure.facecolor':'lightgrey','figure.edgecolor':'black','axes.grid':False})
本文通過一個(gè)例子展現(xiàn)了matplotlib動(dòng)畫函數(shù)的用法。當(dāng)然,你可以將它用在任何你想要?jiǎng)赢嫽膱D形上。只需調(diào)整animate()函數(shù)中的參數(shù)和圖形類型,便有無限可能。
我希望你喜歡matplotlib的整個(gè)功能,并能善加利用。另外,如果你對我之前提到的紀(jì)錄片感興趣,可以在YouTube上查看:https://youtu.be/7xrvuSDLHiY
-
圖像
+關(guān)注
關(guān)注
2文章
1079瀏覽量
40375 -
可視化
+關(guān)注
關(guān)注
1文章
1166瀏覽量
20858 -
python
+關(guān)注
關(guān)注
55文章
4768瀏覽量
84376
原文標(biāo)題:matplotlib秘技:讓可視化圖形動(dòng)起來
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論