matplotlib save every n steps for animation - python

I'm simulating something with many time step, and there are a lot of steps involved. I would like to animate my results, plotting only one step every n.
Right now i have two non working propositions. The first one doesn't seem to loop correctly, and the second still fills the video with steps that are not update in the figure, making the file large and slow.
Can you help me ?
Thanks
X, Y = np.meshgrid(256,256)
fig,ax = plt.subplots()
plot_every = 50
saved_steps = []
def make_step(s, t):
c = ...
if (s%plot_every) == 0:
print("plotting step {:.0f} , t ={:.0f} ".format(s, t*dt))
ax.clear()
ax.contourf(X, Y, c,
np.arange(0, 1.0, 0.01),
extend='both')
saved_steps.append(ax)
for s , t in enumerate(range(t_steps)):
make_step(s,t)
print("will now save anim")
def plot_ani(i):
return saved_steps[i]
anim = animation.FuncAnimation(fig, plot_ani,range(len(saved_steps)) , interval=500, blit=False)
or :
fig,ax = plt.subplots()
saved_steps = []
def make_step(s, t):
if (s%plot_every) == 0:
print("plotting step {:.0f} , t ={:.0f} ".format(s, t*dt))
ax.clear()
ax.contourf(X, Y, c,
np.arange(0, 1.0, 0.01),
extend='both')
return ax
anim = animation.FuncAnimation(fig, make_step,range(len(saved_steps)) , interval=500, blit=False)
and then i do
anim.save('spinodal_decompo_ex.ogv', codec='libtheora')

