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).
Related
I have just started learning python to plot realtime gragh. I have tried solutions provided on stackoverflow but none of them are working. Below is my code and it isn't woorking. Please help
import numpy as np
import matplotlib.pyplot as plt
import pyautogui as pg
from matplotlib.animation import FuncAnimation
%matplotlib notebook
binSize = 512
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
def animate(i):
try:
while True:
x, y = pg.position()
f.append(x)
except KeyboardInterrupt:
print('')
# f.append(15)
if len(f)<binSize :
plt.cla()
plt.plot(f, color='c',LineWidth=1.5,label="Noisy")
else:
plt.cla()
plt.plot(f[-binSize:],color='c',LineWidth=1.5,label="Noisy")
ani = FuncAnimation(plt.gcf(),animate,interval=1);
So I have updated the code and trying to draw two subplots but after sometime
Upper graph stopped clearing the canvas (Mouse X coordinates)
Lower graph stopped updating the plot (FFT)
When data grows beyond the binSize, notebook freezes and plots update really slowly
%matplotlib notebook
binSize = 256
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
t = 0
dt = 1
fig,axs = plt.subplots(2,1)
def animate(i):
x, y = pg.position()
f.append(x)
n = len(f)
if n<binSize :
plt.sca(axs[0])
plt.cla()
plt.plot(f, color='c',LineWidth=1.5,label="MOUSE")
else:
fhat = np.fft.fft(f,binSize)
PSD = fhat*np.conj(fhat)/binSize
freq = (1/(dt*binSize))*np.arange(binSize)
L = np.arange(1,np.floor(binSize/2),dtype='int')
# update the code third time
axs[0].clear()
axs[0].plot(f[-binSize:], color='c',LineWidth=1.5,label="MOUSE")
# axs[0].xlim(0,binSize) # this stopped the FFT graph to be plotted
# plt.cla()
axs[1].clear()
axs[1].plot(freq[L],PSD[L],color='r',LineWidth=2,label="FFT")
# plt.xlim(t[0],t[-1])
# plt.legend()
# plt.sca(axs[1])
# plt.plot(freq[L],PSD[L],color='c',LineWidth=2,label="Mouse FFT")
# plt.xlim(0,300)
# plt.legend()
# plt.cla()
# plt.plot(f[-binSize:],color='c',LineWidth=1.5,label="Mouse")
ani = FuncAnimation(plt.gcf(),animate,interval=dt)
To make it faster you may reduce data like in other answer
f.pop(0)
I use also different method to update plot which works much faster on my computer.
I create empty plots at start
# needs `,` to get first element from list
p1, = axs[0].plot([], [], color='c', LineWidth=1.5, label="MOUSE")
p2, = axs[1].plot([], [], color='r', LineWidth=2, label="FFT")
and later only update data in plots without clear() and plot() again
xdata = range(len(f))
ydata = f
p1.set_data(xdata, ydata)
and
# replace data in plot
xdata = range(binSize)
ydata = f[-binSize:]
p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
#p1.set_ydata(ydata)
# replace data in plot
xdata = freq[:(binSize//2)]
ydata = PSD[:(binSize//2)]
p2.set_data(xdata, ydata)
It needs only to run code which rescale plot
# rescale view
axs[0].relim()
axs[0].autoscale_view(True,True,True)
axs[1].relim()
axs[1].autoscale_view(True,True,True)
animate() has to also return new plots
# return plots
return p1, p2
And FuncAnimation() has to blit them
ani = FuncAnimation(..., blit=True)
EDIT:
Animation works much, much faster also because I run it normally python script.py, not in Jupuyter Notebook
EDIT:
when I run normally I found one problem which I could find solution: it doesn't update values/ticks on axes. Jupyter Notebook doesn't have this problem.
import numpy as np
import matplotlib.pyplot as plt
import pyautogui as pg
from matplotlib.animation import FuncAnimation
%matplotlib notebook
binSize = 256
f = []
t = 0
dt = 1
fig, axs = plt.subplots(2, 1)
# needs `,` to get first element from list
p1, = axs[0].plot([], [], color='c', LineWidth=1.5, label="MOUSE")
p2, = axs[1].plot([], [], color='r', LineWidth=2, label="FFT")
freq = np.arange(binSize)/(dt*binSize)
def animate(i):
x, y = pg.position()
n = len(f)
if n < binSize :
f.append(x)
# replace data in plot
xdata = range(len(f))
ydata = f
p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
#p1.set_ydata(ydata)
else:
f.pop(0)
f.append(x)
fhat = np.fft.fft(f, binSize)
PSD = fhat * np.conj(fhat) / binSize
# replace data in plot
#xdata = range(binSize)
ydata = f[-binSize:]
#p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
p1.set_ydata(ydata)
# replace data in plot
xdata = freq[:(binSize//2)]
ydata = PSD[:(binSize//2)]
p2.set_data(xdata, ydata)
# rescale view
axs[0].relim()
axs[0].autoscale_view(True,True,True)
axs[1].relim()
axs[1].autoscale_view(True,True,True)
# return plots
return p1, p2
ani = FuncAnimation(plt.gcf(), animate, interval=dt, blit=True)
plt.show()
You should try this. Instead of clearing the plt clear axs[0] and so on. Also, instead of plotting on plt.plot, plot on axs[0].plot
%matplotlib notebook
binSize = 256
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
t = 0
dt = 1
fig,axs = plt.subplots(2,1)
plt.sca(axs[0])
plt.sca(axs[1])
def animate(i):
x, y = pg.position()
n = len(f)
if n<binSize :
f.append(x*100)
axs[0].clear()
axs[0].plot(f, color='c',LineWidth=1.5,label="MOUSE")
else:
f.pop(0)
f.append(x)
fhat = np.fft.fft(f,binSize)
PSD = fhat*np.conj(fhat)/binSize
freq = (1/(dt*binSize))*np.arange(binSize)
L = np.arange(1,np.floor(binSize/2),dtype='int') # index array of [1,2,3..... binsize/2] type int
axs[0].clear()
axs[0].plot(f[-binSize:], color='c',LineWidth=1.5,label="MOUSE")
axs[1].clear()
axs[1].plot(freq[L],PSD[L],color='r',LineWidth=2,label="FFT")
ani = FuncAnimation(plt.gcf(),animate,interval=dt)
plt.show()
this is my question: if I get a code with a funcAnimation implementation (financial field, grabbing data from a exchange and live plot every interval of time)..how can I run a function every interval in a optimise manner?
Imagine we have this code as example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(-6, 6)
tmax = 1
tmin = -1
t = np.linspace(-1, 1)
def f(x, t):
term = (np.exp(-1*(x-3*t)**2))*np.sin(3*np.pi*(x-t))
return term
y = f(x, tmax)
lines = plt.plot(x, y)
plt.axis([x[0], x[-1], -1, 1])
plt.xlabel('x')
plt.ylabel('f')
counter = [0]
def animate(ts):
y = f(x, ts)
lines[0].set_ydata(y)
plt.legend(['ts=%4.2f' % ts])
#plt.savefig('tmp_%04d.png' % counter)
counter[0] += 1
anim = animation.FuncAnimation(plt.gcf(), animate, frames=t, interval = 1000)
plt.show()
In this case that maybe is so simple, when I run it the code get stuck to the close loop cause by funcAnimation.
What I do? put the function to run inside animate function. But...that is a good way to proceed?
Here we have a portion of my code and one simple example:
fig = plt.figure(figsize=(16,18),facecolor="#232a3b")
def animate(i):
say(5)
graphData('BTC/USDT','1m',8,13,21)
def say(i):
print('Hello World ' + str(i))
while True:
#pair = input('Crypto pair to plot: ')
#timeframe = input('Enter timeframe: ')
say(5)
ani = animation.FuncAnimation(fig, animate,interval=60000)
plt.show()
I tried to write a simple script which updates a scatter plot for every timestep t. I wanted to do it as simple as possible. But all it does is to open a window where I can see nothing. The window just freezes. It is maybe just an small error, but I can not find it.
The the data.dat has the format
x y
Timestep 1 1 2
3 1
Timestep 2 6 3
2 1
(the file contains just the numbers)
import numpy as np
import matplotlib.pyplot as plt
import time
# Load particle positioins
with open('//home//user//data.dat', 'r') as fp:
particles = []
for line in fp:
line = line.split()
if line:
line = [float(i) for i in line]
particles.append(line)
T = 100
numbParticles = 2
x, y = np.array([]), np.array([])
plt.ion()
plt.figure()
plt.scatter(x,y)
for t in range(T):
plt.clf()
for k in range(numbP):
x = np.append(x, particles[numbParticles*t+k][0])
y = np.append(y, particles[numbParticles*t+k][1])
plt.scatter(x,y)
plt.draw()
time.sleep(1)
x, y = np.array([]), np.array([])
The simplest, cleanest way to make an animation is to use the matplotlib.animation module.
Since a scatter plot returns a matplotlib.collections.PathCollection, the way to update it is to call its set_offsets method. You can pass it an array of shape (N, 2) or a list of N 2-tuples -- each 2-tuple being an (x,y) coordinate.
For example,
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
T = 100
numbParticles = 2
particles = np.random.random((T,numbParticles)).tolist()
x, y = np.array([]), np.array([])
def init():
pathcol.set_offsets([[], []])
return [pathcol]
def update(i, pathcol, particles):
pathcol.set_offsets(particles[i])
return [pathcol]
fig = plt.figure()
xs, ys = zip(*particles)
xmin, xmax = min(xs), max(xs)
ymin, ymax = min(ys), max(ys)
ax = plt.axes(xlim=(xmin, xmax), ylim=(ymin, ymax))
pathcol = plt.scatter([], [], s=100)
anim = animation.FuncAnimation(
fig, update, init_func=init, fargs=(pathcol, particles), interval=1000, frames=T,
blit=True, repeat=True)
plt.show()
I finally found a solution. You can do it simply by using this script. I tried to keep it simple:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# Helps me to get the data from the file I want to plot
N = 0
# Load particle positioins
with open('//home//user//data.dat', 'r') as fp:
particles = []
for line in fp:
line = line.split()
particles.append(line)
# Create new Figure and an Axes which fills it.
fig = plt.figure(figsize=(7, 7))
ax = fig.add_axes([0, 0, 1, 1], frameon=True)
border = 100
ax.set_xlim(-border, border), ax.set_xticks([])
ax.set_ylim(-border, border), ax.set_yticks([])
# particle data
p = 18 # number of particles
myPa = np.zeros(p, dtype=[('position', float, 2)])
# Construct the scatter which we will update during animation
scat = ax.scatter(myPa['position'][:, 0], myPa['position'][:, 1])
def update(frame_number):
# New positions
myPa['position'][:] = particles[N*p:N*p+p]
# Update the scatter collection, with the new colors, sizes and positions.
scat.set_offsets(myPa['position'])
increment()
def increment():
global N
N = N+1
# Construct the animation, using the update function as the animation director.
animation = FuncAnimation(fig, update, interval=20)
plt.show()
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()
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)