How to make live graphs plotted in a single one graph? - python

I'm trying to show real-time graphs(dynamic plotting) using python. However, the result didn't show in a single one graph, but generate a new one each second, which didn't mean updating live graph. How can I solve it? Is there any problem in my code?
import serial
import time
import matplotlib.pyplot as plt
from drawnow import drawnow
DataList = []
pcs = serial.Serial('COM4', baudrate = 9600, timeout = 1)
time.sleep(3)
plt.ion()
def makeFig():
plt.plot(DataList, 'rd-')
def getValues():
pcs.write(b"MEASure:VOLTage:DC?\n")
pcsData = pcs.readline().decode('ascii').split('\n\r')
DataList.append(float(pcsData[0]))
while(1):
getValues()
drawnow(makeFig)
plt.pause(.000001)
The result snapshot:

the drawnow called in this way is designed as one-shot draw. See if drawnow(caller, show_once=True) corrects your problem, else you may need to look elsewhere - such as using alternative plotting functions:
How do I plot in real-time in a while loop using matplotlib?

Related

Live plot through tkinter .after - Combining pyplot event loop with tkinter event loop [duplicate]

I am having problems trying to make matplotlib plot a function without blocking execution.
I have tried using show(block=False) as some people suggest, but all I get is a frozen window. If I simply call show(), the result is plotted properly but execution is blocked until the window is closed. From other threads I've read, I suspect that whether show(block=False) works or not depends on the backend. Is this correct? My backend is Qt4Agg. Could you have a look at my code and tell me if you see something wrong? Here is my code.
from math import *
from matplotlib import pyplot as plt
print(plt.get_backend())
def main():
x = range(-50, 51, 1)
for pow in range(1,5): # plot x^1, x^2, ..., x^4
y = [Xi**pow for Xi in x]
print(y)
plt.plot(x, y)
plt.draw()
#plt.show() #this plots correctly, but blocks execution.
plt.show(block=False) #this creates an empty frozen window.
_ = raw_input("Press [enter] to continue.")
if __name__ == '__main__':
main()
PS. I forgot to say that I would like to update the existing window every time I plot something, instead of creating a new one.
I spent a long time looking for solutions, and found this answer.
It looks like, in order to get what you (and I) want, you need the combination of plt.ion(), plt.show() (not with block=False) and, most importantly, plt.pause(.001) (or whatever time you want). The pause is needed because the GUI events happen while the main code is sleeping, including drawing. It's possible that this is implemented by picking up time from a sleeping thread, so maybe IDEs mess with that—I don't know.
Here's an implementation that works for me on python 3.5:
import numpy as np
from matplotlib import pyplot as plt
def main():
plt.axis([-50,50,0,10000])
plt.ion()
plt.show()
x = np.arange(-50, 51)
for pow in range(1,5): # plot x^1, x^2, ..., x^4
y = [Xi**pow for Xi in x]
plt.plot(x, y)
plt.draw()
plt.pause(0.001)
input("Press [enter] to continue.")
if __name__ == '__main__':
main()
A simple trick that works for me is the following:
Use the block = False argument inside show: plt.show(block = False)
Use another plt.show() at the end of the .py script.
Example:
import matplotlib.pyplot as plt
plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")
plt.show(block=False)
#more code here (e.g. do calculations and use print to see them on the screen
plt.show()
Note: plt.show() is the last line of my script.
You can avoid blocking execution by writing the plot to an array, then displaying the array in a different thread. Here is an example of generating and displaying plots simultaneously using pf.screen from pyformulas 0.2.8:
import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time
fig = plt.figure()
canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')
start = time.time()
while True:
now = time.time() - start
x = np.linspace(now-2, now, 100)
y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
plt.xlim(now-2,now+1)
plt.ylim(-3,3)
plt.plot(x, y, c='black')
# If we haven't already shown or saved the plot, then we need to draw the figure first...
fig.canvas.draw()
image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))
screen.update(image)
#screen.close()
Result:
Disclaimer: I'm the maintainer for pyformulas.
Reference: Matplotlib: save plot to numpy array
Live Plotting
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1]) # disable autoscaling
for point in x:
plt.plot(point, np.sin(2 * point), '.', color='b')
plt.draw()
plt.pause(0.01)
# plt.clf() # clear the current figure
if the amount of data is too much you can lower the update rate with a simple counter
cnt += 1
if (cnt == 10): # update plot each 10 points
plt.draw()
plt.pause(0.01)
cnt = 0
Holding Plot after Program Exit
This was my actual problem that couldn't find satisfactory answer for, I wanted plotting that didn't close after the script was finished (like MATLAB),
If you think about it, after the script is finished, the program is terminated and there is no logical way to hold the plot this way, so there are two options
block the script from exiting (that's plt.show() and not what I want)
run the plot on a separate thread (too complicated)
this wasn't satisfactory for me so I found another solution outside of the box
SaveToFile and View in external viewer
For this the saving and viewing should be both fast and the viewer shouldn't lock the file and should update the content automatically
Selecting Format for Saving
vector based formats are both small and fast
SVG is good but coudn't find good viewer for it except the web browser which by default needs manual refresh
PDF can support vector formats and there are lightweight viewers which support live updating
Fast Lightweight Viewer with Live Update
For PDF there are several good options
On Windows I use SumatraPDF which is free, fast and light (only uses 1.8MB RAM for my case)
On Linux there are several options such as Evince (GNOME) and Ocular (KDE)
Sample Code & Results
Sample code for outputing plot to a file
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")
after first run, open the output file in one of the viewers mentioned above and enjoy.
Here is a screenshot of VSCode alongside SumatraPDF, also the process is fast enough to get semi-live update rate (I can get near 10Hz on my setup just use time.sleep() between intervals)
A lot of these answers are super inflated and from what I can find, the answer isn't all that difficult to understand.
You can use plt.ion() if you want, but I found using plt.draw() just as effective
For my specific project I'm plotting images, but you can use plot() or scatter() or whatever instead of figimage(), it doesn't matter.
plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)
Or
fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)
If you're using an actual figure.
I used #krs013, and #Default Picture's answers to figure this out
Hopefully this saves someone from having launch every single figure on a separate thread, or from having to read these novels just to figure this out
I figured out that the plt.pause(0.001) command is the only thing needed and nothing else.
plt.show() and plt.draw() are unnecessary and / or blocking in one way or the other. So here is a code that draws and updates a figure and keeps going. Essentially plt.pause(0.001) seems to be the closest equivalent to matlab's drawnow.
Unfortunately those plots will not be interactive (they freeze), except you insert an input() command, but then the code will stop.
The documentation of the plt.pause(interval) command states:
If there is an active figure, it will be updated and displayed before the pause......
This can be used for crude animation.
and this is pretty much exactly what we want. Try this code:
import numpy as np
from matplotlib import pyplot as plt
x = np.arange(0, 51) # x coordinates
for z in range(10, 50):
y = np.power(x, z/10) # y coordinates of plot for animation
plt.cla() # delete previous plot
plt.axis([-50, 50, 0, 10000]) # set axis limits, to avoid rescaling
plt.plot(x, y) # generate new plot
plt.pause(0.1) # pause 0.1 sec, to force a plot redraw
Iggy's answer was the easiest for me to follow, but I got the following error when doing a subsequent subplot command that was not there when I was just doing show:
MatplotlibDeprecationWarning: Adding an axes using the same arguments
as a previous axes currently reuses the earlier instance. In a future
version, a new instance will always be created and returned.
Meanwhile, this warning can be suppressed, and the future behavior
ensured, by passing a unique label to each axes instance.
In order to avoid this error, it helps to close (or clear) the plot after the user hits enter.
Here's the code that worked for me:
def plt_show():
'''Text-blocking version of plt.show()
Use this instead of plt.show()'''
plt.draw()
plt.pause(0.001)
input("Press enter to continue...")
plt.close()
The Python package drawnow allows to update a plot in real time in a non blocking way.
It also works with a webcam and OpenCV for example to plot measures for each frame.
See the original post.
Substitute the backend of matplotlib can solve my problem.
Write the bellow command before import matplotlib.pyplot as plt.
Substitute backend command should run first.
import matplotlib
matplotlib.use('TkAgg')
My answer come from Pycharm does not show plot

