Python matplotlib.animation.FuncAnimation never makes a second frame iteration - python

I am trying to use matplotlib.animation.FuncAnimation to create a custom animation. However, the FuncAnimation function does not seem to make a second iteration of the animate function. I have enclosed a simple example that I found online, which is supposed to work and draw a sine wave. Both on my computer and the Amazon EC2 server the script calls animate and draws the frame for one iteration. A second iteration never seems to happen. What am I getting wrong?
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)
# animation function. This is called sequentially
def animate(i):
print("animate invoked")
print(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, frames=np.arange(100), interval=200)
plt.show()
Script output:
animate invoked
0

According to the example here, you need to pass an init_func to FunctionAnimation as well. So you can do:
# First set up the figure, the axis, and the plot element we want to animate
fig, ax = plt.subplots()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
# init function
def init():
return line,
# animation function. This is called sequentially
def animate(i):
print("animate invoked")
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 = FuncAnimation(fig, animate, init_func=init, frames=np.arange(100), interval=200)
# for jupyter notebook
HTML(anim.to_html5_video())
Which gives:

Related

I need some help on making an animated plot using Matplotlib

I have an array x_trj that has shape (50,12), and I would like to make an animation for all the rows corresponding to certain columns in a 2-D plot (so I can see what direction each line is going). Below is my code:
from matplotlib.animation import FuncAnimation
fig,ax = plt.subplots()
# Plot initial line to fill in as we go
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
# Plot resulting trajecotry of car
line.set_xdata(x_trj[i,0:9:4])
line.set_ydata(x_trj[i,1:10:4])
return line,
anim = FuncAnimation(fig, animate, init_func = init, frames=x_trj.shape[0], interval=200) # animation doesn't show up?
However, the animation does not show up at all. All I get is an empty figure. How should I fix this issue? I am writing my code in Google colab.
The animation shows up but it's empty (it has a built-in play button):
I don't know what values you have in x_traj but I had to set xlim, ylim to see animation because animation doesn't change visible area automatically and it displays line in area which I can see.
I tested it only on local computer.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
np.random.seed(0) # `randint()` will always select the same values
x_trj = np.random.randint(0, 10, (52,12))
fig, ax = plt.subplots()
ax.set_xlim(0, 10) # it would need to get min(), max() values from x_trj
ax.set_ylim(0, 10) # it would need to get min(), max() values from x_trj
#ax = plt.axes(xlim=(0, 10), ylim=(0, 10)) # needs `ax =`
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
print('i:', i)
x = x_trj[i,0:9:4]
y = x_trj[i,1:10:4]
print('x,y:', x, y)
line.set_xdata(x)
line.set_ydata(y)
return line,
anim = FuncAnimation(fig, animate, init_func=init, frames=x_trj.shape[0], interval=200)
plt.show()
Eventually you can change xlim, ylim during animation to change visible area but this may not look as you expect.
def animate(i):
print('i:', i)
x = x_trj[i,0:9:4]
y = x_trj[i,1:10:4]
print('x,y:', x, y)
line.set_xdata(x)
line.set_ydata(y)
ax.set_xlim(min(x), max(x)) # change visible are
ax.set_ylim(min(y), max(y)) # change visible are
return line,

Matplotlib animation duration and why it won't stop

matplotlib animation duration explained that basically the argument franes in FuncAnimation defines the total amount of frames it should animate. However, when I run the example code it just seems to be running constantly. I expected the fig will stop updating after 4 seconds but it didnot. Is there some kind of loop I need to disable? Thanks. I ran it on Python 3.7 and matplotlib 3.0.3
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2,2))
line, = ax.plot([], [], lw=2)
# init func, plot the background of each frame.
def init():
line.set_data([], [])
return line,
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,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)
plt.show()
You need repeat=False in your FuncAnimation call.
Checkout the doc please https://matplotlib.org/api/_as_gen/matplotlib.animation.FuncAnimation.html#matplotlib.animation.FuncAnimation
repeat : bool, optional
Controls whether the animation should repeat when the sequence of frames is completed. Defaults to True.
As default it repeats animation but you can use
FuncAnimation(... , repeat=False)
Doc: FuncAniamtion

Drawing a Nyquist diagram animation on python