The problem with your first approach is that you try to store matplotlib axes objects in a list. However, the ax you store in the list is always the same. Once you call ax.clear() even the previously saved ax object will be cleared.
The problem with the second approach is that FuncAnimation will always save the figure for each time step. It does not matter, whether or not you change something in the axes.
Turning towards a solution:
A principle that is always wise to stick to is to keep data generation and calculation separate from data visualization.
The advise would therefore be to
First Calculate the data.
time_steps = 10000
data = []
for t in range(time_steps):
d = calculate_data(t)
data.append(d)
# alternatively save data to file
# if it's to big to be kept in memory.
Only then start visualization
# now start visualizing
plot_every = 50
fig, ax = plt.subplots()
def make_step(step):
ax.clear()
# recall data from list, but take only every 50th entry
c = data[step*plot_every] #alternatively reload data from file
ax.contourf(X, Y, c, ...)
anim = animation.FuncAnimation(fig, make_step,range(time_steps//plot_every), interval=500)

Based on ImportanceofBeingErnest answer, here is what i came up with. The additional thing, is that contourf is not an artist, apparently...
fig = plt.figure()
saved_steps = []
def make_step(i, t):
c = ...
if (i%plot_every) == 0:
print("plotting step {:.0f} , t ={:.0f} ".format(i, t*dt))
im = plt.imshow(c, animated=True)
saved_steps.append([im])
for s , t in enumerate(range(t_steps)):
make_step(s, t)
print("will now save anim")
anim = animation.ArtistAnimation(fig, saved_steps, interval=50, blit=False)
anim.save('spinodal_decompo_ex.ogv', codec='libtheora', extra_args=['-qscale:v', '7'])
thank you for pointing this out.

Related

animation.FuncAnimation maps

I am sorry I can't understand how to put the arguments through the animation.FuncAnimation module no matter how many examples I use.
And my task is quite simple, I have geophysical arrays (time,x,y).
All I want is to animate how a certain field changes over time.
I guess my func argument should simply be my plotting function with changing index along the time axis. But it just doesn't happen.
field.shape
(12,912,1125)
X,Y = np.meshgrid(lon,lat)
fig, ax = plt.subplots()
def animate(dset,i):
ax[i] = plt.pcolormesh(X,Y,field_monthly[i].T)
plt.colorbar()
plt.set_cmap('viridis')
return ax
i = np.arange(12)
anim = animation.FuncAnimation(fig, animate(field_monthly,i), frames=12,
interval=500,
repeat=False,
blit=False)
I know I have some fundamental leak in my logic, but can't find it.
The code above is 1 out of 50 ways I tried twist and turn functions and indices.
Thank you!
There were a few issues with your implementation.
ax[i], you have just one axis, do not confuse subplots with time steps / frames
use the keyword fargs to pass additional arguments
def animate(i, ...) the first argument must be the frame
calling the colorer inside the update instead of once in the beginning
Fixing those gives:
from matplotlib import animation, pyplot as plt
import numpy as np
k, n, m = 12, 30, 50
field = np.random.random((k, n, m))
x, y = np.meshgrid(np.arange(n), np.arange(m))
fig, ax = plt.subplots()
plt.pcolormesh(x, y, field[0].T)
plt.colorbar()
plt.set_cmap('viridis')
def animate(i, field2):
plt.cla()
h = plt.pcolormesh(x, y, field[i].T)
return h,
anim = animation.FuncAnimation(fig=fig, func=animate, fargs=(field,),
frames=k, nterval=500, repeat=False, blit=False)

How to make an animation of a curve from scratch using Matplotlib

Note this is a follow-up question of How to make an animation of a Lissajous curve;
My first idea was to edit my original question and ask for the animation, but I understand and respect SO way of operating. So the best is making another question.
I want to make an animation of the curve (where you incrementally draw it) with parametrization: x(t) = sin(3t) and y(y) = sin(4t) where t[0, 2pi].
For doing so I would add the code:
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'b')
def init():
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
x.append(np.sin(4*frame))
y.append(np.sin(3*frame))
ln.set_data(x, y)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
The problem is that with this code it doesn't draw the whole curve from scratch. What does is overdrawing it, getting overlapping.
How can I draw it from scratch (i.e. starting with white background)? I've been thinking about an if else but got nothing.
Thanks
EDIT
Let me show you the whole code:
%matplotlib notebook
import matplotlib.pyplot as plt
import math
import numpy as np
from matplotlib.animation import FuncAnimation
# set the minimum potential
rm = math.pow(2, 1 / 6)
t = np.linspace(-10, 10, 1000, endpoint = False)
x = []
y = []
for i in t: #TypeError 'int' object is not iterable
x_i = np.sin( 3 * i )
y_i = np.sin( 4 * i )
x.append(x_i)
y.append(y_i)
# set the title
plt.title('Plot sin(4t) Vs sin(3t)')
# set the labels of the graph
plt.xlabel('sin(3t)')
plt.ylabel('sin(4t)')
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'b')
def init():
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
x.append(np.sin(4*frame))
y.append(np.sin(3*frame))
ln.set_data(x, y)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
# display the graph
plt.show()
This is the image I get at the beginning (screenshot taken after approximately 1s after started running; that's why you see that funny line): https://imgur.com/a/bNoViDA. As you can see it doesn't start from scratch (i.e not from white background)
This is the plot I get at the end: https://imgur.com/a/WQHHUk9
I am seeking getting that ending point but drawing everything from scratch, without starting with the shown plot.

Matplotlib - stop animation

I have made an animation using matplotlib and I'm trying to save it to a file. For this it seems I would need to turn off automatic repeating of the animation. If not, matplotlib will try to render a movie file that never ends.
But how do I keep the animation from looping? I have found that there is a keyword argument for the animation function, repeat, that can be set to False, but this has no apparent effect on my code! So what should I do? I've been googling for way to long with no avail.
The relevant code is as follows (last two lines is where I believe the error must be) (largely based on this):
# Set up figure & 3D axis for animation
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1], projection='3d')
# ax.axis('off')
# choose a different color for each trajectory
colors = plt.cm.jet(np.linspace(0, 1, n_bodies))
# set up lines and points
lines = sum([ax.plot([], [], [], '-', c=c)
for c in colors], [])
pts = sum([ax.plot([], [], [], 'o', c=c)
for c in colors], [])
# prepare the axes limits
xmin_max = (np.min(r[:,:,0])/2, np.max(r[:,:,0])/2)
ymin_max = (np.min(r[:,:,1])/2, np.max(r[:,:,1])/2)
zmin_max = (np.min(r[:,:,2])/2, np.max(r[:,:,2])/2)
ax.set_xlim(xmin_max)
ax.set_ylim(ymin_max)
ax.set_zlim(zmin_max)
# set point-of-view: specified by (altitude degrees, azimuth degrees)
ax.view_init(30, 0)
# initialization function: plot the background of each frame
def init():
for line, pt in zip(lines, pts):
line.set_data([], [])
line.set_3d_properties([])
pt.set_data([], [])
pt.set_3d_properties([])
return lines + pts
# animation function. This will be called sequentially with the frame number
def animate(i):
# we'll step two time-steps per frame. This leads to nice results.
i = (5 * i) % r.shape[1]
for line, pt, ri in zip(lines, pts, r):
# x, y, z = ri[:i].T
x, y, z = ri[i-1].T
line.set_data(x, y)
line.set_3d_properties(z)
pt.set_data(x, y)
# pt.set_data(x[-1:], y[-1:])
pt.set_3d_properties(z)
# pt.set_3d_properties(z[-1:])
ax.legend(['t = %g' % (i/float(n_timesteps))])
#ax.view_init(30, 0.01 *0.3 * i )
fig.canvas.draw()
return lines + pts
# instantiate the animator.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=n_timesteps, interval=10, blit=True,repeat=False)
anim.save('../fig/animation.mp4', writer = 'mencoder', fps=15)
print 'done!'
plt.show()
Have you run this? I have found that saving the animation produces a file with the number of frames set by FuncAnimation( , ,frames=numberofframes).
ani = animation.FuncAnimation(fig, update, frames=numberofframes, interval=1000/fps)
filename = 'doppler_plot'
ani.save(filename+'.mp4',writer='ffmpeg',fps=fps)
ani.save(filename+'.gif',writer='imagemagick',fps=fps)
If the output format is an animated GIF, this will usually repeat when played, but the file will only contain the number of frames specified.