Jupyter Notebook: duplicated scatter plot using when using ipywidgets

I'm trying to control the display of a scatter plot with a checkbox. When I built it using the interact function it worked as expected. The plot was shown or hidden based on the value in the checkbox.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
def on_change(Display):
if Display == True:
plt.scatter(x,y)
plt.show()
return Display
interact(on_change, Display=False);
When I tried to do the same thing using the observe function every time I clicked on the checkbox I get an additional plot displayed below. What do I need to do to get it to redraw the same plot so it works like the example above?
I suppose something in the interact example is clearing the display but it's not clear how to do this manually.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
x = [1,2,3,4,5,6,7,8]
y = [5,2,4,2,1,4,5,2]
def on_change(change):
if change['new'] == True:
scat = plt.scatter(x,y)
plt.show()
cb = widgets.Checkbox(False, description = "Display")
cb.observe(on_change, names='value')
display(cb)
A couple of alterations I made to your example to hopefully demonstrate what you want. I have taken a more object-oriented route, not sure if you specifically wanted to avoid it but it helps achieve your desired outcome, it seems like you are moving towards a simple GUI here.
1) Include an Output widget (out) - basically a cell output which you can display like a normal widget. You can use a context manager block (with out:) when you want to print to that specific output widget. You can also clear the widget with out.clear_output()
2) Use the object oriented interface in matplotlib rather than using plt. I find this easier to control which plots are displayed and in which location at the right times.
temporarily suspend the interactive matplotlib with plt.ioff()
Create your figure and axis with fig, ax = plt.subplots(). NB figures can have multiple axes/subplots but we only need one.
'plot' the scatter data to your axis using ax.scatter(x,y), but this won't cause it to appear.
Explicitly display the figure with display(fig).
I'm assuming you want your figure to be replotted each time you check the box, so I have included it in the observe function. If your figure doesn't change, it would make sense to move it outside of the loop.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
out = widgets.Output()
x = [1,2,3,4,5,6,7,8]
y = [5,2,4,2,1,4,5,2]
def on_change(change):
if change['new'] == True:
with out:
plt.ioff()
fig,ax = plt.subplots()
ax.scatter(x,y)
display(fig)
else:
out.clear_output()
cb = widgets.Checkbox(False, description = "Display")
cb.observe(on_change, names='value')
display(cb)
display(out)

