如何基於Python Matplotlib實現網格動畫
—1—
如果你對本文的程式碼感興趣,可以去 Github (文末提供)裡檢視。第一次執行的時候會報一個錯誤(還沒找到解決辦法),不過只要再執行一次就正常了。
這篇文章雖然不是篇典型的資料科學類文章,不過它涉及到資料科學以及商業智慧的應用。Python 的 Matplotlib 是最常用的圖表繪製以及資料視覺化庫。我們對摺線圖、柱狀圖以及熱力圖都比較熟悉,但你知道用 Matplotlib 還能做簡單的動畫嗎?
下面就是用 Matplotlib 製作動畫的例子。展示的是 John Conway 的 《The Game of Life》,這是一個 Metis(資料科學夏令營)中的程式設計挑戰題目,同時給了我一個機會來製作我的第一個 Python 動畫。看看結果的動圖:
這篇文章的重點還是主要放在 python 中如何用 Matploylib 製作動畫。
但如果你不太熟悉模擬遊戲的話(它更像是可以看的模擬動畫,而非可以玩的遊戲),我來給大家介紹一下規則:
- 一開始先設定一個 N×N 的網格(我的動畫中用的是 50×50 );
- 接著隨機地向格子中填充“小細胞”(一開始隨機地從 2500 個格子中選取 1500 個進行填充);
- 如果鄰居小細胞少於等於 1 個,那格子中的小細胞會死掉;
- 如果鄰居大於等於 4 個的也會死掉;
- 只有 2 個或 3 個鄰居時可以生存;
- 空的格子中如果正好有 3 個鄰居,則會長出 1 個新的“小細胞”;
—2—
建立網格
我們首先匯入所需的庫。
import time from IPython import display import matplotlib.pyplot as plt import matplotlib.animation as animation
我們會利用Matploylib 動畫模組中的 FuncAnimation() 函式。 FuncAnimation()是通過多次呼叫一個函式並逐次更新圖片來實現讓圖片動起來的。我們來一步步地實現這個過程。
但首先,我們需要先初始化我們的網格。下面的幾行程式碼用來儲存我們輸入的資料:
- 我們需要一個 50×50 大小的網格;
- pad 變數可以使得計算鄰居變得更容易。通過在邊界外新增一層空白格子,我們就不需要額外再寫一個邏輯來處理網格的邊界。因此我們 50×50 的網格其實是被一圈空白格子包圍著,這使得實際的 numpy 序列的大小為 52×52;
- initial_cels 變量表示在網格啟動的時候我們想要多少“小細胞”。他們會被隨機地分佈在網格上。
# Input variables for the board boardsize = 50 # board will be X by X where X = boardsize pad = 2 # padded border,do not change this! initial_cells = 1500 # this number of initial cells will be placed # in randomly generated positions
接下來我們隨機地生成一系列“小細胞”的初始座標(上面我們選擇了 1500 個)。把這些座標儲存在 pos_list 變數中。
# Get a list of random coordinates so that we can initialize # board with randomly placed organisms pos_list = [] for i in range(initial_cells): pos_list.append([random.randint(1,boardsize),random.randint(1,boardsize)])
然後我們是時候該初始化網格了。我們會用一組叫 my_board 的 numpy 序列來代表我們的網格——我們先生成一個 52×52 數值為 0 的矩陣序列作為開始(比 50×50 大是由於增加了空白邊緣),然後呼叫 init_board() 函式來根據 pos_list 中的座標把“小細胞”填充到網格中。輔助函式的具體細節我不再展開講了,不過我把他們都整理到我的 Github 上了。
# Initialize the board my_board = np.zeros((boardsize+pad,boardsize+pad)) my_board = init_board(pos_list,my_board)
—3—
製作網格動畫
這是我們最期待的部分——動畫!首先,我們需要完善一些配置。下面的幾行程式碼用來生成展示我們動畫的 mtplotlib 圖框。
# Required line for plotting the animation %matplotlib notebook # Initialize the plot of the board that will be used for animation fig = plt.gcf()
接下來製作我們的第一幀。 mtplotlib 中的 imshow() 函式可以接收一組 numpy 矩陣然後返回一張圖片。很酷吧!
# Show first image - which is the initial board im = plt.imshow(my_board) plt.show()
傳入 imshow() 的變數是我們的初始的網格 my_board。生成的圖片長這樣:
現在我們需要寫一個可以給 FuncAnimation() 呼叫的輔助函式。 animate() 函式接受一幀畫面作為輸入充當計數器。這個畫面計數器就是 FuncAnimation() 和 animate() 函式溝通的橋樑——在每一個時間點(也就是每一幀),它都會呼叫一次 animate()。然後 animate() 會逐次使用輔助函式 update_board() 來對網格進行迭代。最後, set_data() 函式將圖片更新為迭代後的網格,這就完成了。
# Helper function that updates the board and returns a new image of # the updated board animate is the function that FuncAnimation calls def animate(frame): im.set_data(update_board(my_board)) return im,
一切順利!我們準備呼叫 FuncAnimation() 函數了。注意輸入的引數:
- fig 是我們在前面建立的用來裝載我們的動畫的圖形變數;
- animate 是 FuncAnimation() 用畫面計數器進行溝通的函式(自動傳入,不需要特別宣告)
- frames 表示我們希望動畫持續多少幀,在這裡我們想要動畫的長度為 200 幀;
- interval 表示每一幀之間間隔的毫秒數。我們想要每幀之間間隔 50 毫秒。
# This line creates the animation anim = animation.FuncAnimation(fig,animate,frames=200,interval=50)
就這麼簡單!不是很難吧?為了慶祝我們成功製作動畫,我再送大家一個動畫:
—4—
總結
希望這篇文章能幫到大家。在結束之前,讓我來幫助大家腦補更多我們今天學到的動畫功能在資料科學上的應用:
- 一個個地畫出蒙特卡洛模擬資料,你能觀察到最終的分佈是如何逐步形成的;
- 按順序遍歷時間序列資料,可以描繪你的模型或資料在新的觀察角度下有什麼表現;
- 當你改變輸入引數時,比如族群數,可以展現你的演算法是如何劃分族群的;
- 根據時間或不同的資料子集生成關聯熱力圖,用於觀察不同的樣本是如何影響你的模型的預期引數的。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。