數據可視化之美-動態圖繪製(以Python為工具)
持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第2天,點擊查看活動詳情
數據可視化之美-動態圖繪製(以Python為工具) 可視化是數據描述的圖形表示,旨在一目瞭然地揭示數據中的複雜信息。數據可視化在講故事中的重要性不言而喻。
在可視化當中,我們常用的方式有點、線、面,或者説這幾者組合。部分繪圖結果可見下面鏈接: Python 畫點線圖、Python 畫柱狀圖、Python 畫柱狀圖(總結)
在可視化中最為複雜的要數動態圖的繪製,動態圖能表達出更多的信息(如時間維),包含二維(x,time)、三維(x,y,time)、四維(x,y,z,time)數據中的信息。
同時,動畫可以有趣地展示某種現象。相比於靜態圖表,人們更容易被動畫和交互式的圖表所吸引。
由於博主本身專業的限制,只重點關注了地學的相關文獻、代碼,其他專業也可借鑑本文中的相關動態圖製作方法。
動圖可視化作品舉例
在日常學習生活中,可以發現有非常多精彩的動態圖可視化作品,這些動態圖為工作的展現、數據的説明都起到錦上添花的效果。見下:
Example1:
\ 該圖基於MITgcm模式輸出數據繪製
Example2:
該動圖的左邊反映的是缺氧區域隨時間的變化,右邊反映的是空間的變化
Example3:
\ 新冠肺炎初期死亡人數的變化\ 該項目鏈接:GitHub bar_chart_race
這種動圖也可用pandas_alive繪製,見下
Example4:
This animation shows the transport of air parcels continuously emitted from four point sources inside the canopy during a period of 2.5 hours for the simulation 該動圖來源於SCI論文《地形對茂密森林內排放的氣體在冠層內運輸的影響》 (Effects of topography on in‐canopy transport of gases emitted within dense forests - Chen - 2019 - Quarterly Journal of the Royal Meteorological Society - Wiley Online Library)
接下來重點介紹GIF圖的生成,需要注意的是,GIF的生成一般是需要安裝imagemagick(圖片處理工具),用pillow也行(但相對較有瑕疵)。
舉例説明:基於matplotlib中的anamation
Test1:移動的sin曲線(通過更新數據方式)
```python import numpy as np from matplotlib import pyplot as plt from matplotlib.animation import FuncAnimation plt.style.use('seaborn-pastel') fig = plt.figure() ax = plt.axes(xlim=(0, 4), ylim=(-2, 2)) # 創建了一個圖像窗口,設置了座標軸的範圍 line, = ax.plot([], [], lw=3) # 建了一個空的line對象,之後將通過更新line的數據,實現動畫效果。
def init(): # 初始化函數init,這個函數其實什麼也沒幹,給了line對象空的數據 line.set_data([], []) return line, def animate(i): # 動畫函數animate,入參i是動畫幀序號,根據i計算新的sin曲線數據,更新到line對象。 x = np.linspace(0, 4, 1000) y = np.sin(2 * np.pi * (x - 0.01 * i)) line.set_data(x, y) return line, anim = FuncAnimation(fig, animate, init_func=init, frames=100, interval=20, blit=True)
利用之前所説的FuncAnimation()函數創建了對象anim,初始化時傳入了figure對象,init()函數和animate()函數,以及幀數和更新時間。
循環一個週期,也就是100幀,就可以實現前後相接的循環效果了
anim.save('sine_wave_pillow.gif', writer='pillow') # writer 根據實際安裝可選擇
``` \ (備註: 這裏保存GIF參數選用pillow,座標軸顯示不清晰,可選用imagemagick)
Test2:示波器上的李薩如圖形(通過更新數據方式)
李薩如圖形見下:
```python import matplotlib.pyplot as plt import matplotlib.animation as animation import numpy as np plt.style.use('dark_background') fig = plt.figure() ax = plt.axes(xlim=(-50, 50), ylim=(-50, 50)) line, = ax.plot([], [], lw=2)
initialization function
def init(): # creating an empty plot/frame line.set_data([], []) return line,
lists to store x and y axis points
xdata, ydata = [], []
simulate ghost effect of oscilloscope
def ghostImage(x,y): xdata.append(x) ydata.append(y) if len(xdata)>60: del xdata[0] del ydata[0] return xdata,ydata
animation function
def animate(i): # t is a parameter t = i/100.0
# x, y values to be plotted
x = 40*np.sin(2*2*np.pi*(t+0.3))
y = 40*np.cos(3*2*np.pi*t)
# appending new points to x, y axes points list
line.set_data(ghostImage(x,y))
return line,
setting a title for the plot
plt.title('Creating a Lissajous figure with matplotlib')
hiding the axis details
plt.axis('off')
call the animator
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=400, interval=20, blit=True)
save the animation as gif file
anim.save('figure.gif',writer='pillow')
```
Test3:二維動畫繪製(通過疊加圖像形式)
預先計算的【圖像列表】的動畫圖像
```python import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation
fig = plt.figure()
def f(x, y): return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120) y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
ims is a list of lists, each row is a list of artists to draw in the
current frame; here we are just animating one artist, the image, in
each frame
ims = [] for i in range(60): x += np.pi / 15. y += np.pi / 20. im = plt.imshow(f(x, y), animated=True) ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000)
ani.save('test_2d.gif', writer='pillow')
```
Test4:二維動畫繪製(通過替換數據的形式)
```pyhton import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation #導入負責繪製動畫的接口
fig, ax = plt.subplots() x = np.linspace(-10np.pi,10np.pi,300) y = np.linspace(-10np.pi,10np.pi,300) X,Y = np.meshgrid(x,y) img = plt.imshow(np.sin(X),cmap='ocean') nums = 1000 #需要的幀數
def init(): ax.set_xlim(0, 100) ax.set_ylim(0, 100) return img
def update(step): z = np.cos((0.5X(np.cos(0.01step)+0.1))+step)+np.sin(0.5(Y(np.sin(0.01step)+0.1))+step) # z = np.cos((X(np.cos(0.01step)+0.2))+step)+np.sin((Y(np.sin(0.01step)+0.2))+step) img.set_data(z) #設置新的 x,y return img
ani = FuncAnimation(fig, update, frames=nums, #nums輸入到frames後會使用range(nums)得到一系列step輸入到update中去 init_func=init,interval=100)
plt.show()
ani.save('example4.gif', writer='pillow')
```
備註:以上皆是利用matplotlib中的anamation繪製,還可以利用上文提到的pandas_alive進行繪製
舉例説明:基於pandas_alive
Test5:x軸顯示固定
```python import pandas as pd import pandas_alive
elec_df = pd.read_csv("data/Aus_Elec_Gen_1980_2018.csv",index_col=0,parse_dates=[0],thousands=',')
elec_df.fillna(0).plot_animated('./fixed-example.gif',period_fmt="%Y",title='Australian Electricity Generation Sources 1980-2018',fixed_max=True,fixed_order=True)
```
Test6:多子圖動態圖
```pyhthon import pandas as pd import pandas_alive
covid_df = pd.read_csv('data/covid19.csv', index_col=0, parse_dates=[0])
animated_line_chart = covid_df.diff().fillna(0).plot_animated(kind='line', period_label=False,add_legend=False) animated_bar_chart = covid_df.plot_animated(n_visible=10)
pandas_alive.animate_multiple_plots('./example-bar-and-line-chart.gif', [animated_bar_chart, animated_line_chart], enable_progress_bar=True)
```