I am currently trying to create a python code that is supposed to draw an animated nyquist diagram and save it as a gif file.
The problem is, I don't know how to make the animate function work. Here is a code I found on the internet that works:
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,
As you may know, linspace and sin are functions that returns arrays with the sequential values. The real and imag variables on my code are also arrays with sequential values. w variable is an array too, corresponding to the values of real and imag. I wanted real and imag to be drawn for every w value, thus being the "step" of the animation. What is wrong with my code?
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation as an
import control
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(-2, 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):
G = control.TransferFunction((1),(1,0))
real, imag, w = control.nyquist(G)
line.set_data(real, imag)
return line,**
# call the animator. blit=True means only re-draw the parts that have changed.
anim = an.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=200, blit=True)
#anim.save('GIF.gif', dpi=100, writer='imagemagick')
plt.title('Nyquist Diagram of 1/s')
plt.ylabel('Imaginary')
plt.xlabel('Real')
plt.grid(True)
plt.show()
In your code you are always ploting the current data (real and imag), but according to matplotlib you need to use a list of data which is updated in every iteration.
Matplotlib - Animation
In the code below I have created the lists realData and imagData, so in every iteration real and imag are appended to the list and these lists are used as line.set_data arguments.
I have also used the control package just in the begining because it already returns a list containing everything you need to plot.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation as an
import control
# First set up the figure, the axis, and the plot element we want to animate
fig, ax = plt.subplots()
realData, imagData = [], []
line, = plt.plot([], [], 'rx', animated=True)
G = control.TransferFunction((1),(1,0))
real, imag, w = control.nyquist(G)
print(real)
print(imag)
def init():
ax.set_xlim(-2, 2)
ax.set_ylim(-10, 10)
return line,
# animation function. This is called sequentially
def animate(i):
realData.append(real[i])
imagData.append(imag[i])
line.set_data(realData, imagData)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = an.FuncAnimation(fig, animate, init_func=init,
frames=range(len(real)), interval=2, blit=True)
#anim.save('GIF.gif', dpi=100, writer='imagemagick')
plt.title('Nyquist Diagram of 1/s')
plt.ylabel('Imaginary')
plt.xlabel('Real')
plt.grid(True)
plt.show()

Matplotlib animation of a step

I creating a Matplotlib animation of a step function. I am using the following code...
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.step([], [])
def init():
line.set_data([], [])
return line,
def animate(i):
x = np.linspace(0, 2, 10)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
plt.show()
It vaguely resembles what I desire (something like the gif below) but instead of the values being constant and scrolling with time each step is dynamic and shifts up and down. How would go about changing my code to achieve this shift?
step explicitly plots steps between the input data points. It can never plot a partial "step".
You're wanting an animation with "partial steps" in between.
Instead of using ax.step, use ax.plot, but make a stepped series by plotting y = y - y % step_size.
In other words, something like:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 1000) # Using a series of 1000 points...
y = np.sin(x)
# Make *y* increment in steps of 0.3
y -= y % 0.3
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()
Notice the partial "steps" at the beginning and end
Incorporating this into your animation example, we'd get something similar to:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [])
def init():
line.set_data([], [])
return line,
def animate(i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
y -= y % 0.3
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
plt.show()

Saving Animation with matplotlib?

I have installed ffmpeg using homebrew.
However the problem I am facing now is that no animation is saved when running the script.
I have included below my script and my .bash_profile
import matplotlib
matplotlib.use('TKAgg')
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)
# save the animation as an mp4. This requires ffmpeg or mencoder to be
# installed. The extra_args ensure that the x264 codec is used, so that
# the video can be embedded in html5. You may need to adjust this for
# your system: for more information, see
# http://matplotlib.sourceforge.net/api/animation_api.html
anim.save('basic_animation.mp4', fps=30)
.bash_profile
export PATH=$PATH:/$HOME/../../usr/local/Celler/ffmpreg/2.2.2/bin
export FFMPEG_BIN=/$HOME/../../usr/local/Cellar/ffmpeg/2.2.2/bin/
I was able to get output the animation correctly by Using an ipython notebook with the Anaconda environment.
There was a bit of a work around needed to get the animation to be displayed in the ipython notebook. Here it is:
from matplotlib import animation
from tempfile import NamedTemporaryFile
VIDEO_TAG = """<video controls>
<source src="data:video/x-webm;base64,{0}" type="video/webm">
Your browser does not support the video tag.
</video>"""
def anim_to_html(anim):
if not hasattr(anim, '_encoded_video'):
with NamedTemporaryFile(suffix='.webm') as f:
anim.save(f.name, fps=6, extra_args=['-vcodec', 'libvpx'])
video = open(f.name, "rb").read()
anim._encoded_video = video.encode("base64")
return VIDEO_TAG.format(anim._encoded_video)
from IPython.display import HTML
def display_animation(anim):
plt.close(anim._fig)
return HTML(anim_to_html(anim))
Once the above code was in place, the following animation was output correctly into the ipython notebook:
# 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)
anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
display_animation(anim)
For some reason when I right click and save I am unable to view the movie in a readable format...if anyone knows or figures out the solution to this please let me know!

Categories

Resources