Create animation for multilayer array in python [duplicate] - python

I have a 3D matrix of shape (100,50,50), e.g.
import numpy as np
data = np.random.random(100,50,50)
And I want to create an animation that shows each of the 2D slices of size (50,50) as a heatmap or imshow
e.g.:
import matplotlib.pyplot as plt
plt.imshow(data[0,:,:])
plt.show()
would display the 1st 'frame' of this animation. I'd like to also have this display in a Jupyter Notebook. I am currently following this tutorial for inline notebook animations being displayed as a html video, but I can't figure out how to replace the 1D line data with a slice of my 2D array.
I know I need to create a plot element, an initialisation function and an animate function. Following that example, I've tried:
fig, ax = plt.subplots()
ax.set_xlim((0, 50))
ax.set_ylim((0, 50))
im, = ax.imshow([])
def init():
im.set_data([])
return (im,)
# animation function. This is called sequentially
def animate(i):
data_slice = data[i,:,:]
im.set_data(i)
return (im,)
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
HTML(anim.to_html5_video())
But I'm getting various errors whatever I try, mostly associated with the line im, = ax.imshow([])
Any help appreciated!

Several issues:
You have a lot of missing imports.
numpy.random.random takes a tuple as input, not 3 arguments
imshow needs an array as input, not an empty list.
imshow returns an AxesImage, which cannot be unpacked. Hence no , on the assignment.
.set_data() expects the data, not the framenumber as input.
Complete code:
from IPython.display import HTML
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
data = np.random.rand(100,50,50)
fig, ax = plt.subplots()
ax.set_xlim((0, 50))
ax.set_ylim((0, 50))
im = ax.imshow(data[0,:,:])
def init():
im.set_data(data[0,:,:])
return (im,)
# animation function. This is called sequentially
def animate(i):
data_slice = data[i,:,:]
im.set_data(data_slice)
return (im,)
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
HTML(anim.to_html5_video())

Related

Trying to make a gif in Python

I'm approximating functions with Fourier Series and I would like to make, hopefully in a simple way, an animated gif of the different approximations. With a for and plt.savefig commands I generate the different frames for the gif but I haven't been able to animate them. The idea is to obtain something like this
This can be done using matplotlib.animation.
EDIT
I'm going to show you how to plot even powers of x like x^2, x^4 and so on. take a look at following example :
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# Setting up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(-10, 10), ylim=(-10, 1000))
line, = ax.plot([], [], lw=2)
# initialization method: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation method. This method will be called sequentially
pow_ = 2
def animate(i):
global pow_
x = np.linspace(-10, 10, 1000)
y = x**pow_
pow_+=2
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=10, interval=200, blit=True)
# if you want to save this animation as an mp4 file, uncomment the line bellow
# anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()

How do I make an animation with a for-loop when I have the data for each picture?

I have an array with a lot of variables. Each row represents the data needed to calculate one frame of y, while x is numpy.linspace array.
When I use plt.plot(x, np.conj(bildevector[i,:])*bildevector[i,:] in a for-loop, I get exactly the the images I want, but I want them as a video file.
I have tried using ArtistAnimation, but I cannot get it to work.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
""" Plotter de lagrede verdiene """
fig = plt.figure()
ims = []
for i in range(Nx):
plt.grid(True)
#plt.plot(x, np.conj(bildevector[i,:])*bildevector[i,:])
im = plt.imshow(plt.plot(x,
np.conj(bildevector[i,:])*bildevector[i,:]), animated=True)
ims.append([im])
#plt.show()
#plt.close()
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
The parts with # are the ones that work.
I have also tried this:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
def f(u,v):
return np.conj(bildevector[v,:])*bildevector[v,:]
fig = plt.figure()
ax = plt.axes(xlim=(0,L),ylim=(0, 8*10^7))
ims = []
for i in range(340):
im = plt.imshow((x, np.real(np.conj(bildevector[i,:])*bildevector[i,:])), animated=True)
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
ani.save('dynamic_images.mp4')
plt.show()
I expect to see the final image of the graph in the console, but all I get is an empty picture with the y-axis right, and the x-axis in one point, 0. I also expect to get a file with some actual movement.
I think you got confused by the matplotlib example that is using plt.imshow() to create its artists. You seem to be wanting to create a plot using plt.plot(), so you don't have to use imshow:
fig = plt.figure()
ims = []
for i in range(Nx):
plt.grid(True)
p = plt.plot(x,
np.conj(bildevector[i,:])*bildevector[i,:]), animated=True)
ims.append(p)
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
Note that there is one subtlety in the fact that plt.plot() returns a list of artists, while most other plotting functions (including plt.imshow()) usually only return one Artist object. You have to keep this in mind when you want to append the returned values to a list.

