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.
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).
I'm trying to create a video of many figures, so I need the axis to remain steady across multiple, independent figures. However, the y-axis changes scale, so the framing of the axis keeps moving as the ticklabels change. I'm trying to manually tell matplotlib exactly what size the whole figure should be and tell it exactly the position of the axis within the figure, but it's not working properly.
Here's what a base figure looks like:
import matplotlib.pyplot as plt
fig=plt.figure(figsize=(8,4),facecolor=(0.5,0.5,0.5))
ax=fig.add_subplot()
ax.plot([5,10],[800,900])
plt.show()
Here is one way for how I'm trying to change it if I want the axis frame to start at left=0.5, bottom=0.5, width=0.2, and height=0.2. I've tried many different ways, and all have failed, so this is illustrative of what I'm trying to do:
fig=plt.figure(figsize=(8,4),facecolor=(0.5,0.5,0.5))
ax=fig.add_axes((0.5,0.5,0.2,0.2))
ax.plot([5,10],[800,900])
plt.show()
Now, I want it to look more like this so that the black box of the axis frame will be in the exact same position for every figure, and each figure will be the exact same size. That way, when I make it an animation, the black frame won't be jerking around. (Obviously, I wouldn't make the buffer that big in the real video.)
You need to use ax.set_position.
If your ax box initially occupies the full figure, you can create a new size relatively to the old one, for example:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8, 4), facecolor=(0.5, 0.5, 0.5))
ax = fig.add_subplot(111)
bbox = ax.get_position()
new_bbox = (bbox.x0+0.40, bbox.y0+0.40, bbox.width*0.5, bbox.height*0.5)
ax.set_position(new_bbox)
ax.plot([5, 10], [800, 900])
plt.show()
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()
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.
I am developing some code to produce an arbitrary number of 2D plots (maps and simple contour plots) on a figure. The matplotlib subplots routine works great for this. In the simplified example below, everything works as it should. However, in my real application - which uses the exact same commands for subplots, contourf and colorbar, only that these are dispersed across several routines - the labels on the colorbars are not showing up (the color patches seem to be ok though). Even after hours of reading documentation and searching the web, I don't even have a clue where I could start looking for what the problem is. If I have my colorbar instance (cbar), I should be able to find out if the ticklabel position makes sense, if the ticklabels are set to visible, if my font settings make sense, etc.... But how do I actually check these properties? Has anyone encountered similar problems already? (and even better: found a solution?) Oh yes: if I manually create a new figure and axes in the actual plotting routine (where the contourf command is issued), then it will work again. But that means losing all control over the figure layout etc. Could it be that I am not passing my axes instance correctly? Here is what I do:
fig, ax = plt.subplots(nrows, ncols)
row, col = getCurrent(...)
plotMap(x, y, data, ax=ax[row,col], ...)
Then, inside plotMap:
c = ax.contourf(x, y, data, ...)
ax.figure.colorbar(c, ax=ax, orientation="horizontal", shrink=0.8)
As said above, the example below with simplified plots and artificial data works fine:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0.,360.,5.)*np.pi/180.
y = np.arange(0.,360.,5.)*np.pi/180.
data = np.zeros((y.size, x.size))
for i in range(x.size):
data[:,i] = np.sin(x[i]**2*y**2)
fig, ax = plt.subplots(2,1)
contour = ax[0].contourf(x, y, data)
cbar = ax[0].figure.colorbar(contour, ax=ax[0], orientation='horizontal', shrink=0.8)
contour = ax[1].contourf(x, y, data, levels=[0.01,0.05,0.1,0.05])
cbar = ax[1].figure.colorbar(contour, ax=ax[1], orientation='horizontal', shrink=0.8)
plt.show()
Thanks for any help!
Addition after some further poking around:
for t in cbar.ax.get_xticklabels():
print t.get_position(), t.get_text(), t.get_visible()
shows me the correct text and visible=True, but all positions are (0.,0.). Could this be a problem?
BTW: axis labels are also missing sometimes... and I am using matplotlib version 1.1.1 with python 2.7.3 on windows.
OK - I could track it down: matplotlib is working as it should!
The error was embedded in a utility routine that adds some finishing touches to each page (=figure) once the given number of plot panels has been produced. In this routine I wanted to hide empty plot panels (i.e. on the last page) and I did this with
ax = fig.axes
for i in range(axCurrent, len(ax)):
ax[i].set_axis_off()
However, axCurrent was already reset to zero when the program entered this routine for any page but the last, hence the axes were switched off for all axes in figure. Adding
if axCurrent > 0:
before the for i... solves the problem.
Sorry if I stole anyone's time. Thanks anyway to everyone who was considering to help!