python繪製動態模擬圖

動圖

很多時候我們繪圖不僅僅是繪製最終的統計結果圖,而是想看看在不同參數不同時刻下的連續圖形,這個在仿真模擬的時候相當有用。比如機器學習中,參數的變化導致的變化,比如我最近做的庫存水平變化模擬等。如果我們繪製靜態圖像,只能看到某一時刻的直觀圖形,如果加入時間線,那麼就能直觀感受變量的變化過程。

其實原理也挺簡單的,就是創建一幅圖,定義圖形中曲線,散點,標註等各個對象,然後在不同時刻,更新這些對象的數據,matplotlib會自動根據新的數據刷新圖形。

動圖的核心函數是matplotlib.animation.FuncAnimation,基本用法是:

anim = animation.funcanimation(fig, animate, init_func=init, frames=100, interval=20, blit=true)
# fig: 是我們創建的畫布
# animat: 是重點,是我們每個時刻要更新圖形對象的函數,返回值和init_func相同
# init_func: 初始化函數,其返回值就是每次都要更新的對象,
#    告訴FuncAnimation在不同時刻要更新哪些圖形對象
# frames: 相當於時刻t,要模擬多少幀圖畫,不同時刻的t相當於animat的參數
# interval: 刷新頻率,毫秒
# blit: blit是一個非常重要的關鍵字,它告訴動畫只重繪修改的部分,結合上面保存的時間,
#    blit=true會使動畫顯示得會非常非常快

下面通過幾個例子來說明使用方法。

例子1

第一個例子參考:
http://codingpy.com/article/drawing-gifs-with-matplotlib/?utm_source=tuicool&utm_medium=referral
該文繪製不同參數狀態下的圖形,很有意思,這裏簡化一些代碼,順便增加更多註釋。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation  # 動圖的核心函數
import seaborn as sns  # 美化圖形的一個繪圖包

sns.set_style("whitegrid")  # 設置圖形主圖

# 創建畫布
fig, ax = plt.subplots()
fig.set_tight_layout(True)

# 畫出一個維持不變(不會被重畫)的散點圖和一開始的那條直線。
x = np.arange(0, 20, 0.1)
ax.scatter(x, x + np.random.normal(0, 3.0, len(x)))
line, = ax.plot(x, x - 5, 'r-', linewidth=2)

def update(i):
    label = 'timestep {0}'.format(i)
    print(label)
    # 更新直線和x軸(用一個新的x軸的標籤)。
    # 用元組(Tuple)的形式返回在這一幀要被重新繪圖的物體
    line.set_ydata(x - 5 + i)  # 這裏是重點,更新y軸的數據
    ax.set_xlabel(label)    # 這裏是重點,更新x軸的標籤
    return line, ax

# FuncAnimation 會在每一幀都調用“update” 函數。
# 在這裏設置一個10幀的動畫,每幀之間間隔200毫秒
anim = FuncAnimation(fig, update, frames=np.arange(0, 10), interval=200)

這裏寫圖片描述

例子2

爲了代碼更加通用,我們繪製上下兩個子圖,每個子圖繪製正弦波和散點圖的組合。
重點是散點圖的繪製,線圖和例子1一樣,都是更新x,y軸數據即可。
散點圖用 set_offsets(data)函數,而且data的數據格式是[[x1,y1],[x2,y2]...],而不像直線,其數據格式是x軸的放在一起,y軸的放在一起。

from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
import seaborn as sns
sns.set_style("whitegrid")


def randn_point():
    # 產生隨機散點圖的x和y數據
    x=np.random.randint(1,100,3)
    y=np.random.randint(1,2,3)
    return x,y

# 創建畫布,包含2個子圖
fig = plt.figure(figsize=(15, 10))
ax1 = fig.add_subplot(2, 1, 1)
ax2 = fig.add_subplot(2, 1, 2)

# 先繪製初始圖形,每個子圖包含1個正弦波和三個點的散點圖
x = np.arange(0, 2*np.pi, 0.01)

line1, = ax1.plot(x, np.sin(x)) # 正弦波
x1,y1=randn_point()
sca1 = ax1.scatter(x1,y1)   # 散點圖

