數據可視化之美-動態圖繪製(以Python為工具)

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 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繪製,見下

image.png

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:示波器上的李薩如圖形(通過更新數據方式)

李薩如圖形見下:

image.png

```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')

```

image.png

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')

```

image.png

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')

```

image.png

備註:以上皆是利用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)

```

image.png

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)

```

image.png

Reference:

[1] 數據可視化: matplotlib繪製動態圖及3維動畫

[2] 【matplotlib】繪製動態圖像

[3] GitHub pandas_alive