Matplotlib Animation: how to dynamically extend x limits?

I have a simple animation plot like so:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 100), ylim=(0, 100))
line, = ax.plot([], [], lw=2)
x = []
y = []
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
x.append(i + 1)
y.append(10)
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
Now, this works okay, but I want it to expand like one of the subplots in here http://www.roboticslab.ca/matplotlib-animation/ where the x-axis dynamically extends to accommodate the incoming data points.
How do I accomplish this?
I came across this problem (but for set_ylim) and I had some trial and error with the comment of #ImportanceOfBeingErnest and this is what I got, adapted to #nz_21 question.
def animate(i):
x.append(i + 1)
y.append(10)
ax.set_xlim(min(x), max(x)) #added ax attribute here
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=500, interval=20)
Actually in the web quoted by #nz_21 there is a similar solution.
There is a workaround for this even when blit=True if you send a resize event to the canvas, forcing MPL to flush the canvas. To be clear, the fact that the code in #fffff answer doesn't work with blit is a bug in the MPL codebase, but if you add fig.canvas.resize_event() after setting the new x-axis, it will work in MPL 3.1.3. Of course, you should only do this sporadically to actually get any benefit from blitting --- if you're doing it every frame, you're just performing the non-blit procedure anyway, but with extra steps.

Time dependent plot matplotlib

I would like to plot point-by-point a sine wave in Python via Matplotlib, for which, each point, is added every x milliseconds, in order to obtain a smooth animation of the drawing.
This is my attempt:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from math import sin
fig, ax = plt.subplots()
x = [0]
line, = ax.plot(x, np.asarray(0))
def animate(i):
x.append(x[-1]+0.04)
line.set_xdata(np.asarray(x)*2*np.pi/5)
line.set_ydata(np.sin(np.asarray(x)*2*np.pi/5))
plt.draw()
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, animate, 10, init_func=init, interval=40, blit=True)
plt.show()
Which raises:
RuntimeError: The animation function must return a sequence of Artist objects.
What did I mistaken? What is, in your opinion, the most efficient way to obtain this effect?
PS The time axis should stay fixed and not move, so it should be wider than the plot
Firstly you are getting the error because your animate(i) is not returning anything. You need to return line,. Secondly you are not using the iin animate(i) aswell.
Here is a simple sine curve animation from https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# First set up the figure, the axis, and the plot element we want to
animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
# call the animator. blit=True means only re-draw the parts that have
changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
There is additional inspiration in the link which might help you further.
Animate should return a sequence of artist objects:
You should add:
return line, to the end of the animate function
def animate(i):
x.append(x[-1]+0.04)
line.set_xdata(np.asarray(x)*2*np.pi/5)
line.set_ydata(np.sin(np.asarray(x)*2*np.pi/5))
return line,
Source:
Another answer
Simple Example

Matplotlib FuncAnimation only draws one frame

I am trying to do an animation using the FuncAnimation module, but my code only produces one frame and then stops. It seems like it doesn't realize what it needs to update. Can you help me what went wrong?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
def animate(i):
PLOT.set_data(x[i], np.sin(x[i]))
print("test")
return PLOT,
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
def animate(i):
PLOT.set_data(x[:i], np.sin(x[:i]))
# print("test")
return PLOT,
ani = animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
You need to keep a reference to the animation object around, otherwise it gets garbage collected and it's timer goes away.
There is an open issue to attach a hard-ref to the animation to the underlying Figure object.
As written, your code well only plot a single point which won't be visible, I changed it a bit to draw up to current index

Categories