Updating matplotlib figures in real time for data acquisition - python

I want to plot data in matplotlib in real time. I want to open a figure once at the start of the programme, then update the figure when new data is acquired. Despite there being a few similar questions out there, none quite answer my specific question.
I want each set of data points new_data1 and new_data2 to be plotted on the same figure at the end of each while loop i.e. one line after the first while loop, two lines on the same figure after the second while loop etc. Currently they are all plotted together, but only right at the end of the programme, which is no use for real time data acquisition.
import matplotlib.pyplot as plt
import numpy
hl, = plt.plot([], [])
def update_line(hl, new_datax, new_datay):
hl.set_xdata(numpy.append(hl.get_xdata(), new_datax))
hl.set_ydata(numpy.append(hl.get_ydata(), new_datay))
plt.xlim(0, 50)
plt.ylim(0,200)
plt.draw()
x = 1
while x < 5:
new_data1 = []
new_data2 = []
for i in range(500):
new_data1.append(i * x)
new_data2.append(i ** 2 * x)
update_line(hl, new_data1, new_data2)
x += 1
else:
print("DONE")
This programme plots all 5 lines, but at the end of the programme. I want each line to be plotted after one another, after the while loop is completed. I have tried putting in plt.pause(0.001) in the function, but it has not worked.
This programme is different from the one that has been put forward - that programme only plots one graph and does not update with time.

If I correctly understood your specifications, you can modify just a bit your MWE as follows:
import matplotlib.pyplot as plt
import numpy
fig = plt.figure(figsize=(11.69,8.27))
ax = fig.gca()
ax.set_xlim(0, 50)
ax.set_ylim(0,200)
hl, = plt.plot([], [])
def update_line(hl, new_datax, new_datay):
# re initialize line object each time if your real xdata is not contiguous else comment next line
hl, = plt.plot([], [])
hl.set_xdata(numpy.append(hl.get_xdata(), new_datax))
hl.set_ydata(numpy.append(hl.get_ydata(), new_datay))
fig.canvas.draw_idle()
fig.canvas.flush_events()
x = 1
while x < 10:
new_data1 = []
new_data2 = []
for i in range(500):
new_data1.append(i * x)
new_data2.append(i ** 2 * x)
update_line(hl, new_data1, new_data2)
# adjust pause duration here
plt.pause(0.5)
x += 1
else:
print("DONE")
which displays :

Not sure, if I am reading the requirements right but below is a blueprint. Please change it to suit your requirements. You may want to change the function Redraw_Function and edit the frames (keyword parameter, which is np.arange(1,5,1) ) in the FuncAnimation call. Also interval=1000 means 1000 milliseconds of delay.
If you are using Jupyter then comment out the second last line (where it says plt.show()) and uncomment the last line. This will defeat your purpose of real time update but I am sorry I had trouble making it work real time in Jupyter. However if you are using python console or official IDLE please run the code as it is. It should work nicely.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
plot, = plt.plot([],[])
def init_function():
ax.set_xlim(0,50)
ax.set_ylim(0,250)
return plot,
def Redraw_Function(UpdatedVal):
new_x = np.arange(500)*UpdatedVal
new_y = np.arange(500)**2*UpdatedVal
plot.set_data(new_x,new_y)
return plot,
Animated_Figure = FuncAnimation(fig,Redraw_Function,init_func=init_function,frames=np.arange(1,5,1),interval=1000)
plt.show()
# Animated_Figure.save('MyAnimated.gif',writer='imagemagick')
When you run the code, you obtain the below result. I tried to keep very little code but I am sorry, if your requirement was totally different.
Best Wishes,

Related

Real time plotting in matplotlib from a numpy array

My task is to plot a numpy array in real time using matplotlib. Please note that I don't want to use animation function to do this.
import numpy as np
import time
from matplotlib.lines import Line2D
import matplotlib
class Plot:
def __init__(self,f,axis,data):
self.fig = f
self.axis = axis
self.data = data
def plotting(self,i):
xs = [self.data[i,0],self.data[i+1,0]]
ys = [self.data[i,1],self.data[i+1,1]]
line, = self.axis.plot(xs,ys,'g-')
self.fig.canvas.draw()
data = np.random.rand(10,2) #numpy array
f = plt.figure()
axis = f.add_axes([0,0,0.9,0.9])
plotData = Plot(f,axis,data)
for i in range(len(data)-1):
plotData.plotting(i)
time.sleep(1)
plt.show()
But everytime I run this code it returns me one empty figure. How do I rectify it?
import matplotlib.pyplot as plt
import numpy as np
# use ggplot style for more sophisticated visuals
plt.style.use('ggplot')
def live_plotter(x_vec,y1_data,line1,identifier='',pause_time=0.1):
if line1==[]:
# this is the call to matplotlib that allows dynamic plotting
plt.ion()
fig = plt.figure(figsize=(13,6))
ax = fig.add_subplot(111)
# create a variable for the line so we can later update it
line1, = ax.plot(x_vec,y1_data,'-o',alpha=0.8)
#update plot label/title
plt.ylabel('Y Label')
plt.title('Title: {}'.format(identifier))
plt.show()
# after the figure, axis, and line are created, we only need to update the y-data
line1.set_ydata(y1_data)
# adjust limits if new data goes beyond bounds
if np.min(y1_data)<=line1.axes.get_ylim()[0] or np.max(y1_data)>=line1.axes.get_ylim()[1]:
plt.ylim([np.min(y1_data)-np.std(y1_data),np.max(y1_data)+np.std(y1_data)])
# this pauses the data so the figure/axis can catch up - the amount of pause can be altered above
plt.pause(pause_time)
# return line so we can update it again in the next iteration
return line1
A few notes on the function above:
line1.set_ydata(y1_data) can also be switched to line1.set_data(x_vec,y1_data) to change both x and y data on the plots.
plt.pause() is necessary to allow the plotter to catch up - I've been able to use a pause time of 0.01s without any issues
The user will need to return line1 to control the line as it is updated and sent back to the function
The user can also customize the function to allow dynamic changes of title, x-label, y-label, x-limits, etc.

