Sorry I am a bit new to python and matplotlib so I dont know if I am asking it correctly. As of now I am plotting graphs where I collect an array of integers coming via serial port and refreshing the whole plot area at once. Now I want to do partial refresh (idk if it is the correct word) something like a PPG/ECG trace where once the line/trace reaches the end of plot area it starts from beginning, something like in the example here
[1]: http://theblogofpeterchen.blogspot.com/2015/02/html5-high-performance-real-time.html.
I do understand that if I keep on appending the serial port data and plotting it as soon as it arrives will keep on extending the plot forward but I have no clue how to return back at the the beginning point and redraw it gradually as in ECG.
Kindly help in this regard
Thank you
I have a solution below using FuncAnimation.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.misc import electrocardiogram
ecg = electrocardiogram()[0:1000]
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], '-')
# this is repeat length
n = 200
def init():
ax.set_xlim(0, n)
ax.set_ylim(-1, 2)
return ln,
def update(i):
# update xlim of axes
if i % n == 0:
ln.axes.set_xlim(int(i/n)*n, int(i/n)*n+n)
else:
xdata.append(i)
ydata.append(ecg[i])
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=1000, init_func=init, blit=True, interval=10, repeat=False)
Related
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()
Hi I am trying to create a movie of 15 Gaussian graphs that move to the left (thats essentially what the code is suppose to do)
However, my idea for how to create the for loop to create the 15 graphs has not created more than 1, it only speeds up the animation.
A similar code worked on matlab. It created 15 different Gaussian curves.
Here is a sample of my code.
any help would be appreciated.
Thanks
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('agg')
import matplotlib.animation as animation
Gamma=0.0005
q=1.6e-19
m=0.067*9e-31
B=10
Ec=(1.0567e-34)*B/m
#e=2.78
#E0=0+(1.0567e-34)*x*i/m
fig, ax = plt.subplots()
pass
x = np.arange(0, 3.4e-3, 1.7e-5) # x-array, third number is interval here, x is energy
line, = ax.plot(x, np.e**(-(x-((1.0567e-34)*1*1/m))**2/Gamma**2))
def animate(i):
for p in xrange(1,3):
line.set_ydata(np.e**((-(x-((1.0567e-34)*p*i/m))**2)/Gamma**2)) # update the data
return line,
#Init only required for blitting to give a clean slate.
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, animate, np.arange(0, 2, .01), init_func=init,
interval=10, blit=True)
Writer = animation.writers['ffmpeg']
writer = Writer(fps=20, metadata=dict(artist='Me'), bitrate=1800)
ani.save('QHanimati.mp4', writer=writer)
plt.show()
You currently have exactly one line in your code. This line gets updated. If you want to have more lines, you need to create more lines.
You then also need to update all of those lines.
(Since the role of p isn't clear from the example I took it as some incrementing number here. I also restricted this to 8 curves not to overcrowd the image.)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
Gamma=0.0005
q=1.6e-19
m=0.067*9e-31
B=10
Ec=(1.0567e-34)*B/m
fig, ax = plt.subplots()
n = 8 # number of lines
x = np.arange(0, 3.4e-3, 1.7e-5)
lines = [ax.plot(x, np.e**(-(x-((1.0567e-34)*1*1/m))**2/Gamma**2))[0] for _ in range(n)]
def animate(i):
for ln, line in enumerate(lines):
p = (ln+1)/10.
line.set_ydata(np.e**((-(x-((1.0567e-34)*p*i/m))**2)/Gamma**2)) # update the data
return lines
#Init only required for blitting to give a clean slate.
def init():
for line in lines:
line.set_ydata(np.ma.array(x, mask=True))
return lines
ani = animation.FuncAnimation(fig, animate, np.arange(0, 2, .01), init_func=init,
interval=10, blit=True)
plt.show()
Although hours of browsing stackoverflow definitely improved my python animate code, I could not quite figure out one thing and therefore I'm turning towards the kind souls of the community in the hope that someone might be able to shred some light.
In a nutshell, I have a background image that is about 2000 x 1000 pixels let's say, I need to scatter some points over this image and animate the process and save whole thing as a video. I'm able to update the scatterplot as needed only with the exception that I can't remove the precedent scatterplot. So the output is not what I would really want. I would be glad if someone could have a glance at the code and see where the glitch is. I've used scat.remove() which seems to be doing nothing.
Thank you in advance folks.
import matplotlib.pyplot as plt
import pylab as pl
import numpy as np
from pylab import savefig
from matplotlib import animation
import matplotlib
######################################################
fig = plt.figure()
ax = plt.axes()
a = plt.imread('background.jpg')
im = plt.imshow(a)
#######################################################
def randpair(n):
x,y=[],[]
for i in xrange(n):
x.append(np.random.randint(100,1900))
y.append(np.random.randint(100,900))
return x,y
def animate(i):
scat = ax.scatter(0,0,color='white')
points = np.random.randint(5,size=10)
for j in points:
xy = randpair(j)
x = xy[0]
y = xy[1]
print x,y
if len(x) > 0 :
scat.remove()
scat = ax.scatter(x,y,color='r',s=18)
plt.xticks([])
plt.yticks([])
return scat,ax, # ax returns the text to be updated and scat returns the scatterplot.
anim = animation.FuncAnimation(fig, animate, 49,interval=300, blit=True)
writer = animation.writers['ffmpeg']
anim.save('film_3.mp4')
#plt.show()
In the code you already remove the last scatter before the loop is finished; so some scatter plots will be added and then removed immediately.
One can prevent this by collecting the scatters in a list, then removing the scatters in the list from the canvas using remove and cleaning the list.
Apart from that, returning the complete ax object is a little useless. So I would suggest to simply turn blitting off, since it will not matter for saving the animation.
Here is a complete code that worked for me:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
fig = plt.figure()
ax = plt.axes()
scats = []
a = np.random.rand(8,18)
im = ax.imshow(a, cmap="YlGn", vmin=0, vmax=3, extent=[0,2000,0,1000])
plt.xticks([])
plt.yticks([])
def randpair(n):
x,y=[],[]
for i in xrange(n):
x.append(np.random.randint(100,1900))
y.append(np.random.randint(100,900))
return x,y
def animate(i):
global scats
# first remove all old scatters
for scat in scats:
scat.remove()
scats=[]
# now draw new scatters
points = np.random.randint(5,size=10)
for j in points:
x, y = randpair(j)
if len(x) > 0 :
scats.append(ax.scatter(x,y,color='r',s=18))
anim = matplotlib.animation.FuncAnimation(fig, animate, 50,
interval=1000, blit=False)
writer = matplotlib.animation.FFMpegWriter(fps=15,
codec="h264",
extra_args=["-preset", "veryslow","-crf","0"])
anim.save(__file__+".mp4", writer=writer)
plt.show()
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()
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