我有一个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
提前致谢!
答案 0 :(得分:10)
这就是我的工作:
# 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参数也有帮助......
答案 1 :(得分:9)
费利克斯施耐德对动画变得非常慢很正确。他设置ax.collections = []
的解决方案删除了所有旧的(和被取代的)&#34;艺术家&#34; s。更具手术性的方法是仅删除参与绘制轮廓的艺术家:
for c in cont.collections:
c.remove()
在更复杂的情况下是有用的,而不是为每个帧重建整个图形。这也适用于Rehman Ali的例子;而不是使用clf()
清除整个数字,contourf()
返回的值将被保存并在下一次迭代中使用。
这是一个类似于6月7日和13日的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())
答案 2 :(得分:5)
这是一行:
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)
您需要使用大小的数组进行插入。
答案 3 :(得分:3)
如果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();
请注意,这适用于包含不同子图和各种类型图(即极地或笛卡儿)的图形
答案 4 :(得分:1)
我刚才看过这个。我的情况我有一些带有轮廓的子图,我想要制作动画。我不想使用plh.clf()解决方案,正如Rehman ali建议的那样,我使用了一些特殊的轴设置(带有pi符号等)也会被清理,所以我更喜欢&#39; remove() &#39;方法建议是菲利克斯。问题是只使用&#39;删除&#39;不会清理内存并最终堵塞您的计算机,因此您需要通过将轮廓设置为空列表来明确删除轮廓。
为了有一个能够带走轮廓和文本的通用删除例程,我写了例程&#39; clean_up_artists&#39;你应该在所有轴上的每个时间步上使用它。
这个例程清理了名为&#39; artist_list&#39;的列表中传递的艺术家。在一个给定的轴&#39;轴上。这意味着,为了动画化多个子图,我们需要为每个轴存储艺术家列表,我们需要每个时间步清洁它们。
在完整代码下方为一些随机数据子图设置动画。这是非常不言自明的,所以希望它变得清晰。无论如何,我只是想发布它,因为它结合了我在堆栈溢出时发现的几个想法,我只是想出这个工作示例。
任何有改进代码建议的人,请拍 - )
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()
答案 5 :(得分:0)
在FuncAnimation调用中删除blit = 0或blit = True参数也有帮助 很重要!!!
答案 6 :(得分:0)
我使用了Lukes方法(从6月7日到9月13日在8:08),但添加了
ax.collections = []
之前
cont = plt.contourf(x, y, z, 25).
否则我经历过,对于大帧数,创建动画会变得很慢。