Slow and smooth drawing lines python matplotlib - python

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).

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,

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

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:

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()

How can i make points of a python plot appear over time?

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()

Defining multiple plots to be animated with a for loop in matplotlib

Thanks to Jake Vanderplas, I know how to start to code an animated plot with matplotlib. Here is a sample code:
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(0, 100))
line, = plt.plot([], [])
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data([0, 2], [0,i])
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
plt.show()
Suppose now I'd like to plot tons of functions (say four here), defined with the help of a loop. I did some voodoo programming, trying to understand how to mimic the comma following line and here is what I got (needless to say that it does not work: AttributeError: 'tuple' object has no attribute 'axes').
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(0, 100))
line = []
N = 4
for j in range(N):
temp, = plt.plot([], [])
line.append(temp)
line = tuple(line)
def init():
for j in range(N):
line[j].set_data([], [])
return line,
def animate(i):
for j in range(N):
line[j].set_data([0, 2], [10 * j,i])
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
plt.show()
Some my question is: how can I make it work? Bonus (probably linked): what is the difference between line, = plt.plot([], []) and line = plt.plot([], [])?
Thanks
In the solution below I showcase a bigger example (with also bar plot) that may help people understand better what should be done for other cases. After the code I explain some details and answer the bonus question.
import matplotlib
matplotlib.use('Qt5Agg') #use Qt5 as backend, comment this line for default backend
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(0, 100))
N = 4
lines = [plt.plot([], [])[0] for _ in range(N)] #lines to animate
rectangles = plt.bar([0.5,1,1.5],[50,40,90],width=0.1) #rectangles to animate
patches = lines + list(rectangles) #things to animate
def init():
#init lines
for line in lines:
line.set_data([], [])
#init rectangles
for rectangle in rectangles:
rectangle.set_height(0)
return patches #return everything that must be updated
def animate(i):
#animate lines
for j,line in enumerate(lines):
line.set_data([0, 2], [10 * j,i])
#animate rectangles
for j,rectangle in enumerate(rectangles):
rectangle.set_height(i/(j+1))
return patches #return everything that must be updated
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=20, blit=True)
plt.show()
Explanation
The idea is to plot what you need and then reuse the artists (see more here) returned by matplotlib. This is done by first plotting a dummy sketch of what you want and keeping the objects matplotlib gives you. Then on your init and animate functions you can update the objects that need to be animated.
Note that in plt.plot([], [])[0] we get a single line artist, thus I collect them with [plt.plot([], [])[0] for _ in range(N)]. On the other hand plt.bar([0.5,1,1.5],[50,40,90],width=0.1) returns a container that can be iterated for the rectangle artists. list(rectangles) just convert this container into a list to be concatenated with lines.
I separate the lines from the rectangles because they are updated differently (and are different artists) but init and animate return all of them.
Answer to bonus question:
line, = plt.plot([], []) assign the first element of the list returned by plt.plot to the veriable line.
line = plt.plot([], []) just assign the whole list (of only one element).
Here is a modified example which is more readable.
This is just the code from the matplotlib website with another piece added. What threw me was I wasn't initially realizing the plt functions were returning lists, overlooking the trailing comma, or even how things were being tied together, which seems to be getting handled pretty implicitly by the library itself. But the key is to create several update-able objects and return them from the two key functions as part of the same list and they will be synced when the animation is run.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
xdata2,ydata2= [], []
ln, = plt.plot([], [], 'ro')
ln2, = plt.plot([], [], 'ro')
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,ln2
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
xdata2.append(frame)
ydata2.append(np.cos(frame))
ln2.set_data(xdata2,ydata2)
return ln,ln2
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()
For more fun change the animation callback to the following:
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
delta = 2*np.pi/128
xdata2.append([frame*2,frame*2+delta])
ydata2.append([np.cos(frame*2), np.cos(frame*2+delta)])
ln2.set_data(xdata2,ydata2)
return ln,ln2

Categories

Resources