Individual colors for animated 3-D scatter plot in Python

I'm trying to make an animated 3-D scatter plot with the ability to plot a dynamic number of classes as different colors. This is one of the attempts. I've included the whole code in case it is helpful, and marked the trouble spot with a row of stars:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
from random import uniform
x_arr,y_arr,depth_arr,time_arr,ml_arr,cluster_arr = np.loadtxt(data, unpack=5, usecols=(0, 1, 2, 5, 6))
class Point:
def __init__(self,x,y,depth,time,cluster):
self.x=x
self.y=y
self.depth=depth
self.time=time
self.cluster=cluster
points = []
for i in range(0,len(x_arr)):
points.append(Point(x_arr[i],y_arr[i],depth_arr[i],time_arr[i],cluster_arr[i]))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_xlim(min(x_arr), max(x_arr))
ax.set_ylim(min(y_arr), max(y_arr))
ax.set_zlim(min(depth_arr), max(depth_arr))
colors_1 = plt.cm.jet(np.linspace(0,max(cluster_arr),max(cluster_arr)+1))
colors = colors_1.reshape(-1,4)
def plot_points(time):
x = []
y = []
z = []
clust = []
points_cp = list(np.copy(points))
for i in range(0,(int(max(cluster_arr))+1)):
for event in points_cp:
if event.cluster == i:
if event.time < time:
points_cp.remove(event)
elif event.time <= time + 86400:
x.append(event.x)
y.append(event.y)
z.append(event.depth)
clust.append(event.cluster)
points_cp.remove(event)
# **************************************************************
color_ind = 0
first_ind = 0
last_ind = 0
for i in range(0,len(x)):
if clust[i] != color_ind:
last_ind = i
for i in range(0,len(x)):
ax.scatter(x[first_ind:last_ind],y[first_ind:last_ind],z[first_ind:last_ind],c=colors[int(color_ind)])
color_ind = clust[i]
first_ind = i
time = np.linspace(min(time_arr),max(time_arr),100)
ani = animation.FuncAnimation(fig,plot_points,time)
plt.show()
This gives me a plot with the correct colors, but once a point is plotted, it remains throughout the entire animation.
I have also tried set_x, set_color, etc., but this doesn't work with a loop (it is updated with each iteration, so that only the last class is actually plotted), and I need to use a for loop to accommodate a variable number of classes. I've tried using a colormap with a fixed extent, but have been unsuccessful, as colormapping doesn't work with the plot function, and I haven't been able to get the rest of the code to work with a scatter function.
Thanks in advance for your help, and my apologies if the code is a little wonky. I'm pretty new to this.

Limit the Number of Open Matplotlib Figures in Python 3

I have a small python 3 script:
import matplotlib.pyplot as plt;
i = 0;
while(i < 40):
x = [1,2,3,4,5];
y = [1,2,3,4,5];
fig = plt.figure();
grid = plt.GridSpec(1, 1)
axis = fig.add_subplot(grid[0,0]);
axis.bar(x,y);
fig.canvas.flush_events()
while(len(plt.get_fignums()) > 10):
pass;
plt.show(block=False);
i += 1;
My goal is to plot 40 plots. I want the first 10 to plot immediately, and then the next figures will only plot if one of the open 10 plots are closed, one by one. This script seems to almost achieve what I want to do, but it crashes when I try to close one of the first 10 plots. Why does this happen? Thanks
while loop is not proper idea for this situation, your code should listens for close event of figures, then act as desired. these few lines of code may help you:
import matplotlib.pyplot as plt;
desiredNumberOfPlots_initialPopulation=1
desiredNumberOfPlots_total=3
def figOnce():
x = [1,2,3,4,5];
y = [1,2,3,4,5];
fig = plt.figure();
grid = plt.GridSpec(1, 1)
axis = fig.add_subplot(grid[0,0]);
axis.bar(x,y);
fig.canvas.flush_events()
fig.canvas.mpl_connect('close_event', handle_close)
plt.show(block=False);
global desiredNumberOfPlots_total
desiredNumberOfPlots_total-=1
def handle_close(evt):
global desiredNumberOfPlots_total
if desiredNumberOfPlots_total>0:
figOnce()
i=1
while(i<=desiredNumberOfPlots_initialPopulation):
print(i)
i+=1
figOnce()

