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)
Related
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.
I'm trying to animate a 2d path, and I would like it to have a sort of "Disappearing Tail", where at any given time, it shows only the last 5 (for example) particles.
What I currently have is quite far from this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from IPython.display import HTML
sample_path = np.random.uniform(size=(100,2))
fig, ax = plt.subplots()
x = np.arange(-1, 1, 0.01) # x-array
line, = ax.plot(sample_path[0,0], sample_path[0,1])
def connect(i):
(x0,y0) = sample_path[i-1,:]
(x1,y1) = sample_path[i,:]
plt.plot([x0,x1],[y0,y1],'ro-')
return line,
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, connect, np.arange(1, 100), init_func=init,
interval=200, blit=True)
HTML(ani.to_html5_video())
This retains a 'full tail', i.e. after k steps, it shows all of the first k locations.
Is there a way to adapt what I've got so that the animation only shows the most recent history of the particle?
You would probably want to update the line instead of adding a lot of new points to the plot. Selecting the 5 most recent points can be done via indexing, e.g.
sample_path[i-5:i, 0]
Complete example, where we take care not to have a negative index and also don't use blit (which does not make sense if saving the animation).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, rc
r = np.sin(np.linspace(0,3.14,100))
t = np.linspace(0, 10, 100)
sample_path = np.c_[r*(np.sin(t)+np.cos(t)), r*(np.cos(t)-np.sin(t))]/1.5
fig, ax = plt.subplots()
line, = ax.plot(sample_path[0,0], sample_path[0,1], "ro-")
def connect(i):
start=max((i-5,0))
line.set_data(sample_path[start:i,0],sample_path[start:i,1])
return line,
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
ani = animation.FuncAnimation(fig, connect, np.arange(1, 100), interval=200)
plt.show()
Not as good as ImportanceOfBeingErnest's answer technically, but it still does the job and looks pretty cool, just plot the latest points and clear the old ones. I added a few more and sped it up because I thought it looked better with a longer trail.
def connect(i):
#clear current points
plt.clf()
#prevent axis auto-resizing
plt.plot(0,0)
plt.plot(1,1)
#generate points to plot
(x0,y0) = sample_path[i-8,:]
(x1,y1) = sample_path[i-7,:]
(x2,y2) = sample_path[i-6,:]
(x3,y3) = sample_path[i-5,:]
(x4,y4) = sample_path[i-4,:]
(x5,y5) = sample_path[i-3,:]
(x6,y6) = sample_path[i-2,:]
(x7,y7) = sample_path[i-1,:]
(x8,y8) = sample_path[i,:]
#plot old points
plt.plot([x0,x1,x2,x3,x4,x5,x6,x7],[y0,y1,y2,y3,y4,y5,y6,y7],'ro-')
#plot new point in blue
plt.plot([x7,x8],[y7,y8],'bo-')
return line,
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, connect, frames=np.arange(1, 100),
init_func=init,
interval=50, blit=True)
HTML(ani.to_html5_video())
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.
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!
I'm having issues with redrawing the figure here. I allow the user to specify the units in the time scale (x-axis) and then I recalculate and call this function plots(). I want the plot to simply update, not append another plot to the figure.
def plots():
global vlgaBuffSorted
cntr()
result = collections.defaultdict(list)
for d in vlgaBuffSorted:
result[d['event']].append(d)
result_list = result.values()
f = Figure()
graph1 = f.add_subplot(211)
graph2 = f.add_subplot(212,sharex=graph1)
for item in result_list:
tL = []
vgsL = []
vdsL = []
isubL = []
for dict in item:
tL.append(dict['time'])
vgsL.append(dict['vgs'])
vdsL.append(dict['vds'])
isubL.append(dict['isub'])
graph1.plot(tL,vdsL,'bo',label='a')
graph1.plot(tL,vgsL,'rp',label='b')
graph2.plot(tL,isubL,'b-',label='c')
plotCanvas = FigureCanvasTkAgg(f, pltFrame)
toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
toolbar.pack(side=BOTTOM)
plotCanvas.get_tk_widget().pack(side=TOP)
You essentially have two options:
Do exactly what you're currently doing, but call graph1.clear() and graph2.clear() before replotting the data. This is the slowest, but most simplest and most robust option.
Instead of replotting, you can just update the data of the plot objects. You'll need to make some changes in your code, but this should be much, much faster than replotting things every time. However, the shape of the data that you're plotting can't change, and if the range of your data is changing, you'll need to manually reset the x and y axis limits.
To give an example of the second option:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)
# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma
for phase in np.linspace(0, 10*np.pi, 500):
line1.set_ydata(np.sin(x + phase))
fig.canvas.draw()
fig.canvas.flush_events()
You can also do like the following:
This will draw a 10x1 random matrix data on the plot for 50 cycles of the for loop.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
for i in range(50):
y = np.random.random([10,1])
plt.plot(y)
plt.draw()
plt.pause(0.0001)
plt.clf()
This worked for me. Repeatedly calls a function updating the graph every time.
import matplotlib.pyplot as plt
import matplotlib.animation as anim
def plot_cont(fun, xmax):
y = []
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
def update(i):
yi = fun()
y.append(yi)
x = range(len(y))
ax.clear()
ax.plot(x, y)
print i, ': ', yi
a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
plt.show()
"fun" is a function that returns an integer.
FuncAnimation will repeatedly call "update", it will do that "xmax" times.
This worked for me:
from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
clear_output(wait=True)
y = np.random.random([10,1])
plt.plot(y)
plt.show()
I have released a package called python-drawnow that provides functionality to let a figure update, typically called within a for loop, similar to Matlab's drawnow.
An example usage:
from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
# can be arbitrarily complex; just to draw a figure
#figure() # don't call!
plot(t, x)
#show() # don't call!
N = 1e3
figure() # call here instead!
ion() # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
x = sin(2 * pi * i**2 * t / 100.0)
drawnow(draw_fig)
This package works with any matplotlib figure and provides options to wait after each figure update or drop into the debugger.
In case anyone comes across this article looking for what I was looking for, I found examples at
How to visualize scalar 2D data with Matplotlib?
and
http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop
(on web.archive.org)
then modified them to use imshow with an input stack of frames, instead of generating and using contours on the fly.
Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.
def animate_frames(frames):
nBins = frames.shape[0]
frame = frames[0]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
for k in range(nBins):
frame = frames[k]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
del tempCS1
fig.canvas.draw()
#time.sleep(1e-2) #unnecessary, but useful
fig.clf()
fig = plt.figure()
ax = fig.add_subplot(111)
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)
I also found a much simpler way to go about this whole process, albeit less robust:
fig = plt.figure()
for k in range(nBins):
plt.clf()
plt.imshow(frames[k],cmap=plt.cm.gray)
fig.canvas.draw()
time.sleep(1e-6) #unnecessary, but useful
Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg
Thank you for the help with everything.
All of the above might be true, however for me "online-updating" of figures only works with some backends, specifically wx. You just might try to change to this, e.g. by starting ipython/pylab by ipython --pylab=wx! Good luck!
Based on the other answers, I wrapped the figure's update in a python decorator to separate the plot's update mechanism from the actual plot. This way, it is much easier to update any plot.
def plotlive(func):
plt.ion()
#functools.wraps(func)
def new_func(*args, **kwargs):
# Clear all axes in the current figure.
axes = plt.gcf().get_axes()
for axis in axes:
axis.cla()
# Call func to plot something
result = func(*args, **kwargs)
# Draw the plot
plt.draw()
plt.pause(0.01)
return result
return new_func
Usage example
And then you can use it like any other decorator.
#plotlive
def plot_something_live(ax, x, y):
ax.plot(x, y)
ax.set_ylim([0, 100])
The only constraint is that you have to create the figure before the loop:
fig, ax = plt.subplots()
for i in range(100):
x = np.arange(100)
y = np.full([100], fill_value=i)
plot_something_live(ax, x, y)