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()
Related
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,
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:
I would like to create a animation where my data points would gradually appear on my graph and freeze when all the data points have appeared. I've seen in done with correlations i'm just not too sure how to do it with just individual points themselves
This isn't something that will show anything particularly useful but i though it would look cool since i am trying to visualize some location data on a map
I know this isn't very clear so please as for clarifications, I'm not too sure how to phrase my problem very well.
Thanks
matplotlib.animation.FuncAnimation is the right tool for you. First create an empty graph, and then gradually add data points to it in the function. The following piece of code will illustrate it:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x = np.arange(10)
y = np.random.random(10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph, = plt.plot([], [], 'o')
def animate(i):
graph.set_data(x[:i+1], y[:i+1])
return graph
ani = FuncAnimation(fig, animate, frames=10, interval=200)
plt.show()
The result (saved as gif file) is shown below:
EDIT: To make the animation look stopped when finished in matplotlib window, you need to make it infinite (omit frames parameter in FuncAnimation), and set the frame counter to the last number in your frame series:
def animate(i):
if i > 9:
i = 9
graph.set_data(x[:i+1], y[:i+1])
return graph
ani = FuncAnimation(fig, animate, interval=200)
Or, which is better, you can set repeat parameter in FuncAnimation to False, as per answer to this question.
EDIT 2: To animate a scatter plot, you need a whole bunch of other methods. A piece of code is worth a thousand words:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x = np.arange(10)
y = np.random.random(10)
size = np.random.randint(150, size=10)
colors = np.random.choice(["r", "g", "b"], size=10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph = plt.scatter([], [])
def animate(i):
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i+1])
graph.set_facecolors(colors[:i+1])
return graph
ani = FuncAnimation(fig, animate, repeat=False, interval=200)
plt.show()
I'm using matplotlib for drawing graphs such as smooth lines. it is not problem to draw for me, but I have problems with animations.
import numpy as np
import random as random
from matplotlib import pyplot as plt
from matplotlib import animation
So, i have array, such as:
a = [0,1,2,4,5,8,9,12,14,18,22,17,30,37,29,45]
And I need to draw it smooth point-to-point.
Now I have this strings:
for i in range(len(a)-1):
desty = np.append(desty,np.linspace(a[i],a[i+1],n))
destx = np.append(destx,np.linspace(i,i+1,n))
that allows me to draw lines without problems (and animations :))
the full code:
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 50), ylim=(0, 50))
line, = ax.plot([], [], lw=2)
global x
global y
global n
global a
n=5
a = [0,1,2,4,5,8,9,12,14,18,22,17,30,37,29,45]
global desty,destx
desty = np.linspace(0,0,n)
destx = np.linspace(0,0,n)
for i in range(len(a)-1):
desty = np.append(desty,np.linspace(a[i],a[i+1],n))
destx = np.append(destx,np.linspace(i,i+1,n))
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
global destx
global desty
x=destx
y=desty
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()
Allows me to draw it only, but how can i draw it slow and smooth point to point?
I'm using python 2.7, centos 7.
You can slim your plotting routine down to:
import numpy as np
import random as random
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, 50), ylim=(0, 50))
line, = ax.plot([], [], lw=2)
n=5
a = [0,1,2,4,5,8,9,12,14,18,22,17,30,37,29,45]
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(np.linspace(i,i+1,n))
y.append(np.linspace(a[i],a[i+1],n))
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, np.arange(0,len(a)-1) ,init_func=init,
interval=200, blit=True, repeat=False)
plt.show()
Things to note:
You don't require global variables for this to work
You don't want to set up x and y outside of the animation but inside for the plot to develop (in your case you had the complete x and y set up so that the animation would only plot the entire graph)
You need to pass an interable to animate (the i); this is the third input to FuncAnimation - np.arange(0,len(a)-1).
set repeat=False to stop after one run and to avoid 'closing' the curve
I increased interval to make the plot develop slower
You need to use i in your animate function, otherwise it just plots the same thing over and over, which is what you see.
import numpy as np
import random as random
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, 50), ylim=(0, 50))
line, = ax.plot([], [], lw=2)
global x
global y
global n
global a
n=5
a = [0,1,2,4,5,8,9,12,14,18,22,17,30,37,29,45]
global desty,destx
desty = np.linspace(0,0,n)
destx = np.linspace(0,0,n)
plot_me_x = []
plot_me_y = []
for i in range(len(a)-1):
desty = np.append(desty, np.linspace(a[i],a[i+1],n))
destx = np.append(destx, np.linspace(i,i+1,n))
plot_me_x.append(np.array(destx)) # keep a copy of the intermediaries to plot later
plot_me_y.append(np.array(desty))
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
global destx
global desty
x=plot_me_x[i]
y=plot_me_y[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=len(a)-1, interval=20, blit=True)
plt.show()
For the plot to look different at each animation, i must index something that looks different at each iteration. Although desty and destx are appended to gradually in the iteration that builds them, the final result is not this gradual build but a single thing, so you need to save the intermediaries of this build. I did this with plot_me_x and y.
I wrote this answer with minimal changes to the OP's code to make it clear where the error was. Overall, the changes that #Schorsch made lead to an overall cleaner approach (eg, with no globals).
I am trying to use the FuncAnimation of Matplotlib to animate the display of one dot per frame of animation.
# modules
#------------------------------------------------------------------------------
import numpy as np
import matplotlib.pyplot as py
from matplotlib import animation
py.close('all') # close all previous plots
# create a random line to plot
#------------------------------------------------------------------------------
x = np.random.rand(40)
y = np.random.rand(40)
py.figure(1)
py.scatter(x, y, s=60)
py.axis([0, 1, 0, 1])
py.show()
# animation of a scatter plot using x, y from above
#------------------------------------------------------------------------------
fig = py.figure(2)
ax = py.axes(xlim=(0, 1), ylim=(0, 1))
scat = ax.scatter([], [], s=60)
def init():
scat.set_offsets([])
return scat,
def animate(i):
scat.set_offsets([x[:i], y[:i]])
return scat,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=len(x)+1,
interval=200, blit=False, repeat=False)
Unfortunately, the final animated plot is not the same as original plot. The animated plot also flashes several dots during each frame of animation. Any suggestions on how to correctly animate a scatter plot using the animation package?
The only problem with your example is how you fill the new coordinates in the animate function. set_offsets expects a Nx2 ndarray and you provide a tuple of two 1d arrays.
So just use this:
def animate(i):
data = np.hstack((x[:i,np.newaxis], y[:i, np.newaxis]))
scat.set_offsets(data)
return scat,
And to save the animation you might want to call:
anim.save('animation.mp4')
Disclaimer, I wrote a library to try and make this easy but using ArtistAnimation, called celluloid. You basically write your visualization code as normal and simply take pictures after each frame is drawn. Here's a complete example:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
from celluloid import Camera
fig = plt.figure()
camera = Camera(fig)
dots = 40
X, Y = np.random.rand(2, dots)
plt.xlim(X.min(), X.max())
plt.ylim(Y.min(), Y.max())
for x, y in zip(X, Y):
plt.scatter(x, y)
camera.snap()
anim = camera.animate(blit=True)
anim.save('dots.gif', writer='imagemagick')