Trying to animate a scatter plot in matplotlib

I have gotten a scatter plot working. Now I am trying to animate it. I have looked through multiple docs on how to do this. I get animation of the scatter plot, but none of the points are in the right position. I believe I have misunderstood something about how to use set_offsets, but I don't know what.
Here is the code in the class that calls matplotlib. It sets the agents in the right position in the initial plot:
def plot(self):
Show where agents are in graphical form. -------------------------- 2---
data = self.plot_data()
disp.display_scatter_plot("Agent Positions", data, anim=True,
data_func=self.plot_data)
def plot_data(self):
data = {}
for v in self.agents.varieties_iter():
data[v] = {"x": [], "y": []}
for agent in self.agents.variety_iter(v):
x_y = self.get_pos_components(agent)
data[v]["x"].append(x_y[0])
data[v]["y"].append(x_y[1])
return data
And here is my attempt to animate this plot:
def display_scatter_plot(title, varieties, anim=False,
data_func=None):
"""
Display a scatter plot.l
varieties is the different types of
entities to show in the plot, which
will get assigned different colors
"""
def update_plot(i):
varieties = data_func()
for var, scat in zip(varieties, scats):
x = np.array(varieties[var]["x"])
y = np.array(varieties[var]["y"])
scat.set_offsets((x, y))
return scats
fig, ax = plt.subplots()
scats = []
i = 0
for var in varieties:
color = colors[i % NUM_COLORS]
x = np.array(varieties[var]["x"])
y = np.array(varieties[var]["y"])
scat = plt.scatter(x, y, c=color, label=var,
alpha=0.9, edgecolors='none')
scats.append(scat)
i += 1
ax.legend()
ax.set_title(title)
plt.grid(True)
if anim:
animation.FuncAnimation(fig,
update_plot,
frames=1000,
interval=1000,
blit=False)
plt.show(block=False)
Once the animation starts, the points do move around, but, as I mentioned, none of them to the right positions! As I said, I think I have gotten set_offsets wrong, but I don't know how I did so.

How can I fill gradually an array in a loop without declaring it as ones or zeros/empty in Python?

I would like to fill an array gradually in a loop and display the result by every iteration, so that I don’t like to declare it as ones or zeros. Is there any technique to achieve that?
Here is an example in which I want to fill xp and yp gradually. Defining xp and yp as empty falsifies the plots! Any help please?
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
a=.1
dt=.05
nx=33
ny=33
px=1
py=1
qx=1.0*px/(nx-1)
qy=1.0*py/(ny-1)
x = np.linspace(0,px,nx)
y = np.linspace(0,py,ny)
fig = plt.figure()
ax = plt.axes(xlim=(0, px), ylim=(0, py))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
ax.set_xlim(0,px)
ax.set_ylim(0,py)
X,Y = np.meshgrid(x,y)
U=-a*Y
V=a*X
x1=.5
y1=.5
xp=np.empty(nx)
yp=np.empty(ny)
xp[0]=x1
yp[0]=y1
def animate(i):
xp[i+1]=xp[i]+dt*U[yp[i]/qy,xp[i]/qx]
yp[i+1]=yp[i]+dt*V[yp[i]/qy,xp[i]/qx]
line.set_data(xp,yp)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=int(10), interval=5, blit=True)
plt.show()
Thank you!
In order to declare an array without filling it with ones or zeros/empty you may use for your 2 vectors the following:
xp = [None] * 80
yp = [None] * 80
And all now is going well!

Categories