Simple matplotlib.animation graph geting slower very fast and than stoping

I've started to learn about matplotlib functions because i wanted to visualize data i was receiving via websocket. For that i made a dummy program that mimics the behaviour of my main program but has added the functionality of mathplotlib. what i noticed is the program takes more and more time to finish each loop and eventually 'freezes'. i managed to extend it life by changing the interval in animation.FuncAnimation from 1000 to 10000. But that just the program to plot sometimes up to 9s for 1 new peace of data. I believe the problem lays in a inappropriate way of cleaning old plots. But i don't know where exactly i did the mistake
import time
import datetime
import timeit
import queue
import os
import random
import copy
import matplotlib.pyplot as plt
import matplotlib.animation as animation
q = queue.Queue()
beta=[0,]
b=False
czas=[]
produkty=["primo"]
cena=[[] for _ in range(len(produkty))]
fig=plt.figure()
#ax1=fig.add_subplot(1,1,1)
#ax2=fig.add_subplot(1,1,1)
ax1=plt.subplot(1,1,1)
ax2=plt.subplot(1,1,1)
def animate(i):
ax1.clear()
ax2.clear()
ax1.plot(czas,cena[0])
ax2.plot(czas,beta)
while True:
time.sleep(1)
alpfa=time.time()
#input('press enter')
rand_produkt=random.choice(produkty)
rand_price=random.randint(1,10)
rand_czas=time.ctime()
alfa={'type':'ticker','price':rand_price,'product_id':rand_produkt,'time':rand_czas}
q.put(alfa)
if q.not_empty:
dane=q.get()
typ=dane.get('type',None)
if typ=='ticker':
price=dane.get('price', None)
pair=dane.get('product_id',None)
t=dane.get('time', None)
b=True
if b==True:
b=False
produkt_id=produkty.index(pair)
cena[produkt_id].append(float(price))
czas.append(t)
plt.ion()
ani=animation.FuncAnimation(fig,animate,interval=1000)#, blit=True)repeat=True)
plt.show()
plt.pause(0.001)
#fig.clf()
beta.append(time.time()-alpfa)
print(beta[-1])
The problem with your code is that you call a new animation in you while loop. Hence this will cause slow down down the line. It is better to initiate your plot ones. One trick may be to update the object data directly as such:
from matplotlib.pyplot import subplots, pause, show
from numpy import sin, pi
fig, ax = subplots()
x = [0]
y = [sin(2 * pi * x[-1])]
p1, = ax.plot(x, y)
show(block = False)
while True:
# update data
x.append(x[-1] + .1)
y.append(sin(2 * pi * x[-1]))
p1.set_data(x, y) # update data
ax.relim() # rescale axis
ax.autoscale_view()# update view
pause(1e-3)

Python Animated plotting, one point at a time

I have a set of points [index, minimum] and I would like to scatter one point i (index[i],minimum[i]) at a time so that I can see the evolution of the plot.
I would like to know how I can do that. I have tried a time- delay like:
plt.figure()
for i in range (np.size(index)):
plt.plot(index[i], minimum[i],'*')
plt.show()
time.sleep(1)
it did not work.
Thanks in advance.
Might seem stupid but did you import the time library ? Also there is no indentation, is your code really like that or that's a copy/paste fail ?
Edit: Answer in comments, use plt.pause(1), see http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.pause
you should use an "animate" plot :
http://matplotlib.org/api/animation_api.html
and here some good example :
http://matplotlib.org/examples/animation/index.html
You do have to use a nan arrays to plot empty values then update your array as you move in time. Here is a working example:
import numpy as np
import matplotlib.pyplot as plt
import time
nbPoints = 100
nanArray = np.array(np.ones(nbPoints))
nanArray[:] = np.nan
index = range(nbPoints)
minimum = np.random.randint(5, size=nbPoints)
minimumPlotData = nanArray
fig = plt.figure()
ax = plt.subplot(111)
ax.set_xlim(0, nbPoints)
ax.set_ylim(min(minimum), max(minimum))
li, = ax.plot(index,minimumPlotData, marker = 'o', linestyle="")
fig.canvas.draw()
plt.show(block=False)
for i in range(nbPoints):
minimumPlotData[i]=minimum[i]
li.set_ydata(minimumPlotData)
fig.canvas.draw()
time.sleep(1)

Categories

Resources