I am trying to follow the basic animation tutorial located here and adapting it to display an already computed dataset instead of evaluating a function every frame, but am getting stuck. My dataset involves XY coordinates over time, contained in the lists satxpos and satypos I am trying to create an animation such that it traces a line starting at the beginning of the dataset through the end, displaying say 1 new point every 0.1 seconds. Any help with where I'm going wrong?
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
Code here creates satxpos and satypos as lists
fig = plt.figure()
ax = plt.axes(xlim=(-1e7,1e7), ylim = (-1e7,1e7))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(satxpos[i], satypos[i])
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames = len(satxpos), interval = 1, blit=True)
Edit: The code runs without errors, but generates a blank plot window with no points/lines displayed and nothing animates. The dataset is generated correctly and views fine in a static plot.
In order to "trace a line starting at the beginning of the dataset through the end" you would index your arrays to contain one more element per timestep:
line.set_data(satxpos[:i], satypos[:i])
(Note the :!)
Everything else in the code looks fine, such that with the above manipulation you should get and extending line plot. You might then want to set interval to something greater than 1, as that would mean 1 millesecond timesteps (which might be a bit too fast). I guess using interval = 40 might be a good start.
Your code looks correct! So long as satxpos and satypos are both configured and initialized properly, I believe everything else is valid!
One part of the code you do not show in your question is the invocation of the anim.save() and plt.show() functions, which are both necessary for your code to work (as per the tutorial you shared!)
You would therefore need to add something like:
anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()
to the end of your code to create the animation (and show it, I presume)!
Hope it helps!
Source - Matplotlib Animation Tutorial
I saw you mentioned "the parts that generate satxpos and satypos do create valid datasets. I can view those as a static plot just fine". But my guess is still the problem originated from your satxpos and satypos.
One way you can trouble shoot is to replace your two functions and animation code with line.set_data(satxpos[i], satypos[i]). Set i as 0, 1, ... and see if you can see the plot. If not, your satxpos and satypos are not as valid as you claimed.
As an example, a valid satxpos and satypos can be like this:
x = np.array([np.linspace(-1e7, 1e7, 1000)])
i = 200
satxpos = x.repeat(i, axis=0)
satypos = np.sin(2 * np.pi * (satxpos - 0.01 * np.arange(i).reshape(-1, 1).repeat(satxpos.shape[1], axis=1)))
satypos *= 1e7 / 2
This works with the code you provided thus indicating the code you've shown us is fine.
Edit in response to comments:
If your satxpos and satypos are just np.linespace, the animation loop will get just one point with (satxpos[i], satypos[i]) and you won't see the point on the plot without a setting like marker='o'. Therefore, you see nothing in your animation.
Related
I currently have a simulation process that outputs a data point on each iteration. I would like to animate this with matplotlib, but am unsure if possible with matplotlib.animation.
Many online tutorials/examples I have come across always start with a list of predefined points, e.g. x = [1,2,3,4,5], y=[5.5,3.6,7.1,2.2,3.3], and essentially animate this list. Technically this also works for me, but I will have to first run the simulation and append the results into lists x and y, and then run the animation process on these lists (which would require iterating through the lists again, which is pointless as ideally it should be animating alongside the simulation phase.) This will be cumbersome if I run the simulation with millions of iterations.
I was wondering if mpl.animation can animate data as it comes, e.g. start with x=[], y=[], then on first iteration we get x=[0.1], y=[3.3] and we animate this, and then on second iteration we get x=[0.1,0.52], y=[3.3,4.4] and we animate again, and so on, rather than requiring the entire list to be populated first before animating.
Why not just try it?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
pltdata, = plt.plot([], [])
plt.ylim(-1.1,1.1)
plt.xlim(0,200)
def animate(i):
x=np.arange(i, i*2+2)
y=np.sin(x*0.1)
pltdata.set_data(x, y)
return [pltdata]
theAnim = animation.FuncAnimation(fig, animate, frames=100, interval=100, blit=True, repeat=False)
plt.show()
As you can see, it is not a predefined list (it could have been for this example, but it is not. First plot is with constant [] list. And then x and y are recomputed from scratch at each animate call).
And works as intended.
(As always with animation, one must take care of xlim and ylim, because if they are chosen automatically, since there is no data at the beginning, they won't fit the future, yet unknown, data).
Edit: I got it to work by omitting matplotlib animation completely and adding plt.ion() before initializing the figures.
I have a measurement device that sends data to my PC that I can read with Python. Every second it sends 100 data points. Every time these 100 points are received, they can be obtained by calling a function. I want to update the figure, starting from X=0 again. The data contains X and Y data.
In other SO threads I found that matplotlib.animation.FuncAnimation can update the graph efficiently, but I have only found examples where data is appended to the graph.
At the moment, my graph is redrawing, but it just draws new points without clearing the previous graph and it is really slow (I can't drag the plot window while it's drawing). The clear function just leaves me with a grey window.
Does anyone know a fast enough workaround for this problem? Thanks a lot!
My current implementation:
def plot():
freq = np.linspace(f_start, f_stop, points, endpoint=True)
def update(frame):
#for i in range (iterations - 1):
print(frame)
complex_data = vna.data(0)
plotFigure(figInitProperties, freq, Rect2Pol(complex_data))
time.sleep(2) # Wait for all points to be scanned
ani = anim.FuncAnimation(figInitProperties[2], func=update, frames=(10), repeat=False)
plt.show()
plot()
figInitProperties returns the fig (size, labels, axis, ticks etc.) of the figure.
You can use ax.clear() or plt.cla() at the beginning of the update function.
I have N=lots of 256x256 images (grayscales saved as numpy.ndarray with shape=(N, 256, 256)) and want to look at all of them by use of animation. I also want to add some label showing details related to each of the images, such as its index, its maximum value, etc. I'm using matplotlib, which I'm not familiar with.
There are a number of StackOverflow topics concerned with this exact problem (e.g. 1, 2, 4), as well as numerous tutorials (e.g. 3). I pieced together below attempts at solving the problem from these sources.
The two possibilities I have tried are using the matplotlib.animation classes FuncAnimation and ArtistAnimation. I'm not happy with my solutions because:
I have not been able to display and animate text information together with the images. I can display animated text on top of the images using axes.text but don't know how to put text next to the image.
I strongly dislike the FuncAnimation solution for aesthetic reasons (use of global variables, etc.)
I also want an animated colorbar. I think this is possible (somehow) with FuncAnimation but I don't see how it is possible with ArtistAnimation
ArtistAnimation gets slow since a large number of Artists (each picture) are required
# python 3.6
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, ArtistAnimation
images = np.random.rand(1000,256,256)
fig, ax = plt.subplots()
# ####################### Solution using ArtistAnimation ##################################################
# for much larger numbers of pictures this gets very slow
# How do I display information about the current picture as text next to the plot?
ims = []
for i in range(images.shape[0]):
ims.append([plt.imshow(images[i], animated=True)])
ani = ArtistAnimation(fig, ims, interval=250, blit=True, repeat_delay=5000)
plt.show()
# ####################### Solution using FuncAnimation ##################################################
# I don't like to use global variables in principle (but still want to know how to make this work).
# I can't figure out a way to display text while animating.
# Here I try to animate title and return it from update_figure (since it's an Artist and should update?!) but it has no effect.
nof_frames = images.shape[0]
i = 0
im = plt.imshow(images[0], animated=True)
# I do know that variables that aren't changed need not be declared global.
# However, I want to mark them and don't like accessing global variables in the first place.
def update_figure(frame, *frargs):
global i, nof_frames, ax, images, im
if i < nof_frames - 1:
i += 1
else:
i = 0
im.set_array(images[i])
ax.set_title(str(i)) # this has no effect
return im, ax
ani = FuncAnimation(fig, update_figure, interval=300, blit=True)
plt.show()
I'm trying to create an animation which shows multiple particles moving around.
If I have one particle with one array giving the positions of that particle in each step of the animation, I get it to work (mostly thanks to extensive help from other answers I found here on stackoverflow).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
positions = np.array([[2,2],[3,3],[4,4]])
def init():
scatterplot.set_offsets([[], []])
return [scatterplot]
def update(i, scatterplot, positions):
scatterplot.set_offsets(positions[i])
return [scatterplot]
fig = plt.figure()
scatterplot = plt.scatter([], [], s=100)
plt.xlim(0,5)
plt.ylim(0,5)
anim = animation.FuncAnimation(
fig, update, init_func=init, fargs=(scatterplot, positions), interval=1000, frames=3,
blit=True, repeat=True)
plt.show()
But I cannot figure out how to add more particles to the same animation.
Let's say I want to add a second particle with positions
positions2 = np.array([[2,1][3,2][4,3]])
and have it move around in the same scatter plot, how do I manage that?
I'm a matplotlib newbie, and have been googling furiously to no avail, will be very grateful for any help :)
EDIT:
I figured it out eventually, just a matter of formatting the data correctly.
positions = np.array([[[2,2],[2,1]],[[3,3],[3,2]],[[4,4],[4,3]]])
Where the array contains all the positions in step one, then all the positions in step two etc. works.
I'd prefer to get one color pr moving point, to keep track of them, but at least it works now.
I figured it out eventually, just a matter of formatting the data correctly.
positions = np.array([[[2,2],[2,1]],[[3,3],[3,2]],[[4,4],[4,3]]])
Where the array contains all the positions in step one, then all the positions in step two etc. works.
I'd prefer to get one color pr moving point, to keep track of them, but at least it works now.
Im trying to save a GIF with the evolucion of some waves in 2d using pcolormesh (using surface or wireframe would also be ok).
This has been my aproach so far:
set the quadmesh to plot in polar coordinates:
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation as FuncAnimation
pi=np.pi
rmax=6.
r=2*np.linspace(0,np.sqrt(rmax*.5),100)**2
phi=np.linspace(0,2*pi,80)
R, P = np.meshgrid(r, phi)
X, Y = R*np.cos(P), R*np.sin(P)
set the figure and functions for the animation:
count is the amount of frames i have.
Z is a count*2D-array with the values i want to plot.
(it has the sum of some fourier like series)
fig, ax = pyplot.subplots()
def anim_I(count,r,phi):
anim=np.zeros((count,len(phi), len(r)))
for i in range(count):
anim[i,:,:]=coef_transf(final_coefs[i,:,:,:,0],r,phi)**2
return anim
Z=anim_I(count,r,phi)
def animate(i):
pyplot.title('Time: %s'%time[i])
#This is where new data is inserted into the plot.
plot=ax.pcolormesh(X, Y,Z[i,:,:],cmap=pyplot.get_cmap('viridis'),vmin=0., vmax=15.)
return plot,
ax.pcolormesh(X, Y,Z[0,:,:],cmap=pyplot.get_cmap('viridis'),vmin=0., vmax=15.)
pyplot.colorbar()
anim = FuncAnimation(fig, animate, frames = range(0,count,7), blit = False)
i don't really need to see it live, so i just save a gif.
anim.save('%d_%d_%d-%d.%d.%d-2dgif.gif' %(localtime()[0:6]), writer='imagemagick')
pyplot.close()
While this works, it can take to an hour to make the gif of a even a hundred frames.
I wan't to know what would be the correct way to do this so it could be usable.
I have seen the other post in this regard, but i couldn't get the code working, or it would be just as inneficient.
You could try to write
def animate(i):
pyplot.title('Time: %s'%time[i])
#This is where new data is inserted into the plot.
plot=plot.set_array(Z[i,:,:].ravel())
return plot,
instead of
def animate(i):
pyplot.title('Time: %s'%time[i])
#This is where new data is inserted into the plot.
plot=ax.pcolormesh(X, Y,Z[i,:,:],cmap=pyplot.get_cmap('viridis'),vmin=0., vmax=15.)
return plot,
This does not create a new object every time you call the animate funtion. Instead it changes the image of object that was already created.
However, the set_array method seems to need a flattened array, hence the .ravel().
This only produces the right image if you set the shading option of the pcolormap function to shading='gouraud'.
I don't know why, unfortunatelly, it seems to have to do with the sorting of the array.
I hoped, that helped a little.
I suggest inserting a
pyplot.clf()
at the beginning of your animate(i) function. This will start each frame with a blank figure. Otherwise, I suspect the plot will not be cleared, and the long time is due to actually plotting all previous frame below the new one.