『科學計算』科學繪圖庫matplotlib學習之繪制動畫


基礎

1.matplotlib繪圖函數接收兩個等長list,第一個作為集合x坐標,第二個作為集合y坐標

2.基本函數:

animation.FuncAnimation(fig, update_point,data)

  • fig是畫布
  • update是繪畫函數需自己定義,需要一個參數,會自動接收data,需要返回plt.plot對象,描述比較費解,看例子就好
  • data種類很多,包括總幀數(例1)、當前幀數(即不設定data的默認參數,例2)、返回迭代器的函數(例3)、list(作業2)
  • frames=200 總幀數(非update參數)
  • interval=20  幀間隔(毫秒)

示例代碼

簡單調用幀數繪圖

from matplotlib import pyplot as plt
import matplotlib.animation as animation
import numpy as np


def update_point(num):
    fig_points.set_data(data[:, 0:num])
    return fig_points,

fig1 = plt.figure()

num_point = 50
data = np.random.rand(2, num_point)
fig_points, = plt.plot([], [], 'ro')

plt.xlim(0, 1)
plt.ylim(0, 1)
plt.xlabel('x')
plt.title('Scatter Point')

# interval
# repeat
# frames
# fargs
# init_func
anim = animation.FuncAnimation(fig1, update_point,num_point)

#anim = animation.FuncAnimation(fig1, update_point,frames=num_point, interval=50, blit=False, repeat=False)

plt.show()

 

 

利用幀做參數繪制