line2, = ax2.plot(x, np.cos(x))  # 餘弦波
x2,y2=randn_point()
sca2 = ax2.scatter(x2,y2)   # 散點圖

def init():
    # 構造開始幀函數init
    # 改變y軸數據,x軸不需要改
    line1.set_ydata(np.sin(x))
    line1.set_ydata(np.cos(x))
    # 改變散點圖數據
    x1, y1 = randn_point()
    x2, y2 = randn_point()
    data1 = [[x,y] for x,y in zip(x1,y1)]
    data2 = [[x, y] for x, y in zip(x2, y2)]
    sca1.set_offsets(data1)  # 散點圖
    sca2.set_offsets(data2)  # 散點圖
    label = 'timestep {0}'.format(0)
    ax1.set_xlabel(label)
    return line1,line2,sca1,sca2,ax1  # 注意返回值,我們要更新的就是這些數據

def animate(i):
    # 接着,構造自定義動畫函數animate,用來更新每一幀上各個x對應的y座標值,參數表示第i幀
    # plt.cla() 這個函數很有用,先記着它
    line1.set_ydata(np.sin(x + i/10.0))
    line2.set_ydata(np.cos(x + i / 10.0))
    x1, y1 = randn_point()
    x2, y2 = randn_point()
    data1 = [[x,y] for x,y in zip(x1,y1)]
    data2 = [[x, y] for x, y in zip(x2, y2)]
    sca1.set_offsets(data1)  # 散點圖
    sca2.set_offsets(data2)  # 散點圖
    label = 'timestep {0}'.format(i)
    ax1.set_xlabel(label)
    return line1,line2,sca1,sca2,ax1


# 接下來,我們調用FuncAnimation函數生成動畫。參數說明:
# fig 進行動畫繪製的figure
# func 自定義動畫函數,即傳入剛定義的函數animate
# frames 動畫長度,一次循環包含的幀數
# init_func 自定義開始幀,即傳入剛定義的函數init
# interval 更新頻率,以ms計
# blit 選擇更新所有點,還是僅更新產生變化的點。應選擇True,但mac用戶請選擇False,否則無法顯示動畫

ani = animation.FuncAnimation(fig=fig,
                              func=animate,
                              frames=100,
                              init_func=init,
                              interval=20,
                              blit=False)
plt.show()

這裏寫圖片描述

保存圖形

保存圖形要用到一個叫ImageMagick的工具,按照網上的參考文章,都是使用ImageMagick 6.9版本,第一次下載最新版,怎麼都沒找到convert模塊,好坑啊。
步驟:
1。下載安裝ImageMagick 6.9,安裝過程中選擇默認選項即可。
2。找到
"python_home\Lib\site-packages\matplotlib\mpl-data\matplotlibrc"
修改convert路徑
#animation.convert_path: 'convert'
–>
#animation.convert_path: 'C:\Program Files (x86)\ImageMagick-6.9.2-Q16\convert.exe'

在繪圖後增加一個save操作:

ani = animation.FuncAnimation(fig=fig,
                              func=animate,
                              frames=100,
                              init_func=init,
                              interval=20,
                              blit=False)
plt.show()
ani.save(r'D:\demoanimation.gif', writer='imagemagick', fps=2)

參考

使用Matplotlib製作動圖
http://blog.csdn.net/theonegis/article/details/51037850
如何用 Matplotlib 畫 GIF 動圖
https://www.tuicool.com/articles/Z7BzY3V
使用Matplotlib和Imagemagick實現算法可視化與GIF導出
http://www.hankcs.com/ml/using-matplotlib-and-imagemagick-to-realize-the-algorithm-visualization-and-gif-export.html
利用Matplotlib和ImageMagick製作gif動畫
http://blog.csdn.net/stereohomology/article/details/35845399
matplotlib裏的動畫
http://blog.csdn.net/riverflowrand/article/details/51189573
官方動畫教程
http://matplotlib.org/api/animation_api.html

發佈了61 篇原創文章 · 獲贊 163 · 訪問量 42萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章