Matplotlib in Python - Drawing shapes and animating them - python

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.

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

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.

set_array() in tripcolor bug?

I am new to Python and matplotlib, and I recently referenced to THIS to update my tripcolor plot. With following data preparation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import math
r = np.zeros((100,100))
t = np.zeros((100,100))
for i in range(0,100):
for j in range(0,100):
r[i,j]=i
t[i,j]=2*math.pi*j/100
x=r*np.cos(t)
y=r*np.sin(t)
z=r*r
xf=x.flatten()
yf=y.flatten()
zf=z.flatten()
triang = tri.Triangulation(xf,yf)
If I use tripcolor as it is intended,
# Works well
p = plt.tripcolor(triang, zf)
correct figure appears. But, if I try to update after creating tripcolor,
# Not working well
p = plt.tripcolor(triang, xf)
p.set_array(zf)
then, wrong figure appears. Both xf and zf have identical dimensions.
What am I doing wrong? What is the cause of the problem, and how can I avoid it?
Many thanks in advance.
=========================================================
Update
Thank you all. I actually solved myself.
The key was that I need to assign color for each area, which is controlled by shading argument, and default value for tripcolor is 'flat', which is, color for each vertex. So, when I plot the first figure, I need to make sure shading is 'gouraud', which assigns color for each area.
So,
p = plt.tripcolor(triang, xf, shading='gouraud')
p.set_array(zf)
works as I intended.
The reason
p = plt.tripcolor(triang, xf)
p.set_array(zf)
is not working as (may be) expected, is the following. In your case plt.tripcolor() returns a PolyCollection. The PolyCollection's set_array() will essentially set the colors of that Collection. However, the underlying triangles will not be changed, such that you end up with the triangles from xf but the colors from zf.
Since the generation of the tripcolor PolyCollection is quite involved (as it calls Triangulation itself) and there probably is no helper function to set the data externally (at least I am not aware of any), the solution might be not to update the tripcolor at all and instead generate a new one.
Is there any reason for you to update? Couldn't you just directly create p = plt.tripcolor(triang, zf)?
In case there is a real reason to it, like in an animation or so, an option would be to delete the first tripcolor plot before setting up the next.
# create one plot
p = plt.tripcolor(triang, xf)
#delete this plot (you need both lines actually!!)
p.remove()
del p
#create second plot
p = plt.tripcolor(triang, zf)
This is not really efficient, though, and in case someone has a better idea, I'd like to hear about that one as well.

Animating a Quadmesh from pcolormesh with matplotlib

As a result of a full day of trial and error, I'm posting my findings as a help to anyone else who may come across this problem.
For the last couple days, I've been trying to simulate a real-time plot of some radar data from a netCDF file to work with a GUI I'm building for a school project. The first thing I tried was a simple redrawing of the data using the 'interactive mode' of matplotlib, as follows:
import matplotlib.pylab as plt
fig = plt.figure()
plt.ion() #Interactive mode on
for i in range(2,155): #Set to the number of rows in your quadmesh, start at 2 for overlap
plt.hold(True)
print i
#Please note: To use this example you must compute X, Y, and C previously.
#Here I take a slice of the data I'm plotting - if this were a real-time
#plot, you would insert the new data to be plotted here.
temp = plt.pcolormesh(X[i-2:i], Y[i-2:i], C[i-2:i])
plt.draw()
plt.pause(.001) #You must use plt.pause or the figure will freeze
plt.hold(False)
plt.ioff() #Interactive mode off
While this technically works, it also disables the zoom functions, as well as pan, and well, everything!
For a radar display plot, this was unacceptable. See my solution to this below.
So I started looking into the matplotlib animation API, hoping to find a solution. The animation did turn out to be exactly what I was looking for, although its use with a QuadMesh object in slices was not exactly documented. This is what I eventually came up with:
import matplotlib.pylab as plt
from matplotlib import animation
fig = plt.figure()
plt.hold(True)
#We need to prime the pump, so to speak and create a quadmesh for plt to work with
plt.pcolormesh(X[0:1], Y[0:1], C[0:1])
anim = animation.FuncAnimation(fig, animate, frames = range(2,155), blit = False)
plt.show()
plt.hold(False)
def animate( self, i):
plt.title('Ray: %.2f'%i)
#This is where new data is inserted into the plot.
plt.pcolormesh(X[i-2:i], Y[i-2:i], C[i-2:i])
Note that blit must be False! Otherwise it will yell at you about a QuadMesh object not being 'iterable'.
I don't have access to the radar yet, so I haven't been able to test this against live data streams, but for a static file, it has worked great thus far. While the data is being plotted, I can zoom and pan with the animation.
Good luck with your own animation/plotting ambitions!

Plot shows up and disappears fast in R

I am plotting some graphs using R. When I run the program the plot appears and then quickly disappears. How can I make the plot stay?`
I am running the following code found in Dynamic Time Warping in Python
import numpy as np
import rpy2.robjects.numpy2ri
from rpy2.robjects.packages import importr
# Set up our R namespaces
R = rpy2.robjects.r
DTW = importr('dtw')
# Generate our data
idx = np.linspace(0, 2*np.pi, 100)
template = np.cos(idx)
query = np.sin(idx) + np.array(R.runif(100))/10
# Calculate the alignment vector and corresponding distance
alignment = R.dtw(query, template, keep=True)4
plot(alignments)
dist = alignment.rx('distance')[0][0]
print(dist)
Basically the main file is in python, I have installed rpy2, i am remotely connecting into a unix machine. Now the plot shows up but immediately disappears. This only happens to R plots. When I run matplotlib plots they stay(not disappear). so I am wondering whether I have to put some line of code to make the plots "stay". For example like the matlab "holdon".
One solution would be to wait for the user to type "enter" before the program finishes:
raw_input("Please type enter...")
This is also useful with my Matplotlib plots (instead of using pyplot.show(): this closes all the plots automatically).
PS: I just saw that this was suggested in the link from the comments to the original question. I approve. :)

Categories