Выкарыстанне matplotlib.animate аніміраваць контур ўчастка ў пітона

У мяне ёсць 3D масіў дадзеных (2 прасторавых вымярэнняў і 1 вымярэння часу), і я спрабую вырабіць аніміраваны контур ўчастка з выкарыстаннем matplotlib.animate. Я выкарыстоўваю гэтую спасылку ў якасці асновы:

http://jakevdp.github.io/blog/2012/ 08/18/Matplotlib-анімацыя-падручнік/

І вось мая спроба:

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

Загадзя дзякую!

10

7 адказы

Гэта тое, што я павінен працаваць:

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

Я выявіў, што выдаленне Blit = 0 аргумент ў выкліку FuncAnimation таксама дапамог ...

7
дададзена

Гэта лінія:

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)

Вы павінны intilize з памерамі масіваў.

4
дададзена
Дакладна. Проста змесціце дадзеныя з самага пачатку. У сусле, што здараецца, што вы змесціце іх у два раз. Ці ёсць у любым выпадку не пашкодзіць вам часта ў анімацыі. Абноўлены адказ.
дададзена аўтар Mike Müller, крыніца
@Luke Ці вы прымусіць яго працаваць?
дададзена аўтар Mike Müller, крыніца
Гэй, дзякуй за адказ, але я цяпер атрымліваю гэтую памылку з адной і той жа лініі: TypeError: Даўжыня х павінна быць лік слупкоў у г, а даўжыня ў павінна быць колькасць радкоў.
дададзена аўтар Luke, крыніца

Фелікс Шнайдер правільна аб анімацыі становіцца вельмі павольным. Яго рашэнне ўстаноўкі ax.collections = [] выдаляе ўсе старыя (і перакрытыя) «мастак» с. Больш хірургічны падыход з'яўляецца толькі выдаліць мастакоў, якія ўдзельнічаюць у чарцяжы контураў:

for c in cont.collections:
    c.remove()

які карысны ў больш складаных выпадках, замест аднаўлення ўсёй фігуры для кожнага кадра. Гэта таксама працуе ў прыкладзе Рэхман Алі; замест ачысткі усю постаць з CLF() значэнне, якое вяртаецца contourf() захоўваецца і выкарыстоўваецца ў наступнай ітэрацыі.

Вось прыклад кода падобны на Люка ад 7 чэр '13, дэманструючы выдаленне толькі контуры:

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())
4
дададзена

Вось яшчэ адзін спосаб зрабіць тое ж самае, калі matplotlib.animation не працуе для вас. Калі вы хочаце, каб пастаянна абнаўляць Colorbar і ўсё астатняе на малюнку, выкарыстоўвайце 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();

Звярніце ўвагу, што гэта працуе з фігурамі, якія змяшчаюць розныя подзаговоры і розныя тыпы участкаў (г.зн. палярным або декартовых)

2
дададзена

Я глядзеў на гэта некаторы час таму. Я мая сітуацыя ў мяне была некалькі сюжэтных ліній з контурамі, якія я хацеў ажывіць. Я не хачу, каб выкарыстоўваць рашэнне plt.clf (), як Рэхман алі мяркуе, як я выкарыстаў некаторыя спецыяльныя налады маёй восі (з пі сімваламі і г.д.), якія будуць ачышчаныя, як добра, так што я аддаваў перавагу "выдаліць() падыход прапануе быць Felix. Справа ў тым, што толькі з дапамогай «выдаліць» не ачышчае памяць і засмечваюць ваш кампутар у рэшце рэшт, так што вам трэба відавочна выдаліць з контураў, усталяваўшы яго ў пусты спіс.

Для таго, каб мець агульны выдаліць падпраграму, якая здольная адымаць контуры, а таксама тэкст, я напісаў руцінныя «clean_up_artists», які вы павінны выкарыстоўваць на кожным кроку па часе па ўсёй восі.

Гэтая працэдура ачышчае мастакоў, якія перадаюцца ў спісе пад назвай «artist_list» у «восі» дадзенай восі. Гэта азначае, што для анімацыі некалькіх сюжэтных ліній, мы павінны захоўваць спісы мастакоў для кожнай восі, мы павінны ачысціць кожны крок па часе.

Ніжэй прыведзены поўны код для анімацыі шэрагу сюжэтных выпадковых дадзеных. Гэта даволі відавочна, так што спадзяюся, становіцца зразумела, што адбываецца. Ва ўсякім выпадку, я проста падумаў, каб размясціць яго, так як ён спалучае ў сабе некалькі ідэй, якія я знайшоў на перапаўненні стэка, які я проста, каб прыдумаць з гэтым працоўным прыкладам.

Хто-небудзь з прапановамі па паляпшэнні кода, калі ласка shoot-)

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
дададзена

Выдаленне Blit = 0 або Blit = True аргумент ў выкліку FuncAnimation таксама дапамог Важна !!!

0
дададзена

Я выкарыстаў Lukes падыход (ад 7 '13 чэрвеня у 8:08), але дадаў

ax.collections = [] 

перад

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

У адваротным выпадку я адчуў, што стварэнне анімацыі будзе вельмі павольным для вялікай колькасці кадраў.

0
дададзена