這種方式每經過interval的時間后會調用函數(傳入當前幀號)繪制一幅新圖更新原圖:

  1. 建立子圖、空白線
  2. 創建動畫發生時調用的函數

    Init()是我們的動畫在在創建動畫基礎框架(base frame)時調用的函數。這里我們們用一個非常簡單的對line什么都不做的函數。這個函數一定要返回line對

    象,這個很重要,因為這樣就能告訴動畫之后要更新的內容,也就是動作的內容是line。--來自( http://mytrix.me/2013/08/matplotlib-animation-tutorial/

  3. 動畫函數

    在這個動畫函數中修改你的圖

  4. 調用函數生成動態圖

 

繪制正弦波函數:

可以使用多個線對象來同時更新多個子圖於同一個動畫生成器之中,不過這需要上面1~3步同時支持(就是寫出來)多個線對象

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation

# 1.First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0,2),ylim=(-2,2))
line, = ax.plot([],[],lw=2)


# 2.initialization function: plot the background of each frame
def init():
    line.set_data([],[])
    return line,


# 3.animation function.  This is called sequentially
# note: i is framenumber
def update(i):
    x = np.linspace(0,2,1000)
    y = np.sin(2 * np.pi * (x - 0.01 * i))  # 調整x相當於向右平移圖像
    line.set_data(x,y)
    return line,


# call the animator.  blit=True means only re-draw the parts that have changed.
# 畫布, 使用幀數做參數的繪制函數, init生成器.。
anim = animation.FuncAnimation(fig,update,init_func=init,frames=200,interval=20,blit=False)
# frames=200   幀數
# interval=20  間隔

# anim.save('anim3.mp4', fps=30, extra_args=['-vcodec', 'libx264'])      # 保存為mp4
# anim.save('anim3.gif', writer='imagemagick')                           # 保存為gif

plt.show()

 

迭代器繪制法

繪制衰減波

這里的update函數僅僅繪制圖像,數據生成被移植到函數data_gen中了,而上面的幀方法中update得到幀號后會自己生成數據。

 

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation


def data_gen():
    t = 0
    cnt = 0
    while cnt < 200:
        cnt += 1
        t += 0.1
        yield t,np.sin(2 * np.pi * t) * np.exp(-t / 10.)


def init():
    ax.set_ylim(-1.1,1.1)
    ax.set_xlim(0,10)
    line.set_data([],[])
    return line,


def update(datag):
    # update the data,默認接受后面的yield
    t,y = datag
    xdata.append(t)
    ydata.append(y)
    line.set_data(xdata,ydata)

    if max(xdata) > 10:
        ax.set_xlim(max(xdata) - 10,max(xdata))
    return line,


fig,ax = plt.subplots()
line, = ax.plot([],[],lw=2)
ax.grid()
xdata,ydata = [],[]

# 參數:{畫布,繪制圖像函數,更新數據函數,時間間隔,重復標識,初始化函數}
ani = animation.FuncAnimation(fig,update,data_gen,interval=10,repeat=False,init_func=init)
plt.show()

作業

冒泡排序算法動畫演示

使用迭代器更新方式:

參考:

  
import numpy as np  
import matplotlib.pyplot as plt  
import matplotlib.animation as animation  
  
fig = plt.figure()  
axes1 = fig.add_subplot(111)  
line, = axes1.plot(np.random.rand(10))  
  
#因為update的參數是調用函數data_gen,所以第一個默認參數不能是framenum  
def update(data):  
    line.set_ydata(data)  
    return line,  
# 每次生成10個隨機數據  
def data_gen():  
    while True:  
        yield np.random.rand(10)  
  
ani = animation.FuncAnimation(fig, update, data_gen, interval=2*1000)  
plt.show()  

代碼:

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation

fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot(np.zeros(9),'bo')  # 由於沒有使用init_fun,所以初始幀有數據,且尺寸與后續幀一致才行
ax.set_xlim(0,10)
ax.set_ylim(0,10)
ax.set_xticks(np.linspace(0,10,11))
ax.set_yticks(np.linspace(0,10,11))
ax.grid(True)

# update的參數是調用函數data_gen
def update(data):
    line.set_xdata(np.linspace(1,9,9))
    line.set_ydata(data)
    return line,

def data_gen():
    x = [9,8,3,1,2,4,6,5,7]
    for i in range(len(x)-1):
        for j in range(len(x)-1):
            if x[j]>x[j+1]:
                x[j],x[j+1]=x[j+1],x[j]
            yield(x)

# {畫布,繪制函數(數據函數yield),數據函數,幀間隔(毫秒),重復標識}
ani = animation.FuncAnimation(fig,update,data_gen,interval=1000,repeat=False)
plt.show()

 

快速排序算法動畫演示

使用數組更新方式:

  • 不把數據以迭代器的方式傳入,而是list,則每次會把list的子項依次傳入update。list形式是[ [list1], [list2], ... ... ]。
  • 另外使用了init_fun,使用這個函數了后不用繪制初始幀,即可以使用:line = ax.plot([],[],'go'),而不用指定初始化數據,但是這樣的話x軸數據update需要設定好才行(因為默認繼承初始幀的x軸)。

 

參考:

例子使用list(metric),每次會從metric中取一行數據作為參數送入update中

    import numpy as np  
    import matplotlib.pyplot as plt  
    import matplotlib.animation as animation  
      
    start = [1, 0.18, 0.63, 0.29, 0.03, 0.24, 0.86, 0.07, 0.58, 0]  
      
    metric =[[0.03, 0.86, 0.65, 0.34, 0.34, 0.02, 0.22, 0.74, 0.66, 0.65],  
             [0.43, 0.18, 0.63, 0.29, 0.03, 0.24, 0.86, 0.07, 0.58, 0.55],  
             [0.66, 0.75, 0.01, 0.94, 0.72, 0.77, 0.20, 0.66, 0.81, 0.52]  
            ]  
      
    fig = plt.figure()  
    window = fig.add_subplot(111)  
    line, = window.plot(start)  
    #如果是參數是list,則默認每次取list中的一個元素,即metric[0],metric[1],...  
    def update(data):  
        line.set_ydata(data)  
        return line,  
      
    ani = animation.FuncAnimation(fig, update, metric, interval=2*1000)  
    plt.show()  

代碼:

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import copy

obj = [49,38,65,97,76,13,27,49]
res = []

def fast_sort(ob,start,end):
    # print(ob,start,end)
    if  end - start <= 1:
        return
    # i,j,k = 0,(len(ob)-1),ob[0]
    i,j,k = start,end,ob[start]
    while j-i != 1:
        for j_r in range(j,i,-1):
            j = j_r
            if ob[j_r]<k:
                ob[i],ob[j_r] = ob[j_r],ob[i]
                break
        # print(ob,i,j)
        for i_r in range(i,j):
            i = i_r
            if ob[i_r]>k:
                ob[i_r],ob[j] = ob[j],ob[i_r]
                break
    res.append(copy.deepcopy(ob))  # 這里發現添加進res的list對象是淺拷貝,或者說向數據結構中添加的list都是淺拷貝,需要注意
    if ob[i] == k:
        fast_sort(ob,start,i-1)
        fast_sort(ob,i+1,end)
    else:
        fast_sort(ob,start,j-1)
        fast_sort(ob,j+1,end)

fast_sort(obj,0,(len(obj)-1))
print(res)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlim(0,8)
ax.set_ylim(0,100)
ax.set_xticks(np.linspace(0,8,9))
ax.set_yticks(np.linspace(0,100,11))
ax.grid()
line, = ax.plot([],[],'go')

def init():
    line.set_data([], [])
    return line,

def update(data):  # update函數參數默認接收其后的函數或是list
    line.set_xdata(np.linspace(0,7,8))
    line.set_ydata(data)
    return line

ani = animation.FuncAnimation(fig,update,res,init_func=init,interval=2*1000,repeat=False)

 冒泡排序&快速排序代碼

兩種排序具體定義自行百度,不過比較重要的一點是快速排序屬於遞歸過程,蠻好玩的。

冒泡排序

import numpy as np

ob = [9,8,3,1,2,4,6,5,7]
def oppo_sort(x):
    for i in range(len(x)-1):
        for j in range(len(x)-1):
            if x[j]>x[j+1]:
                x[j],x[j+1]=x[j+1],x[j]
            yield((i,j),x)
for t,obj in oppo_sort(ob):
    print(t,obj)
(0, 0) [8, 9, 3, 1, 2, 4, 6, 5, 7]
(0, 1) [8, 3, 9, 1, 2, 4, 6, 5, 7]
(0, 2) [8, 3, 1, 9, 2, 4, 6, 5, 7]
(0, 3) [8, 3, 1, 2, 9, 4, 6, 5, 7]
(0, 4) [8, 3, 1, 2, 4, 9, 6, 5, 7]
(0, 5) [8, 3, 1, 2, 4, 6, 9, 5, 7]
(0, 6) [8, 3, 1, 2, 4, 6, 5, 9, 7]
(0, 7) [8, 3, 1, 2, 4, 6, 5, 7, 9]
(1, 0) [3, 8, 1, 2, 4, 6, 5, 7, 9]
(1, 1) [3, 1, 8, 2, 4, 6, 5, 7, 9]
(1, 2) [3, 1, 2, 8, 4, 6, 5, 7, 9]
(1, 3) [3, 1, 2, 4, 8, 6, 5, 7, 9]
(1, 4) [3, 1, 2, 4, 6, 8, 5, 7, 9]
(1, 5) [3, 1, 2, 4, 6, 5, 8, 7, 9]
(1, 6) [3, 1, 2, 4, 6, 5, 7, 8, 9]
(1, 7) [3, 1, 2, 4, 6, 5, 7, 8, 9]
(2, 0) [1, 3, 2, 4, 6, 5, 7, 8, 9]
(2, 1) [1, 2, 3, 4, 6, 5, 7, 8, 9]
(2, 2) [1, 2, 3, 4, 6, 5, 7, 8, 9]
(2, 3) [1, 2, 3, 4, 6, 5, 7, 8, 9]
(2, 4) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(2, 5) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(2, 6) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(2, 7) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(3, 0) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(3, 1) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(3, 2) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(3, 3) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(3, 4) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(3, 5) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(3, 6) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(3, 7) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(4, 0) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(4, 1) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(4, 2) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(4, 3) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(4, 4) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(4, 5) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(4, 6) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(4, 7) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(5, 0) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(5, 1) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(5, 2) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(5, 3) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(5, 4) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(5, 5) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(5, 6) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(5, 7) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(6, 0) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(6, 1) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(6, 2) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(6, 3) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(6, 4) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(6, 5) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(6, 6) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(6, 7) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(7, 0) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(7, 1) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(7, 2) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(7, 3) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(7, 4) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(7, 5) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(7, 6) [1, 2, 3, 4, 5, 6, 7, 8, 9]
(7, 7) [1, 2, 3, 4, 5, 6, 7, 8, 9]

快速排序

有坑,不僅是因為快速排序本身比較復雜的原因,由於python的拷貝機制,實現起來也有一些注意的地方,注意在探究python拷貝機制的時候id()是個有用的函數。

 

本段方法出錯了,原因試python在給函數傳入list的時候按照是淺拷貝的(即對同一id操作),但是,一旦傳入的是切片,那么就會分配id進行深拷貝,其實不難理解,畢竟對於一個函數可以看作一個較為獨立的節點,其中的元素都要有自己完整的標識,即自己的id:

obj = [49,38,65,97,76,13,27,49]

def fast_sort(ob):
    # print(id(ob))
    if len(ob) <= 1:
        return
    i,j,k = 0,(len(ob)-1),ob[0]
    while j-i != 1:
        for j_r in range(j,i,-1):
            j = j_r
            if ob[j_r]<k:
                ob[i],ob[j_r] = ob[j_r],ob[i]
                break
        # print(ob,i,j)
        for i_r in range(i,j):
            i = i_r
            if ob[i_r]>k:
                ob[i_r],ob[j] = ob[j],ob[i_r]
                break
        print(ob)
    if ob[i] == k:
        fast_sort(ob[:i])
        fast_sort(ob[i+1:])
    else:
        fast_sort(ob[:j])
        fast_sort(ob[j+1:])

fast_sort(obj)
[27, 38, 49, 97, 76, 13, 65, 49]
[27, 38, 13, 49, 76, 97, 65, 49]
[27, 38, 13, 49, 76, 97, 65, 49]
[13, 27, 38]
[49, 76, 65, 97]
[49, 65, 76, 97]

可以清晰地驗明上述結論:

ls = [1,2,3,4,5]
i = 0

def print_id(l1,l2):
    global i
    i += 1
    if i == 10:return
    print(id(l1),'  ',id(l2))
    print_id(l1,l2[1:2])

print_id(ls,ls)
2424669584200    2424669584200
2424669584200    2424669684168
2424669584200    2424669616008
2424669584200    2424669615880
2424669584200    2424669614984
2424669584200    2424669548040
2424669584200    2424669614792
2424669584200    2424669584712
2424669584200    2424669497992

python凡是涉及到修改數據結構都應該遵循如下方式:傳入原始數據結構,傳入索引,在函數內部使用索引修改數據結構而不是直接傳入數據結構的一部分,下面是正確的寫法。

順便注意一下遞歸函數的寫法:終止判斷+主函數體+遞歸調用分部

import copy
obj = [49,38,65,97,76,13,27,49]
res=[]
def fast_sort(ob,start,end):
    # print(ob,start,end)
    if  end - start <= 1:
        return
    # i,j,k = 0,(len(ob)-1),ob[0]
    i,j,k = start,end,ob[start]
    while j-i != 1:
        for j_r in range(j,i,-1):
            j = j_r
            if ob[j_r]<k:
                ob[i],ob[j_r] = ob[j_r],ob[i]
                break
        # print(ob,i,j)
        for i_r in range(i,j):
            i = i_r
            if ob[i_r]>k:
                ob[i_r],ob[j] = ob[j],ob[i_r]
                break
    print(ob)
    res.append(copy.deepcopy(ob)) # 注意深拷貝
    print(res)
    if ob[i] == k:
        fast_sort(ob,start,i-1)
        fast_sort(ob,i+1,end)
    else:
        fast_sort(ob,start,j-1)
        fast_sort(ob,j+1,end)
        
fast_sort(obj,0,(len(obj)-1))
print(obj)
[27, 38, 13, 49, 76, 97, 65, 49]
[[27, 38, 13, 49, 76, 97, 65, 49]]
[13, 27, 38, 49, 76, 97, 65, 49]
[[27, 38, 13, 49, 76, 97, 65, 49], [13, 27, 38, 49, 76, 97, 65, 49]]
[13, 27, 38, 49, 49, 65, 76, 97]
[[27, 38, 13, 49, 76, 97, 65, 49], [13, 27, 38, 49, 76, 97, 65, 49], [13, 27, 38, 49, 49, 65, 76, 97]]
[13, 27, 38, 49, 49, 65, 76, 97]

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM