animating a stem plot in matplotlib - python

I'm trying to animate a stem plot in matplotlib and I can't find the necessary documentation to help me. I have a series of data files which each look like this:
1 0.345346
2 0.124325
3 0.534585
and I want plot each file as a separate frame.
According to this and this other tutorial, I should create a function which updates the data contained in each plot object (artist? I'm not sure about the terminology)
From the second link, this is the update function
def update(frame):
global P, C, S
# Every ring is made more transparent
C[:,3] = np.maximum(0, C[:,3] - 1.0/n)
# Each ring is made larger
S += (size_max - size_min) / n
# Reset ring specific ring (relative to frame number)
i = frame % 50
P[i] = np.random.uniform(0,1,2)
S[i] = size_min
C[i,3] = 1
# Update scatter object
scat.set_edgecolors(C)
scat.set_sizes(S)
scat.set_offsets(P)
# Return the modified object
return scat,
How can I adapt this kind of update function for a stem plot? The documentation for stem is horribly brief (in fact this is a recurring issue as I'm learning matplotlib), but the example code shows that the output of stem is a tuple markerline, stemlines, baseline rather than an artist object like for plt.plot or plt.imshow.
So when I write my update function for the animation, how can I update the data inside the stem plot?

Here you go!
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(0.1, 2*np.pi, 10)
markerline, stemlines, baseline = ax.stem(x, np.cos(x), '-.')
def update(i):
ax.cla()
markerline, stemlines, baseline = ax.stem(x, np.cos(x+i/10), '-.')
ax.set_ylim((-1, 1))
anim = FuncAnimation(fig, update, frames=range(10, 110, 10), interval=500)
anim.save('so.gif', dpi=80, writer='imagemagick')
I think there can be better ways of achieving this- not requiring to clear the plot each time. However, this works!

When using the keyword use_line_collection=True (default behavior since Matplotlib 3.3) one can update the three elements
markerline
stemlines
baseline
individualy. Here is the code for the sine wave example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
x = np.linspace(0.1, 2*np.pi, 10)
y = np.cos(x)
bottom = 0
h_stem = ax.stem(x, y, bottom=bottom, use_line_collection=True, linefmt='-.')
def update(i):
y = np.cos(x+i/10)
# markerline
h_stem[0].set_ydata(y)
h_stem[0].set_xdata(x) # not necessary for constant x
# stemlines
h_stem[1].set_paths([np.array([[xx, bottom],
[xx, yy]]) for (xx, yy) in zip(x, y)])
# baseline
h_stem[2].set_xdata([np.min(x), np.max(x)])
h_stem[2].set_ydata([bottom, bottom]) # not necessary for constant bottom
anim = FuncAnimation(fig, update, frames=range(10, 110, 10), interval=1)
anim.save('so.gif', dpi=80, writer='imagemagick')
Depending on what values (x, y, bottom) should be updated you can omit some parts of this update or reuse the current values. I wrote a more general function, where you can pass an arbitrary combination of these values:
def update_stem(h_stem, x=None, y=None, bottom=None):
if x is None:
x = h_stem[0].get_xdata()
else:
h_stem[0].set_xdata(x)
h_stem[2].set_xdata([np.min(x), np.max(x)])
if y is None:
y = h_stem[0].get_ydata()
else:
h_stem[0].set_ydata(y)
if bottom is None:
bottom = h_stem[2].get_ydata()[0]
else:
h_stem[2].set_ydata([bottom, bottom])
h_stem[1].set_paths([np.array([[xx, bottom],
[xx, yy]]) for (xx, yy) in zip(x, y)])

Related

How to show sliding windows of a numpy array with matplotlib FuncAnimation

I am developing a simple algorithm for the detection of peaks in a signal. To troubleshoot my algorithm (and to showcase it), I would like to observe the signal and the detected peaks all along the signal duration (i.e. 20 minutes at 100Hz = 20000 time-points).
I thought that the best way to do it would be to create an animated plot with matplotlib.animation.FuncAnimation that would continuously show the signal sliding by 1 time-points and its superimposed peaks within a time windows of 5 seconds (i.e. 500 time-points). The signal is stored in a 1D numpy.ndarray while the peaks information are stored in a 2D numpy.ndarray containing the x and y coordinates of the peaks.
This is a "still frame" of how the plot would look like.
Now the problem is that I cannot wrap my head around the way of doing this with FuncAnimation.
If my understanding is correct I need three main pieces: the init_func parameter, a function that create the empty frame upon which the plot is drawn, the func parameter, that is the function that actually create the plot for each frame, and the parameter frames which is defined in the help as Source of data to pass func and each frame of the animation.
Looking at examples of plots with FuncAnimation, I can only find use-cases in which the data to plot are create on the go, like here, or here, where the data to plot are created on the basis of the frame.
What I do not understand is how to implement this with data that are already there, but that are sliced on the basis of the frame. I would thus need the frame to work as a sort of sliding window, in which the first window goes from 0 to 499, the second from 1to 500 and so on until the end of the time-points in the ndarray, and an associated func that will select the points to plot on the basis of those frames. I do not know how to implement this.
I add the code to create a realistic signal, to simply detect the peaks and to plot the 'static' version of the plot I would like to animate:
import neurokit2 as nk
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
#create realistic data
data = nk.ecg_simulate(duration = 50, sampling_rate = 100, noise = 0.05,\
random_state = 1)
#scale data
scaler = MinMaxScaler()
scaled_arr = scaler.fit_transform(data.reshape(-1,1))
#find peaks
peak = find_peaks(scaled_arr.squeeze(), height = .66,\
distance = 60, prominence = .5)
#plot
plt.plot(scaled_arr[0:500])
plt.scatter(peak[0][peak[0] < 500],\
peak[1]['peak_heights'][peak[0] < 500],\
color = 'red')
I've created an animation using the data you presented; I've extracted the data in 500 increments for 5000 data and updated the graph. To make it easy to extract the data, I have created an index of 500 rows, where id[0] is the start row, id1 is the end row, and the number of frames is 10. This code works, but the initial settings and dataset did not work in the scatter plot, so I have drawn the scatter plot directly in the loop process.
import neurokit2 as nk
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.signal import find_peaks
import numpy as np
#create realistic data
data = nk.ecg_simulate(duration = 50, sampling_rate = 100, noise = 0.05, random_state = 1)
#scale data
scaler = MinMaxScaler()
scaled_arr = scaler.fit_transform(data.reshape(-1,1))
#find peaks
peak = find_peaks(scaled_arr.squeeze(), height = .66, distance = 60, prominence = .5)
ymin, ymax = min(scaled_arr), max(scaled_arr)
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot([],[], lw=2)
scat = ax.scatter([], [], s=20, facecolor='red')
idx = [(s,e) for s,e in zip(np.arange(0,len(scaled_arr), 1), np.arange(499,len(scaled_arr)+1, 1))]
def init():
line.set_data([], [])
return line,
def animate(i):
id = idx[i]
#print(id[0], id[1])
line.set_data(np.arange(id[0], id[1]), scaled_arr[id[0]:id[1]])
x = peak[0][(peak[0] > id[0]) & (peak[0] < id[1])]
y = peak[1]['peak_heights'][(peak[0] > id[0]) & (peak[0] < id[1])]
#scat.set_offsets(x, y)
ax.scatter(x, y, s=20, c='red')
ax.set_xlim(id[0], id[1])
ax.set_ylim(ymin, ymax)
return line,scat
anim = FuncAnimation(fig, animate, init_func=init, frames=50, interval=50, blit=True)
plt.show()
Probably not exactly what you want, but hope it can help,
import neurokit2 as nk
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from sklearn.preprocessing import MinMaxScaler
import numpy as np
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# This function is called periodically from FuncAnimation
def animate(i, xs, ys):
xs = xs[i]
ys = ys[i]
# Draw x and y lists
ax.clear()
ax.plot(xs, ys)
if __name__=="__main__":
data = nk.ecg_simulate(duration = 50, sampling_rate = 100, noise = 0.05, random_state = 1)
scaler = MinMaxScaler()
scaled_arr = scaler.fit_transform(data.reshape(-1,1))
ys = scaled_arr.flatten()
ys = [ys[0:50*i] for i in range(1, int(len(ys)/50)+1)]
xs = [np.arange(0, len(ii)) for ii in ys ]
ani = animation.FuncAnimation(fig, animate, fargs=(xs, ys), interval=500)
ani.save('test.gif')

Animate Points Movement in Scatter plot (matplotlib python)

I'm not a beginner, but I'm also not advanced dev of python code.
I'm been trying to animate points movement in scatter plot and to put annotation on every point. All I have done is animation of one point with no annotation. I've searched similar solutions, but it's so confusing. Any help is welcome. This is what I've done.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import matplotlib.animation as animation
frame_count = 0
points = reading_file("some_data") # this method is not of intrest
def make_one_point(i):
global frame_count, points
ex = [1]
ey = [1]
ez = [1]
point = points[i]
frame = point[frame_count]
ex[0] = frame[0]
ey[0] = frame[1]
ez[0] = frame[2]
frame_count += 1
return ex, ey, ez
def update(i):
global frame_count, points
if frame_count < len(points[i]):
return make_one_point(i)
else:
frame_count = 0
return make_one_point(i)
fig = plt.figure()
ax1 = fig.add_subplot(111, projection='3d')
ax1.set_xlim3d(-500, 2000)
ax1.set_ylim3d(-500, 2000)
ax1.set_zlim3d(0, 2000)
x = [1]
y = [1]
z = [1]
scat = ax1.scatter(x,y,z)
def animate(i):
scat._offsets3d = update(0)
ani = animation.FuncAnimation(fig, animate,
frames=len(points[10]),
interval=100, repeat=True)
plt.show()
How to animate more points at the same time, and put annontation on every one of them? There are 50 points, and I'm not so consern about efficiency, just to make it work.
This code output is moving one point animation
It turns out animating Text in 3D was harder than I anticipated. Not surprisingly, I was able to find the solution to the problem in an answer from #ImportanceOfBeingErnest. I then simply adapted the code I had already written in a previous answer, and produced the following code:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D, proj3d
import matplotlib.animation as animation
N_points = 10
def update(num, my_ax):
# the following corresponds to whatever logic must append in your code
# to get the new coordinates of your points
# in this case, we're going to move each point by a quantity (dx,dy,dz)
dx, dy, dz = np.random.normal(size=(3,N_points), loc=0, scale=1)
debug_text.set_text("{:d}".format(num)) # for debugging
x,y,z = graph._offsets3d
new_x, new_y, new_z = (x+dx, y+dy, z+dz)
graph._offsets3d = (new_x, new_y, new_z)
for t, new_x_i, new_y_i, new_z_i in zip(annots, new_x, new_y, new_z):
# animating Text in 3D proved to be tricky. Tip of the hat to #ImportanceOfBeingErnest
# for this answer https://stackoverflow.com/a/51579878/1356000
x_, y_, _ = proj3d.proj_transform(new_x_i, new_y_i, new_z_i, my_ax.get_proj())
t.set_position((x_,y_))
return [graph,debug_text]+annots
# create N_points initial points
x,y,z = np.random.normal(size=(3,N_points), loc=0, scale=10)
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111, projection="3d")
graph = ax.scatter(x, y, z, color='orange')
debug_text = fig.text(0, 1, "TEXT", va='top') # for debugging
annots = [ax.text2D(0,0,"POINT") for _ in range(N_points)]
# Creating the Animation object
ani = animation.FuncAnimation(fig, update, fargs=[ax], frames=100, interval=50, blit=True)
plt.show()

Python multi-body animation does not work

I am stuck with a python animation in which I am trying to animate a system of particles initially arranged in a 2 dimensional hexagonal lattice and gradually spreading out as per rule: xpos1[i]=xpos1[i]+L/10.0. If any particle goes out of the window limit, they are brought in through the other side
if xpos1[i]>L*3: # translate back the particle if it goes out of window limit 0 to L*3
xpos1[i]=xpos1[i]-L*3
elif xpos1[i]<0:
xpos1[i]=L*3-xpos1[i]
And all the updates of position are stored in two list xpos1 and ypos1. This is done for several time steps.
I wish to visualize the time evolution of the system by turning it to an animation. My code is as follows. I have never done matplotlib animations before and actually copied the 'animation' part from another program where it works fine. But it does not work for mine.
from numpy import*
import matplotlib.pyplot as plt
import matplotlib.animation as animation
sigma=3.4e-10 # dist of closest approach
L=4.8e-10 # lattice constant = sigma*2**0.5 (Let)
xpos1=zeros(18,float)
ypos1=zeros(18,float)
# ~~~~~~~~~~~ Setting up the hexagonal lattice ~~~~~~~~~~~~~~~~~~~~~~
k=0
for x in range(0,6,1):
for y in range(0,6,1):
if (x+y)%2==0:
xpos1[k]=x*L*.5+.25*L
ypos1[k]=y*L*.5+.25*L
k=k+1
#~~~~~~~~~~~~~~~~~~TIME EVOLUTION~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t = 4.5e-12
iteration=1
while t<=1e-9:
for i in range(18):
xpos1[i]=xpos1[i]+L/10.0
ypos1[i]=ypos1[i]+L/10.0
if xpos1[i]>L*3: # translate back the particle if it goes out of window limit 0 to L*cell
xpos1[i]=xpos1[i]-L*3
elif xpos1[i]<0:
xpos1[i]=L*3-xpos1[i]
if ypos1[i]>L*3: # translate back the particle if it goes out of window limit 0 to L*cell
ypos1[i]=ypos1[i]-L*3
elif ypos1[i]<0:
ypos1[i]=L*3-ypos1[i]
t = t + 4.5e-12
#~~~~~~~~~~~~~~~~~ ANIMATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def simData():
for i in range(18):
x=xpos1[i]
y=ypos1[i]
yield x,y
def simPoints(simData):
x,y= simData[0],simData[1]
line.set_data(x,y)
return line
fig = plt.figure()
ax = fig.add_subplot(111)
line,= ax.plot([],[],'bo',ms=8)
ax.set_ylim(0 , L*3)
ax.set_xlim(0 , L*3)
ani = animation.FuncAnimation(fig, simPoints, simData, blit=True , interval=200)
plt.show()
Can somebody tell me how to make the animation successfully?
Your animation update (and init if you have one) must return an iterable.
def simPoints(simData):
x, y = simData[0], simData[1]
line.set_data(x, y)
return line, # added a comma to return a tuple
You may also need to set blit=False if you are on mac os
ani = animation.FuncAnimation(fig, simPoints, simData, blit=False, interval=200)
Edit:
Here is a minimum working example that shows 18 random points - you'll have to change the random generation to the pattern you want for the points on your lattice.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.random.random(18)
y = np.random.random(18)
def simData():
"""updates the points position on your lattice.
replace with your own code - can call a helper function to accomplish this task
"""
x = np.random.random(18)
y = np.random.random(18)
yield x, y
def simPoints(simData):
"""initializes the points position on your lattice.
replace with your own code - can call a helper function to accomplish this task
"""
x = np.random.random(18)
y = np.random.random(18)
line.set_data(x, y)
return line,
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot(x, y,'bo', ms=8)
ani = animation.FuncAnimation(fig, simPoints, simData, blit=False, interval=200)
plt.show()

Using Matplotlib-Patch inside an animation

I try to generate an empty patch to be able to set data later on. In order to explain my problem better, i will give an example:
from matplotlib import pyplot as plt
import matplotlib.animation as animation
x = range(10)
y = [i**2 for i in x]
figure = plt.figure()
ax1 = figure.add_subplot(111, xlim=(0,10), ylim=(0,100))
my_line, = ax1.plot([],[], 'o-')
def init():
my_line.set_data([], [])
return my_line,
i = 0
def animate(_):
global i
my_line.set_data(x[0:i], y[0:i])
i = (i+1)%(len(x)+1)
return my_line,
ani = animation.FuncAnimation(figure, animate, repeat=True, blit=True, init_func=init)
plt.show()
Now, I to add a shape, which I define its edge points randomly. I need to use the same structure as I used for plotting lines inside the init() block: my_line.set_data([], []) . However, I couldn't succeed.
I use the same structure as the example provided in the matplotlib tutorial . My verts are generated from a function.
When I try using: foo = patches.PathPatch([], facecolor='red', lw=2, alpha=0.0) I get
<matplotlib.patches.PathPatch at 0x335d390>
But later, I cannot set the path data. I tried using foo.set_data and foo.set_path but PathPatch doesn't have such attributes and therefore, they didn't work. I checked this page but I couldn't get anywhere. I checked all of the tutorials I could find, but none of them helped.
As a workaround, I used ax1.add_patch() command and have set the alpha value to 0. This helped to some extend but, as I have to enter data to be able to use this command, all of the shapes become visible at the final step of the animation for a very short time and, as I save my figure in that moment, it yields unfavorable results.
Any help would be appreciated...
I'm not sure what shape you're using, if you use a polygon, you can update the vertices of a polygon with the set_xy method and create the initial polygon with vertices that are all equal to each other. Example below. If you need a completely arbitrary shape, you might be better off plotting lines and using fill_between to draw it.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
# Create the figure and axis
fig = plt.figure()
ax = plt.axes(xlim=(0, 10), ylim=(0, 10))
# mMke the initial polygon with all vertices set to 0
pts = [[0,0], [0,0], [0,0], [0,0]]
patch = plt.Polygon(pts)
ax.add_patch(patch)
def init():
return patch,
def animate(i):
# Randomly set the vertices
x1= 5*np.random.rand((1))[0]
x2= 5*np.random.rand((1))[0]
x3= 5*np.random.rand((1))[0] + 5
x4= 5*np.random.rand((1))[0] + 5
y1= 5*np.random.rand((1))[0]
y2= 5*np.random.rand((1))[0]
y3= 5*np.random.rand((1))[0] + 5
y4= 5*np.random.rand((1))[0] + 5
patch.set_xy([[x1,y1], [x2,y2], [x3,y3], [x4,y4]])
return patch,
anim = animation.FuncAnimation(fig,animate,init_func=init,frames=36,interval=1000,blit=True)
plt.show()

How to update a plot in matplotlib

I'm having issues with redrawing the figure here. I allow the user to specify the units in the time scale (x-axis) and then I recalculate and call this function plots(). I want the plot to simply update, not append another plot to the figure.
def plots():
global vlgaBuffSorted
cntr()
result = collections.defaultdict(list)
for d in vlgaBuffSorted:
result[d['event']].append(d)
result_list = result.values()
f = Figure()
graph1 = f.add_subplot(211)
graph2 = f.add_subplot(212,sharex=graph1)
for item in result_list:
tL = []
vgsL = []
vdsL = []
isubL = []
for dict in item:
tL.append(dict['time'])
vgsL.append(dict['vgs'])
vdsL.append(dict['vds'])
isubL.append(dict['isub'])
graph1.plot(tL,vdsL,'bo',label='a')
graph1.plot(tL,vgsL,'rp',label='b')
graph2.plot(tL,isubL,'b-',label='c')
plotCanvas = FigureCanvasTkAgg(f, pltFrame)
toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
toolbar.pack(side=BOTTOM)
plotCanvas.get_tk_widget().pack(side=TOP)
You essentially have two options:
Do exactly what you're currently doing, but call graph1.clear() and graph2.clear() before replotting the data. This is the slowest, but most simplest and most robust option.
Instead of replotting, you can just update the data of the plot objects. You'll need to make some changes in your code, but this should be much, much faster than replotting things every time. However, the shape of the data that you're plotting can't change, and if the range of your data is changing, you'll need to manually reset the x and y axis limits.
To give an example of the second option:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)
# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma
for phase in np.linspace(0, 10*np.pi, 500):
line1.set_ydata(np.sin(x + phase))
fig.canvas.draw()
fig.canvas.flush_events()
You can also do like the following:
This will draw a 10x1 random matrix data on the plot for 50 cycles of the for loop.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
for i in range(50):
y = np.random.random([10,1])
plt.plot(y)
plt.draw()
plt.pause(0.0001)
plt.clf()
This worked for me. Repeatedly calls a function updating the graph every time.
import matplotlib.pyplot as plt
import matplotlib.animation as anim
def plot_cont(fun, xmax):
y = []
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
def update(i):
yi = fun()
y.append(yi)
x = range(len(y))
ax.clear()
ax.plot(x, y)
print i, ': ', yi
a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
plt.show()
"fun" is a function that returns an integer.
FuncAnimation will repeatedly call "update", it will do that "xmax" times.
This worked for me:
from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
clear_output(wait=True)
y = np.random.random([10,1])
plt.plot(y)
plt.show()
I have released a package called python-drawnow that provides functionality to let a figure update, typically called within a for loop, similar to Matlab's drawnow.
An example usage:
from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
# can be arbitrarily complex; just to draw a figure
#figure() # don't call!
plot(t, x)
#show() # don't call!
N = 1e3
figure() # call here instead!
ion() # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
x = sin(2 * pi * i**2 * t / 100.0)
drawnow(draw_fig)
This package works with any matplotlib figure and provides options to wait after each figure update or drop into the debugger.
In case anyone comes across this article looking for what I was looking for, I found examples at
How to visualize scalar 2D data with Matplotlib?
and
http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop
(on web.archive.org)
then modified them to use imshow with an input stack of frames, instead of generating and using contours on the fly.
Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.
def animate_frames(frames):
nBins = frames.shape[0]
frame = frames[0]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
for k in range(nBins):
frame = frames[k]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
del tempCS1
fig.canvas.draw()
#time.sleep(1e-2) #unnecessary, but useful
fig.clf()
fig = plt.figure()
ax = fig.add_subplot(111)
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)
I also found a much simpler way to go about this whole process, albeit less robust:
fig = plt.figure()
for k in range(nBins):
plt.clf()
plt.imshow(frames[k],cmap=plt.cm.gray)
fig.canvas.draw()
time.sleep(1e-6) #unnecessary, but useful
Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg
Thank you for the help with everything.
All of the above might be true, however for me "online-updating" of figures only works with some backends, specifically wx. You just might try to change to this, e.g. by starting ipython/pylab by ipython --pylab=wx! Good luck!
Based on the other answers, I wrapped the figure's update in a python decorator to separate the plot's update mechanism from the actual plot. This way, it is much easier to update any plot.
def plotlive(func):
plt.ion()
#functools.wraps(func)
def new_func(*args, **kwargs):
# Clear all axes in the current figure.
axes = plt.gcf().get_axes()
for axis in axes:
axis.cla()
# Call func to plot something
result = func(*args, **kwargs)
# Draw the plot
plt.draw()
plt.pause(0.01)
return result
return new_func
Usage example
And then you can use it like any other decorator.
#plotlive
def plot_something_live(ax, x, y):
ax.plot(x, y)
ax.set_ylim([0, 100])
The only constraint is that you have to create the figure before the loop:
fig, ax = plt.subplots()
for i in range(100):
x = np.arange(100)
y = np.full([100], fill_value=i)
plot_something_live(ax, x, y)

Categories

Resources