web-dev-qa-db-ja.com

matplotlib.animateを使用してpython

データの3D配列(2つの空間次元と1つの時間次元)があり、matplotlib.animateを使用してアニメーションの等高線図を作成しようとしています。私はこのリンクを基礎として使用しています:

http://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/

そして、これが私の試みです:

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from numpy import array, zeros, linspace, meshgrid
from boutdata import collect

# First collect data from files
n = collect("n")   #  This is a routine to collect data
Nx = n.shape[1]
Nz = n.shape[2]
Ny = n.shape[3]
Nt = n.shape[0]

fig = plt.figure()
ax = plt.axes(xlim=(0, 200), ylim=(0, 100))
cont, = ax.contourf([], [], [], 500)

# initialisation function
def init():
    cont.set_data([],[],[])
    return cont,

# animation function
def animate(i): 
    x = linspace(0, 200, Nx)
    y = linspace(0, 100, Ny)
    x,y = meshgrid(x,y)
    z = n[i,:,0,:].T
    cont.set_data(x,y,z)
    return cont, 

anim = animation.FuncAnimation(fig, animate, init_func=init,
                           frames=200, interval=20, blit=True)

plt.show()

しかし、これを行うと、次のエラーが発生します。

Traceback (most recent call last):
  File "showdata.py", line 16, in <module>
    cont, = ax.contourf([], [], [], 500)
  File "/usr/lib/pymodules/python2.7/matplotlib/axes.py", line 7387, in contourf
    return mcontour.QuadContourSet(self, *args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 1112, in __init__
    ContourSet.__init__(self, ax, *args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 703, in __init__
    self._process_args(*args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 1125, in _process_args
    x, y, z = self._contour_args(args, kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 1172, in _contour_args
    x,y,z = self._check_xyz(args[:3], kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 1204, in _check_xyz
    raise TypeError("Input z must be a 2D array.")
TypeError: Input z must be a 2D array.

したがって、すべての[]を[[]、[]]に置き換えようとしましたが、次のようになります。

Traceback (most recent call last):
  File "showdata.py", line 16, in <module>
    cont, = ax.contourf([[],[]], [[],[]], [[],[]],500)
  File "/usr/lib/pymodules/python2.7/matplotlib/axes.py", line 7387, in contourf
    return mcontour.QuadContourSet(self, *args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 1112, in __init__
    ContourSet.__init__(self, ax, *args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 703, in __init__
    self._process_args(*args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 1125, in _process_args
    x, y, z = self._contour_args(args, kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/contour.py", line 1177, in _contour_args
    self.zmax = ma.maximum(z)
  File "/usr/lib/python2.7/dist-packages/numpy/ma/core.py", line 5806, in __call__
    return self.reduce(a)
  File "/usr/lib/python2.7/dist-packages/numpy/ma/core.py", line 5824, in reduce
    t = self.ufunc.reduce(target, **kargs)
ValueError: zero-size array to maximum.reduce without identity

前もって感謝します!

19
Luke

これは私が仕事に取り掛かったものです:

# Generate grid for plotting
x = linspace(0, Lx, Nx)
y = linspace(0, Ly, Ny)
x,y = meshgrid(x,y)

fig = plt.figure()
ax = plt.axes(xlim=(0, Lx), ylim=(0, Ly))  
plt.xlabel(r'x')
plt.ylabel(r'y')

# animation function
def animate(i): 
    z = var[i,:,0,:].T
    cont = plt.contourf(x, y, z, 25)
    if (tslice == 0):
        plt.title(r't = %1.2e' % t[i] )
    else:
        plt.title(r't = %i' % i)

    return cont  

anim = animation.FuncAnimation(fig, animate, frames=Nt)

anim.save('animation.mp4')

FuncAnimation呼び出しでblit = 0引数を削除することも役立つことがわかりました...

10
Luke

Felix Schneiderは、アニメーションが非常に遅くなることについて正しいです。 _ax.collections = []_を設定する彼の解決策は、すべての古い(そして置き換えられた)「アーティスト」を削除します。より外科的なアプローチは、輪郭の描画に関与しているアーティストのみを削除することです。

_for c in cont.collections:
    c.remove()
_

これは、フレームごとに図全体を再構築する代わりに、より複雑な場合に役立ちます。これは、RehmanALiの例でも機能します。図全体をclf()でクリアする代わりに、contourf()によって返された値が保存され、次の反復で使用されます。

これは、2013年6月7日のLukeに似たコード例で、輪郭のみを削除する方法を示しています。

_import pylab as plt
import numpy
import matplotlib.animation as animation
#plt.rcParams['animation.ffmpeg_path'] = r"C:\some_path\ffmpeg.exe"   # if necessary

# Generate data for plotting
Lx = Ly = 3
Nx = Ny = 11
Nt = 20
x = numpy.linspace(0, Lx, Nx)
y = numpy.linspace(0, Ly, Ny)
x,y = numpy.meshgrid(x,y)
z0 = numpy.exp(-(x-Lx/2)**2-(y-Ly/2)**2)   # 2 dimensional Gaussian

def some_data(i):   # function returns a 2D data array
    return z0 * (i/Nt)

fig = plt.figure()
ax = plt.axes(xlim=(0, Lx), ylim=(0, Ly), xlabel='x', ylabel='y')

cvals = numpy.linspace(0,1,Nt+1)      # set contour values 
cont = plt.contourf(x, y, some_data(0), cvals)    # first image on screen
plt.colorbar()

# animation function
def animate(i):
    global cont
    z = some_data(i)
    for c in cont.collections:
        c.remove()  # removes only the contours, leaves the rest intact
    cont = plt.contourf(x, y, z, cvals)
    plt.title('t = %i:  %.2f' % (i,z[5,5]))
    return cont

anim = animation.FuncAnimation(fig, animate, frames=Nt, repeat=False)
anim.save('animation.mp4', writer=animation.FFMpegWriter())
_
9
Patrick Pribyl

これは次の行です。

cont, = ax.contourf([], [], [], 500)

への変更:

 x = linspace(0, 200, Nx)
 y = linspace(0, 100, Ny)
 x, y = meshgrid(x, y)
 z = n[i,:,0,:].T
 cont, = ax.contourf(x, y, z, 500)

サイズの大きい配列を使用する必要があります。

5
Mike Müller

Matplotlib.animationが機能しない場合に、同じことを行う別の方法を次に示します。図のカラーバーとその他すべてを継続的に更新する場合は、最初にplt.ion()を使用してインタラクティブなプロットを有効にし、plt.draw()とplt.clf()の組み合わせを使用してプロットを継続的に更新します。 。

import matplotlib.pyplot as plt
import numpy as np

plt.ion(); plt.figure(1);
for k in range(10):
    plt.clf(); plt.subplot(121);
    plt.contourf(np.random.randn(10,10)); plt.colorbar();
    plt.subplot(122,polar=True)
    plt.contourf(np.random.randn(10,10)); plt.colorbar();
    plt.draw();

これは、さまざまなサブプロットとさまざまなタイプのプロット(つまり、極座標またはデカルト)を含む図で機能することに注意してください。

3
Rehman Ali

私はしばらく前にこれを見てきました。私の状況では、アニメートしたい等高線のサブプロットがいくつかありました。 Rehman ALiが提案するようにplt.clf()ソリューションを使用したくありませんでした。これは、軸の特別な設定(piシンボルなど)も使用したため、「remove()」アプローチの提案を優先しました。フェリックスになりなさい。重要なのは、「remove」を使用するだけではメモリがクリーンアップされず、最終的にコンピュータが詰まるため、輪郭を空のリストに設定して明示的に削除する必要があるということです。

テキストだけでなく輪郭も取り除くことができる一般的な削除ルーチンを作成するために、すべての軸のすべてのタイムステップで使用する必要があるルーチン「clean_up_artists」を作成しました。

このルーチンは、指定された軸「軸」の「artist_list」というリストで渡されたアーティストをクリーンアップします。つまり、複数のサブプロットをアニメーション化するには、各軸のアーティストのリストを保存する必要があり、タイムステップごとにクリーンアップする必要があります。

ランダムデータのいくつかのサブプロットをアニメーション化するための完全なコードの下。それはかなり自明なので、うまくいけば何が起こるかが明らかになるでしょう。とにかく、スタックオーバーフローで見つけたいくつかのアイデアを組み合わせて、この実用的な例を思いついたので、投稿しようと思いました。

コードを改善するための提案がある人は、撃ってください-)

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


def clean_up_artists(axis, artist_list):
    """
    try to remove the artists stored in the artist list belonging to the 'axis'.
    :param axis: clean artists belonging to these axis
    :param artist_list: list of artist to remove
    :return: nothing
    """
    for artist in artist_list:
        try:
            # fist attempt: try to remove collection of contours for instance
            while artist.collections:
                for col in artist.collections:
                    artist.collections.remove(col)
                    try:
                        axis.collections.remove(col)
                    except ValueError:
                        pass

                artist.collections = []
                axis.collections = []
        except AttributeError:
            pass

        # second attempt, try to remove the text
        try:
            artist.remove()
        except (AttributeError, ValueError):
            pass


def update_plot(frame_index, data_list, fig, axis, n_cols, n_rows, number_of_contour_levels, v_min, v_max,
                changed_artists):
    """
    Update the the contour plots of the time step 'frame_index'

    :param frame_index: integer required by animation running from 0 to n_frames -1. For initialisation of the plot,
    call 'update_plot' with frame_index = -1
    :param data_list: list with the 3D data (time x 2D data) per subplot
    :param fig: reference to the figure
    :param axis: reference to the list of axis with the axes per subplot
    :param n_cols: number of subplot in horizontal direction
    :param n_rows: number of subplot in vertical direction
    :param number_of_contour_levels: number of contour levels
    :param v_min: minimum global data value. If None, take the smallest data value in the 2d data set
    :param v_max: maximum global data value. If None, take the largest value in the 2d data set
    :param changed_artists: list of lists of artists which need to be updated between the time steps
    :return: the changed_artists list
    """

    nr_subplot = 0  # keep the index of the current subplot  (nr_subplot = 0,1,  n_cols x n_rows -1)
    # loop over the subplots
    for j_col in range(n_cols):
        for i_row in range(n_rows):

            # set a short reference to the current axis
            ax = axis[i_row][j_col]

            # for the first setup call, add and empty list which can hold the artists belonging to the current axis
            if frame_index < 0:
                # initialise the changed artist list
                changed_artists.append(list())
            else:
                # for the next calls of update_plot, remove all artists in the list stored in changed_artists[nr_subplot]
                clean_up_artists(ax, changed_artists[nr_subplot])

            # get a reference to 2d data of the current time and subplot
            data_2d = data_list[nr_subplot][frame_index]

            # manually set the levels for better contour range control
            if v_min is None:
                data_min = np.nanmin(data_2d)
            else:
                data_min = v_min
            if v_max is None:
                data_max = np.nanmax(data_2d)
            else:
                data_max = v_max

            # set the contour levels belonging to this subplot
            levels = np.linspace(data_min, data_max, number_of_contour_levels + 1, endpoint=True)

            # create the contour plot
            cs = ax.contourf(data_2d, levels=levels, cmap=cm.Rainbow, zorder=0)
            cs.cmap.set_under("k")
            cs.cmap.set_over("k")
            cs.set_clim(v_min, v_max)

            # store the contours artists to the list of artists belonging to the current axis
            changed_artists[nr_subplot].append(cs)

            # set some grid lines on top of the contours
            ax.xaxis.grid(True, zorder=0, color="black", linewidth=0.5, linestyle='--')
            ax.yaxis.grid(True, zorder=0, color="black", linewidth=0.5, linestyle='--')

            # set the x and y label on the bottom row and left column respectively
            if i_row == n_rows - 1:
                ax.set_xlabel(r"Index i ")
            if j_col == 0:
                ax.set_ylabel(r"Index j")

            # set the changing time counter in the top left subplot
            if i_row == 0 and j_col == 1:
                # set a label to show the current time
                time_text = ax.text(0.6, 1.15, "{}".format("Time index : {:4d}".format(frame_index)),
                                    transform=ax.transAxes, fontdict=dict(color="black", size=14))

                # store the artist of this label in the changed artist list
                changed_artists[nr_subplot].append(time_text)

            # for the initialisation call only, set of a contour bar
            if frame_index < 0:
                # the first time we add this  (make sure to pass -1 for the frame_index
                cbar = fig.colorbar(cs, ax=ax)
                cbar.ax.set_ylabel("Random number {}".format(nr_subplot))
                ax.text(0.0, 1.02, "{}) {}".format(string.ascii_lowercase[nr_subplot],
                                                   "Random noise {}/{}".format(i_row, j_col)),
                                         transform=ax.transAxes, fontdict=dict(color="blue", size=12))

            nr_subplot += 1

    return changed_artists


def main():
    n_pixels_x = 50
    n_pixels_y = 30
    number_of_time_steps = 100
    number_of_contour_levels = 10
    delay_of_frames = 1000
    n_rows = 3  # number of subplot rows
    n_cols = 2  # number of subplot columns

    min_data_value = 0.0
    max_data_value = 1.0

    # list containing the random plot per sub plot. Insert you own data here
    data_list = list()
    for j_col in range(n_cols):
        for i_row in range(n_rows):
            data_list.append(np.random.random_sample((number_of_time_steps, n_pixels_x, n_pixels_y)))

    # set up the figure with the axis
    fig, axis = plt.subplots(nrows=n_rows, ncols=n_cols, sharex=True, sharey=True, figsize=(12,8))
    fig.subplots_adjust(wspace=0.05, left=0.08, right=0.98)

    # a list used to store the reference to the axis of each subplot with a list of artists which belong to this subplot
    # this list will be returned and will be updated every time plot which new artists
    changed_artists = list()

    # create first image by calling update_plot with frame_index = -1
    changed_artists = update_plot(-1, data_list, fig, axis, n_cols, n_rows, number_of_contour_levels,
                                                 min_data_value, max_data_value, changed_artists)

    # call the animation function. The fargs argument equals the parameter list of update_plot, except the
    # 'frame_index' parameter.
    ani = animation.FuncAnimation(fig, update_plot, frames=number_of_time_steps,
                                  fargs=(data_list, fig, axis, n_cols, n_rows, number_of_contour_levels, min_data_value,
                                         max_data_value, changed_artists),
                                  interval=delay_of_frames, blit=False, repeat=True)

    plt.show()

if __name__ == "__main__":
    main()
1
Eelco van Vliet

FuncAnimation呼び出しでblit = 0またはblit = True引数を削除することも重要です!!!

0
Qina Yan

私はルークスアプローチを使用しました(2013年6月7日8:08から)が、追加しました

ax.collections = [] 

直前

cont = plt.contourf(x, y, z, 25).

そうしないと、フレーム数が多いとアニメーションの作成が非常に遅くなることを経験しました。

0
Felix Schneider