Matplotlib is slow when plotting many graphs on a single plot

As the title says when I try to graph a bunch of graphs, it takes a very long time. For example if I try to plot a silly example like this 10000 times:
n=10000
numbers = []
for i in range(n):
numbers.append(i)
for i in range(n):
plt.plot(numbers)
plt.show()
It will take about a minute to show the plot.
I know doing this will make it faster
import matplotlib
matplotlib.use('GTKAgg')
But is there any other way to make plotting a bunch of graphs faster? Any suggestions would be much appreciated, thanks!
You could do a dynamic plot with plt.ion()
example:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
su = fig.add_subplot(111)
su.axis([-1, 1, -1, 1])
plt.ion() # allows for dynamic plot
for i in range(1000):
y = np.random.random()
su.scatter(y**2, y)
plt.pause(0.4)
while True:
plt.pause(0.05)
This allows you to see points as they come up on your graph.
Is this what you want?
EDIT:
Maybe you could try using matplotlib.pyplot's savefig functionality
https://matplotlib.org/devdocs/api/_as_gen/matplotlib.pyplot.savefig.html
You could have your computer save all of your figures to separate png files. Then you can look at the pictures any time. This method requires minimal time on your part, just let the program run in the background for a bit while it makes the pngs, and you can view them without having to regenerate them at any time.

python (pandas?) live plot

I have been trying to get an updated plot from a pandas dataframe without success. My problem: the plot window does not appear (it is not hidden - I am sure about that).
I already tried to rebuild and change different solutions from stackoverflow. My most recent try is based on this post. Pure copy,paste does work, so the problem needs to be in my modifications.
I changed it to this as I want to update it automatically every second.
import serial as s
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from time import sleep
data = pd.DataFrame(np.random.random((10,10))) # I want to use pandas because
# of easier timestamp handling
fig, ax = plt.subplots()
ax.set(title='title')
im = ax.imshow(data)
while True:
im.set_data(np.random.random((10,10)))
print "hello" #just to see if sth happens
fig.canvas.draw()
sleep(1)
plt.show()
Just to explain: Later I want to read data from serial ports and feed them to the plot to get my data visualized.
Well, what you expect: the provided code does print hello each second but does not show me any plot. Any ideas? I am out of them.
By the way: I am surprised that there is no "easy, straight forward" solution for this kind of problem to be found. I can imagine, there is some people who are trying to do updated plots?!
you can use the package drawnow
from pylab import * # import matplotlib before drawnow
from drawnow import drawnow, figure
from time import sleep
import numpy as np
def draw_fig_real():
imshow(data, interpolation='nearest')
data = np.random.random((10,10))
figure()
for i in range(10):
data = np.random.random((10,10))
sleep(0.5)
drawnow(draw_fig_real)
Hope this helps

