How to update a plot in matplotlib - python

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)

Related

How can I animate a matplotlib plot from within for loop

I would like to update my matplotlibplot with values calculated in each iteration of a for loop. The idea is that I can see in real time which values are calculated and watch the progress iteration by iteration as my script is running. I do not want to first iterate through the loop, store the values and then perform the plot.
Some sample code is here:
from itertools import count
import random
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
def animate(i, x_vals, y_vals):
plt.cla()
plt.plot(x_vals, y_vals)
if __name__ == "__main__":
x_vals = []
y_vals = []
fig = plt.figure()
index = count()
for i in range(10):
print(i)
x_vals.append(next(index))
y_vals.append(random.randint(0, 10))
ani = FuncAnimation(fig, animate, fargs=(x_vals, y_vals))
plt.show()
Most of the examples I have seen online, deal with the case where everything for the animation is global variables, which I would like to avoid. When I use a debugger to step through my code line by line, the figure does appear and it is animated. When I just run the script without the debugger, the figure displays but nothing is plot and I can see that my loop doesn't progress past the first iteration, first waiting for the figure window to be closed and then continuing.
You should never be using a loop when animating in matplotlib.
The animate function gets called automatically based on your interval.
Something like this should work
def animate(i, x=[], y=[]):
plt.cla()
x.append(i)
y.append(random.randint(0, 10))
plt.plot(x, y)
if __name__ == "__main__":
fig = plt.figure()
ani = FuncAnimation(fig, animate, interval=700)
plt.show()
trying to elaborate on #dumbpotato21 answer, here my attempt:
import random
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
def data():
cnt = 0
x = []
y = []
for i in range(1,10):
# x = []
# y = []
x.append(cnt*i)
y.append(random.randint(0, 10))
cnt += 1
yield x, y, cnt
input('any key to exit !!!')
quit()
def init_animate():
pass
def animate( data, *fargs) :
print('data : ', data, '\n data type : ', type(data), ' cnt : ', data[2])
plt.cla()
x = [i*k for i in data[0]]
y = [i*p for i in data[1]]
plt.plot(x,y)
if __name__ == "__main__":
fig = plt.figure()
k = 3
p = 5
ani = FuncAnimation(fig, animate, init_func=init_animate, frames=data, interval=700, fargs = [k,p])
plt.show()
There are a number of alternatives which might come in handy in different situations. Here is one that I have used:
import matplotlib.pyplot as plt
import numpy as np
from time import sleep
x = np.linspace(0, 30, 51)
y = np.linspace(0, 30, 51)
xx, yy = np.meshgrid(x, y)
# plt.style.use("ggplot")
plt.ion()
fig, ax = plt.subplots()
fig.canvas.draw()
for n in range(50):
# compute data for new plot
zz = np.random.randint(low=-10, high=10, size=np.shape(xx))
# erase previous plot
ax.clear()
# create plot
im = ax.imshow(zz, vmin=-10, vmax=10, cmap='RdBu', origin='lower')
# Re-render the figure and give the GUI event loop the chance to update itself
# Instead of the two lines one can use "plt.pause(0.001)" which, however gives a
# decepracted warning.
# See https://github.com/matplotlib/matplotlib/issues/7759/ for an explanation.
fig.canvas.flush_events()
sleep(0.1)
# make sure that the last plot is kept
plt.ioff()
plt.show()
Additionally, the set_data(...) method of a line plot or imshow object is useful if only the data changes and you don't want to re-drw the whole figure (as this is very time consuming).

Is it possible to change speed during an animation in matplotlib

The question, in brief, is: is it possible (with the tools of matplotlib.animation or other modules for python) to obtain a slow-motion on certain frames of the animation?
Some context:
I have a matplotlib animated plot in which I am varying one variable and showing a contour plot over two other ones. My idea was to slow down the animation while I am near the maximum of the function, so that I can more clearly pinpoint it, while accelerate far from it where there is not much interest.
At the moment, my best idea is to double the frames closest to the maximum, but can someone have a better idea?
Thank you everyone!
Code snippet:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
X = np.linspace(1,10, 100)
Y = np.linspace(1,10, 100)
R = np.linspace(-1, 1, 100)
ims = []
for r in R:
z = func(X, Y, r)
im = plt.imshow(z)
ims.append(im)
if check_r(r):
ims.append(im)
where func() is a function that return a (len(X), len(Y)) array that depends on r (for instance Z[i,j] = X[i]**r * Y[j]**(1-r) or whatever, while check_r() test if r is within the range of the values that need to be maximized.
Your idea is the best, I think. And I've found another way using matplotlib animation. The idea is that use frames as slow delay, by making same points.
In this example just sin curve is plotted but it will be applied other functions.
(most of code is took from here)
import numpy as np
import matplotlib.animation as animation
import matplotlib.pylab as plt
import pandas as pd
TWOPI = 2*np.pi
fig, ax = plt.subplots()
# making frames "delay"
frames = np.arange(0.0, TWOPI, 0.1)
frames = np.insert(frames, 17, [1.7]*5)
frames = np.insert(frames, 16, [1.6]*5)
frames = np.insert(frames, 15, [1.5]*5)
t = np.arange(0.0, TWOPI, 0.001)
s = np.sin(t)
l = plt.plot(t, s)
ax = plt.axis([0,TWOPI,-1,1])
redDot, = plt.plot([0], [np.sin(0)], 'ro')
def animate(i):
redDot.set_data(i, np.sin(i))
return redDot,
myAnimation = animation.FuncAnimation(fig, animate, frames=frames,
interval=100, blit=True, repeat=True)

animating a stem plot in matplotlib

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

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

Updating a matplotlib bar graph?

I have a bar graph which retrieves its y values from a dict. Instead of showing several graphs with all the different values and me having to close every single one, I need it to update values on the same graph. Is there a solution for this?
Here is an example of how you can animate a bar plot.
You call plt.bar only once, save the return value rects, and then call rect.set_height to modify the bar plot.
Calling fig.canvas.draw() updates the figure.
import matplotlib
matplotlib.use('TKAgg')
import matplotlib.pyplot as plt
import numpy as np
def animated_barplot():
# http://www.scipy.org/Cookbook/Matplotlib/Animations
mu, sigma = 100, 15
N = 4
x = mu + sigma*np.random.randn(N)
rects = plt.bar(range(N), x, align = 'center')
for i in range(50):
x = mu + sigma*np.random.randn(N)
for rect, h in zip(rects, x):
rect.set_height(h)
fig.canvas.draw()
fig = plt.figure()
win = fig.canvas.manager.window
win.after(100, animated_barplot)
plt.show()
I've simplified the above excellent solution to its essentials, with more details at my blogpost:
import numpy as np
import matplotlib.pyplot as plt
numBins = 100
numEvents = 100000
file = 'datafile_100bins_100000events.histogram'
histogramSeries = np.loadtext(file)
fig, ax = plt.subplots()
rects = ax.bar(range(numBins), np.ones(numBins)*40) # 40 is upper bound of y-axis
for i in range(numEvents):
for rect,h in zip(rects,histogramSeries[i,:]):
rect.set_height(h)
fig.canvas.draw()
plt.pause(0.001)

Categories

Resources