extra window jupyter notebook matplotlib animation - python

I've found another thread on this site with the same problem that I have. But the solutions of that thread do not help me for some reason. The problem is this, I have this code:
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
a_lijst = np.arange(-1,2,0.1)
fig, ax = plt.subplots()
x = np.arange(-1,1,0.01)
def verandering(X,V,a):
#u -> X'
#v -> Y'
u = V
v = -X - (a*V**3 - V)
return [u,v]
def euler(initx,inity,a,stapgrootte,periode):
#initiƫle condities
X = initx
V = inity
x = []
y = []
for i in range(0,periode):
x.append(X)
y.append(V)
X += stapgrootte*verandering(x[-1],y[-1],a)[0]
V += stapgrootte*verandering(x[-1],y[-1],a)[1]
return x,y
def init():
plt.xlabel('x')
plt.ylabel('y')
plt.xlim(-1,1)
plt.ylim(-2,2)
# animation function. This is called sequentially
def animate(i):
ax.clear()
ax.set_ylim(-1,1)
ax.set_xlim(-2,2)
for coordinaat in coordinaten:
x_i = coordinaat[0]
y_i = coordinaat[1]
x,y = euler(x_i,y_i,a_lijst[i],0.1,100)
ax.plot(x,y)
ax.set_title('a={}'.format(a_lijst[i]))
# call the animator. blit=True means only re-draw the parts that have changed.
anim = FuncAnimation(fig, animate, init_func=init,
frames=30, interval=100)
plt.close()
HTML(anim.to_jshtml())
now, just as in the previous thread, there are 2 plots created but even with the plt.close() and putting HTML(anim.to_jshtml()) in a different cell, and using %%capture on the other cell, I still get 2 plots after executing the HTML(anim.to_jshtml()) code. What am I doing wrong?

I found out that the extra window disappears when I remove the init function (of course also from FuncAnimation) and thus just have the animate function. (This is because of the animate function not having to use the init function, and thus also works when using ax instead of plt in the init function)

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

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

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

Matplotlib create real time animated graph

I am having a hard time setting up my code to create a real time animated graph, my code is graphing after the data is being collected, not showing every iteration. My script runs a regression function then stores in a file, then I access the files and plot them, here is what I have, what do I need to move around or change to have it graph real time? I tried moving the plot functions inside the for loop but that didn't work, any suggestions?
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
num = 10
for idx in range(1,num):
c,e = Regr_magic()
with open("CK_output.txt",'a') as CK:
CK.write("{0},{1}\n".format(idx,c))
with open("error_output.txt",'a') as E:
E.write("{0},{1}\n".format(idx,e))
def animate(i):
pull = open('error_output.txt','r').read()
data = pull.split('\n')
xar = []
yar = []
for each in data:
if len(each)>1:
x,y = each.split(',')
xar.append(float(x))
yar.append(float(y))
ax1.plot(xar, yar)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
FYI, data files contain the following, the iteration number and Ck value or error, so they look like this
1,.0554
2,.0422
3,.0553
4,.0742
5,.0232
Solution for pre-computed results
This makes a decent animation from the data in your output file:
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
with open('error_output.txt') as fobj:
x, y = zip(*([float(x) for x in line.split(',')] for line in fobj))
def animate(n):
line, = plt.plot(x[:n], y[:n], color='g')
return line,
anim = animation.FuncAnimation(fig, animate, frames=len(x), interval=1000)
plt.show()
Solution for a real-time animation as the values are computed
Here a version that allows real-time animation of data produce by regr_magic:
import random
import time
from matplotlib import pyplot as plt
from matplotlib import animation
class RegrMagic(object):
"""Mock for function Regr_magic()
"""
def __init__(self):
self.x = 0
def __call__(self):
time.sleep(random.random())
self.x += 1
return self.x, random.random()
regr_magic = RegrMagic()
def frames():
while True:
yield regr_magic()
fig = plt.figure()
x = []
y = []
def animate(args):
x.append(args[0])
y.append(args[1])
return plt.plot(x, y, color='g')
anim = animation.FuncAnimation(fig, animate, frames=frames, interval=1000)
plt.show()
The class RegrMagic is a helper the mocks Regr_magic(). The __call__method makes an instance of this class behave like a function. It has state and produces the numbers 1, 0.56565, 2, 0.65566 etc. for each call (second number is a random number). It also has a time delay to mimic the computation time.
The important thing is frames(). Replace Regr_magic() with Regr_magic() and should be good to go.
Solution for the concrete problem
A version without mocks:
import random
import time
from matplotlib import pyplot as plt
from matplotlib import animation
def frames():
while True:
yield Regr_magic()
fig = plt.figure()
x = []
y = []
def animate(args):
x.append(args[0])
y.append(args[1])
return plt.plot(x, y, color='g')
anim = animation.FuncAnimation(fig, animate, frames=frames, interval=1000)
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