Matplotlib in Python - Drawing shapes and animating them

So I'm representing a token ring network (doing the simulation in SimPy), I'm a totally newbie to matplotlib, but I was told that it'd be really good for representing my simulation visually.
So I googled around and found out how to draw shapes and lines - using add_patch and add_line respectively to the axes (I believe). So now I have this output which is absolutely fine:
(can't post images yet!!)
http://img137.imageshack.us/img137/7822/screenshot20100121at120.png
But I'm getting this using the pylab.show() function, and what I think I want is to achieve this using the pylab.plot() function so that I can then update it as my simulation progresses using pylab.draw() afterward.
My code is as follows:
plab.ion()
plab.axes()
for circ in self.circleList:
plab.gca().add_patch(circ)
for line in self.lineList:
plab.gca().add_line(line)
plab.axis('scaled')
plab.show()
Where circleList and lineList are lists containing the circles and lines on the diagram
I'm probably misunderstanding something simple here, but I can't actually find any examples that aren't overtly graph based that use the plot() function.
Clarification:
How can I get that same output, using pylab.plot() instead of pylab.show() ?
Replicating your image using the plot method:
from pylab import *
points = []
points.append((-0.25, -1.0))
points.append((0.7, -0.7))
points.append((1,0))
points.append((0.7,1))
points.append((-0.25,1.2))
points.append((-1,0.5))
points.append((-1,-0.5))
points.append((-0.25, -1.0))
a_line = plot(*zip(*points))[0]
a_line.set_color('g')
a_line.set_marker('o')
a_line.set_markerfacecolor('b')
a_line.set_markersize(30)
axis([-1.5,1.5,-1.5,1.5])
show()
EDIT BASED ON COMMENTS
This uses python multiprocessing library to run the matplotlib animation in a separate process. The main process uses a queue to pass data to it which then updates the plot image.
# general imports
import random, time
from multiprocessing import Process, Queue
# for matplotlib
import random
import numpy as np
import matplotlib
matplotlib.use('GTKAgg') # do this before importing pylab
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
def matplotLibAnimate(q,points):
# set up initial plot
fig = plt.figure()
ax = fig.add_subplot(111)
circles = []
for point in points:
ax.add_patch(Circle(point,0.1))
a_line, = ax.plot(*zip(*points))
a_line.set_color('g')
a_line.set_lw(2)
currentNode = None
def animate(currentNode = currentNode):
while 1:
newNode = q.get()
if currentNode: currentNode.remove()
circle = Circle(newNode,0.1)
currentNode = ax.add_patch(circle)
circle.set_fc('r')
fig.canvas.draw()
# start the animation
import gobject
gobject.idle_add(animate)
plt.show()
#initial points
points = ((-0.25, -1.0),(0.7, -0.7),(1,0),(0.7,1),(-0.25,1.2),(-1,0.5),(-1,-0.5),(-0.25, -1.0))
q = Queue()
p = Process(target = matplotLibAnimate, args=(q,points,))
p.start()
# feed animation data
while 1:
time.sleep(random.randrange(4))
q.put(random.sample(points,1)[0])
Of course, after doing this I think you'll be better served with whatnick's image solution. I'd create my own GUI and not use matplotlibs built in widget. I'd then "animate" my GUI by generating PNGs and swapping them.
It sounds like Mark has the answer you were looking for, but if you decide to go with whatnick's approach and build an animation from individual pngs, here is the code to implement Amit's suggestion to use mencoder (from http://en.wikibooks.org/wiki/Mplayer):
mencoder mf://*.png -mf w=400:h=400 -ovc lavc -lavcopts vcodec=xvid -of avi -o output.avi
The core technique is to update the data of the elements being rendered using set_data. Then call draw(). See if your circle and line elements have set_data functions. Otherwise you can use pyvtk. The other option is to render and save the plots to png files and later build an animation from those.

Categories