set_array() in tripcolor bug? - python

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.

Related

Efficiently update several pyplots

I have a script that is supposed to update a grid of 25 images in each iteration of a for loop. The code is essentially this (I am leaving out extraneous stuff to keep it simple):
from matplotlib import pyplot as plt
plt.ion()
fig, ax = plt.subplots(5,5,figsize=(3,3))
for i in range(100):
updateImages()
for j in range(5):
for k in range(5):
ax[j,k].imshow(image[j,k])
plt.draw()
plt.pause(0.01)
The problem is that it runs very, VERY slowly [and no, it is not updateImages() that is slow]. I believe there is a more efficient way to do this, but so far I have been unable to find any examples. Suggestions would be most appreciated!
The reason why your program runs slow after some time might be that you are only adding data points to the plot when always calling imshow instead of replacing/updating them. Even though you always see only the points most recently plotted, Python has to handle a lot of data after a lot of loops which is why it becomes slow.
Instead, you could store the first output of imshow in a list and just update the data. First, create a list list_of_images with the same 'shape' like ax and store the images of the first iteration there:
list_of_images[j,k] = imshow(...)
Then in the loop over iterations, just update the data:
list_of_images[j,k].set_data(image[j,k])
This way, the amount of data Python has to handle doesn't grow with the number of iterations and your program should be stable.
EDIT (by OP): Based on this answer, I tried the following:
from matplotlib import pyplot as plt
plt.ion()
fig, ax = plt.subplots(5,5,figsize=(3,3))
image_grid = ax.copy() # Maybe there is a less awkward way to do this
for i in range(100):
updateImages()
for j in range(5):
for k in range(5):
image_grid[j,k].set_data(image[j,k])
plt.draw()
plt.pause(0.01)
It runs very smoothly (just as fast as with a single image). Thanks!

Matplotlib Patches not matching supplied arguments.

been struggling with python and the matplotlib module. I am trying to draw some circles that are not filled and outlined in black. I am putting the correct arguments in the artist circle but it seems to ignore it and put blue. any ideas? Also the figure shows up automatically without me stating draw or show. How can i block that and control when the graph pops up? Thanks in advance.
my code
def draw_lattice(self,chart):
patches = []
for x in range(1,4):
for y in range (1,4):
circle = Circle((x,y), .25,color='k',fill=False)
# chart.add_patch(circle)
patches.append(circle)
p = PatchCollection(patches)
chart.add_collection(p)
Thanks in advance.
* UPDATE *
if i add each circle individually to the axes it will be formatted properly. If i add the collection it does not work. I have many more shapes to add and i would like to go the collection route. Any reason why one way would work and another wouldn't? I read somewhere that you need to add the artist but i tried that and got an error.
This is a rather late answer but I just came across the same problem and here is how to solve it:
What you need to do is tell the PatchCollection to match the original patches. To do this simply add match_original=True, like so:
p = PatchCollection(my_patches, match_original=True)
You can set the color of the circles when you create the patch collection:
p = PatchCollection(patches,facecolors='w',edgecolor='k')
From the Collection documentation:
"If any of edgecolors, facecolors, linewidths, antialiaseds are None, they default to their matplotlib.rcParams patch setting, in sequence form."

Basemap and Matplotlib - Improving Speed

I'm creating a tool for geospatial visualization of economic data using Matplotlib and Basemap.
However, right now, the only way I thought of that gives me enough flexibility is to create a new basemap every time I want to change the data.
Here are the relevant parts of the code I'm using:
class WorldMapCanvas(FigureCanvas):
def __init__(self,data,country_data):
self.text_objects = {}
self.figure = Figure()
self.canvas = FigureCanvas(self.figure)
self.axes = self.figure.add_subplot(111)
self.data = data
self.country_data = country_data
#this draws the graph
super(WorldMapCanvas, self).__init__(Figure())
self.map = Basemap(projection='robin',lon_0=0,resolution='c', ax=self.axes)
self.country_info = self.map.readshapefile(
'shapefiles/world_country_admin_boundary_shapefile_with_fips_codes', 'world', drawbounds=True,linewidth=.3)
self.map.drawmapboundary(fill_color = '#85A6D9')
self.map.fillcontinents(color='white',lake_color='#85A6D9')
self.map.drawcoastlines(color='#6D5F47', linewidth=.3)
self.map.drawcountries(color='#6D5F47', linewidth=.3)
self.countrynames = []
for shapedict in self.map.world_info:
self.countrynames.append(shapedict['CNTRY_NAME'])
min_key = min(data, key=data.get)
max_key = max(data, key=data.get)
minv = data[min_key]
maxv = data[max_key]
for key in self.data.keys():
self.ColorCountry(key,self.GetCountryColor(data[key],minv,maxv))
self.canvas.draw()
How can I create these plots faster?
I couldn't think of a solution to avoid creating a map every time I run my code. I tried creating the canvas/figure outside of the class but it didn't make that much of a difference. The slowest call is the one that creates the Basemap and loads the shape data. Everything else runs quite fast.
Also, I tried saving the Basemap for future use but since I need new axes I couldn't get it to work. Maybe you can point me in the right direction on how to do this.
I'd like you to know that I'm using the canvas as a PySide QWidget and that I'm plotting different kinds of maps depending on the data, this is just one of them (another would be a map of Europe, for instance, or the US).
You can pickle and unpickle Basemap instances (there is an example of doing this in the basemap source) which might save you a fair chunk of time on the plot creation.
Additionally, it is probably worth seeing how long the shapefile reading is taking (you may want to pickle that too).
Finally, I would seriously consider investigating the option of updating country colours for data, rather than making a new figure each time.
HTH,

Passing pyplot points as arguments

So I initialised a pyplot figure
import ... ## import all relevent modules
f = plt.figure(figsize=(8,3),dpi(100)
a = plt.subplot(111)
a.set_xlim(left=0,right=25,auto=False)
a.set_ylim(bottom=0,top=250,auto=False)
a.plot([5,10,15],[80,150,210])
plt.show()
This works fine... What I want to be able to do is to write a function that can update the scatter plot dynamically... Something like:
def plot_point(x_coord,y_coord):
a.plot([x_coord],[y_coord])
a.draw() ## I thought this would work... :(
No error, but the point doesn't get plotted. How can I get around this? The reason I've done it using figures is so I can embed it in Tkinter.
Thanks for your help!
plot is perfectly fine to use for plotting individual points (it is even recommend over scatter, if you don't wanna add additional information through color or size of the dots). What is missing in the initial example is setting the right linestyle; obviously, a line consisting of a single point doesn't show up. Changing the line style to '+' or something similar fixes the problem:
def plot_point(x_coord,y_coord):
a.plot([x_coord],[y_coord], '+')

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