Matplotlib monitor - plot values from table every X seconds - python

I'm new to python and matplotlib and need a few pointers. I'm trying to write a monitor that queries a table and plots the results. from the table I can extract a timestamp I'd like to use for the X axis and a #of seconds I'd like to use for the Y value (number of packets sent). I'm not sure where "i" is populated for the animate function. My plot comes up but is empty. I'm not sure what ax.set_xlim should be set to and finally how do I get the date/timestamp to show up on the x-axis?
Im trying to modify the following example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(ylim=(0, 45))
line, = ax.plot([], [], lw=5)
def init():
line.set_data([], [])
return line,
def animate(i):
x,y,dk=getData()
line.set_data(x, y)
return line,
def Execute():
#anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=200, blit=True)
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=2000)
plt.show()
return(anim)
def getDataSql(sql):
... run sql
return(rl)
def getData():
...format return for getDataSql
...return X ex(2013-04-12 18:18:24) and Y ex(7.357) (both are lists)
return(X,Y,xy)
x=Execute()

def Execute():
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=2000, blit=True)
plt.show()
return anim
anim = Execute()
If you don't return the anim object (which has all the timers ect) in it, it gets garbage collected when Execute returns, which deletes all those objects, and hence your animation does not run.
You might also test using blit=False, it is a bit slower (which isn't an issue as you are updating ever 2s), but it is a bit less fincky to get working right.
Also try
ax.get_xaxis().set_major_locator(matplotlib.dates.AutoDateLocator())
ax.get_xaxis().set_major_formatter(matplotlib.dates.AutoDateFormatter())
before you run anything.

Related

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

Why does the output of animation.FuncAnimation have to be bound to a name? [duplicate]

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

Saving animations using ffmpeg and matplotlib

im new in here and new in python, im doing some animations with animation.FuncAnimation of matplotliib. The animation works perfectly but i´m having problems saving the animations. here is the part of the code of the animation.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
line, = ax.plot(range(N),sin(x[0,:]),'o-')
ax.axis([0,1,-1,1])
def animate(i):
line.set_ydata(sin(x[i,:])) # update the data
return line,
def init():
line.set_ydata(np.ma.array(x[0,:], mask=True))
return line,
ani = animation.FuncAnimation(fig, animate, np.arange(1, 10000),
interval=25, init_func=init, blit=True)
ani.save('2osc.mp4', writer="ffmpeg")
plt.show()
where x[:,:] is previously set. ani.save is saving every frame of the animation as a .npg image instade of saving the movie. I dont know if this is how it is suposed to work and i have to do the movie with the .npg with another program or if im doing something wrong.
Obs: i've previously installed ffmpeg and it seems to be working just fine.
For me this code seems to be running fine:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots(1,1)
x=np.linspace(np.pi,4*np.pi,100)
N=len(x)
ax.set_xlim(len(x))
ax.set_ylim(-1.5,1.5)
line, = ax.plot([],[],'o-')
def init():
line.set_ydata(np.ma.array(x[:], mask=True))
return line,
def animate(i, *args, **kwargs):
y=np.sin(x*i)
line.set_data(np.arange(N),y) # update the data
return line,
ani = animation.FuncAnimation(fig, animate, init_func=init,
frames=100, interval=10, blit= False, repeat = False)
ani.save('2osc.mp4', writer="ffmpeg")
fig.show()
You can install the ffmpeg library by using:
sudo apt-get install ffmpeg

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

Memory usage for matplotlib animation

I've been tweaking the code from this page http://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/ to make my own animation, but it crashes very quickly. Looking at the task manager, I can see that the memory build-up from running the program rises to 1gb within 30 seconds, which is very significant for my less-than-impressive laptop. From the way the code was calling animation(i) to set the y_data on the line every time, is the old data not replaced, causing the memory build-up? I'd like to fix this. My knowledge on the inner workings of matplotlib is limited, and some things I've tried is putting close(), clf(), and gc.collect() in animation(i), but none of them worked.
"""
Matplotlib Animation Example
author: Jake Vanderplas
email: vanderplas#astro.washington.edu
website: http://jakevdp.github.com
license: BSD
Please feel free to use and modify this, but keep the above information. Thanks!
"""
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, extra_args=['-vcodec', 'libx264'])
plt.show()
By default the animation code tries to save some number of frames (defaults to 100). Try explicitly setting this to zero:
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True,
save_count=0)

Categories

Resources