This notebook will demonstrate some of the basic features of MultiCanvas, the canonical container class for Canvas. A good real-world utilization is in the notebook, labeling nanogold.
Configure notebook style (see NBCONFIG.ipynb), add imports and paths. The %run magic used below requires IPython 2.0 or higher.
%run NBCONFIG.ipynb
Populating the interactive namespace from numpy and matplotlib
Let's use Canvas.random_circles() to generate 10 canvases with a random number of circles between 25 and 75 and randomly ordered names.
MultiCanvas must be initialized with a names and canvii instance; both must be equally-sized lists. We will show alternative constructors subsequently.
import random
from pyparty import MultiCanvas
nameslist = [ 'test-%s' % i for i in range(10) ]
random.shuffle(nameslist)
names, canvii = [], []
for i in range(10):
canvii.append( Canvas.random_circles(n=random.randint(25,75) ) )
mc = MultiCanvas(names=nameslist, canvii=canvii)
mc
MultiCanvas (0x74b2470) 548: test-3 - Canvas (0x4c9f290) : 512 X 512 : 45 particles test-6 - Canvas (0x4ece230) : 512 X 512 : 71 particles test-7 - Canvas (0x50c5470) : 512 X 512 : 54 particles test-1 - Canvas (0x5248410) : 512 X 512 : 53 particles test-5 - Canvas (0x53ab3b0) : 512 X 512 : 41 particles test-0 - Canvas (0x5bc9d70) : 512 X 512 : 70 particles test-8 - Canvas (0x5dd0290) : 512 X 512 : 67 particles test-4 - Canvas (0x5f00cb0) : 512 X 512 : 27 particles test-9 - Canvas (0x733dc50) : 512 X 512 : 65 particles test-2 - Canvas (0x74b2170) : 512 X 512 : 55 particles
Let's sort this (by name). When sorting multicanvas, do not use pythons sorted() function; used mc.sort()
mc.sort(inplace=True)
mc
MultiCanvas (0x74b2470) 548: test-0 - Canvas (0x5bc9d70) : 512 X 512 : 70 particles test-1 - Canvas (0x5248410) : 512 X 512 : 53 particles test-2 - Canvas (0x74b2170) : 512 X 512 : 55 particles test-3 - Canvas (0x4c9f290) : 512 X 512 : 45 particles test-4 - Canvas (0x5f00cb0) : 512 X 512 : 27 particles test-5 - Canvas (0x53ab3b0) : 512 X 512 : 41 particles test-6 - Canvas (0x4ece230) : 512 X 512 : 71 particles test-7 - Canvas (0x50c5470) : 512 X 512 : 54 particles test-8 - Canvas (0x5dd0290) : 512 X 512 : 67 particles test-9 - Canvas (0x733dc50) : 512 X 512 : 65 particles
Indexing is as you'd expect. Valid indicies include:
A slice that returns multiple objects will return a MulitCanvas. A slice that returns a single element will return a Canvas. This is the same behavior as Python's list slicing.
print mc[4:7]
NAME = mc.names[0]
mc[NAME].show(title='Showing Canvas: %s' % NAME);
MultiCanvas (0x74b27d0) 139: test-4 - Canvas (0x5f00cb0) : 512 X 512 : 27 particles test-5 - Canvas (0x53ab3b0) : 512 X 512 : 41 particles test-6 - Canvas (0x4ece230) : 512 X 512 : 71 particles
Setting a deleting by item are also supported, but only by single key; the same as a python dictionary:
mc['test'] = Canvas.random_circles(n=20)
print '"test" found:', 'test' in mc
"test" found: True
del mc['test']
print '"test" found:', 'test' in mc
"test" found: False
Some other ported container method include:
A major motivator for designing the multi-canvas was in to create multiplots, stacked histograms and pie charts. A non-trivial utilization of these is shown in the labeling nanogold example.
One important point about these plots:** please pass optional arguments as keywords and not as a list of args*. To allow for passing ax* as a positional argument, pyparty doesn't attempt to parse the other positional arguments. This may be fixed later, but for now, please only use keyword args (for example bins=50).
Let me take only the first 4 samples from the multi canvas so that the plots do not get too cluttered.
mc_cut = mc[0:4]
mc_cut.show(names=True)
mc_cut.patchshow(colors=False, figsize=(8,6), gcolor='red');
show() and patchshow() wrap their respective Canvas methods, so any valid arguments (like colormaps, grids etc...) should just work. In addition, multicanvas show methods have the following additional keyword arguments:
names: bool (False)
colors: bool (True):
pyparty.splot arguments like column number and figsize (ncols, figsize) are valid. If a user does not specify the number of columns, they will default to 4 and empty plots will be cutoff.
** If passing a pre-constructed axes/subplots to mc.show(), it must be as a keyword. As a positional, it will not work! (see below)**
axes = splot(2,3)
mc_cut.show(axes=axes, colors=False, cmap='gray');
Histogram
Besides any valid matplotlib histogram keywords, the following additional options have been implemented:
annotate: (True)
attr: (area)
xlim: (None)
We see that the annotate option adds a legend, title and axis labels. The default attribute of the histogram is area, corresponding to the attr kwargs. All other valid matplotlib histogram kwargs should work.
ax1, ax2 = splot(1,2)
# Scientific notation on x-axis
ax1.ticklabel_format(axis='x', style='sci', scilimits=(0,1))
ax2.ticklabel_format(axis='x', style='sci', scilimits=(0,1))
mc_cut.hist(ax1)
mc_cut.hist(ax2, annotate=False);
Pie Chart
ax1, ax2 = splot(1,2)
mc_cut.pie(ax1)
mc_cut.pie(ax2, annotate=False);
The default pie chart is an annotated (title, labels etc...) breakdown by particle count per canvas. Instead of the distribution by particle count, we can look at area, eccentricity or any descriptor that can be summed via the attr keyword. The two new options for pie chart are:
autopct: str or fcn or None
usetex : bool (True)
metavar: str or None
Usetex should be set to whatever setting plt.rcParams is using. By default, usetex is False. If the rendering of the labels looks bizarre on the piechart, change this option. Autopct adds a few basic shortcuts for different styles of pie labels. In addition, any matplotlib piechart kwarg should work. For latex to properly render, ensure you have ghostscript and dvipng installed (apt-get compatible). Also, be careful when using axis labels of variables with underscores!
In the next example, we will show how to properly render latex tex.
from matplotlib import rc
rc('text', usetex=True)
ax1, ax2 = splot(1,2)
piekwargs = {'attr':'equivalent_diameter', 'autopct':'count',
'usetex':True, 'shadow':True, 'explode':(0,0,0,0.1)}
mc_cut.pie(ax1, **piekwargs)
mc_cut.pie(ax2, metavar='Equivalent\_Diameter', **piekwargs);
In the above example, notice that the underscore in the title is not a problem. This would be a problem if we had used usetex=True, since latex doesn't render underscores. (Easiest fix is just to do ax.set_title('new title'). You can also tell that the font and text weight are different when compared to the prior pie charts, in which usetex is True (set in NBCONFIG). The difference are more pronounced when rendering math symbols. Let me now turn it off:
rc('text', usetex=False)
Sometimes we'd like to output the MultiCanvas as a standard Python type, such as a dictionary with names as keys and canvii as values. Or maybe we don't want the whole canvas, we just need the particle area array or the binary image, or any other valid canvas attribute. mc.transmute() provides a general and simple method for this. One specifies merely two options:
So, let's output a dictionary of names, areas, and a list of tuple pairs of names, grayimage.
area_dict = mc.transmute(attr='area', as_type=dict)
tuple_imgs = mc.transmute(attr='grayimage', as_type=tuple)
print type(area_dict), type(tuple_imgs)
name, grayimg = tuple_imgs[0]
showim(grayimg, title='Showing ``%s" from tuple transmute' % name);
<type 'dict'> <type 'tuple'>
Currently, MultiCanvas implements two important constructors:
MultiCanvas.from_labeled(img, names, pmanwkargs)
MultiCanvas.from_canvas(canvas, names)
from_labeled(img, names, kwargs) is shown in the related labeling nanogold example in more detail. Here is a quick example in which we map the particles between red and blue based on the mean area, then read this into a MultiCanvas::
def red_or_blue(p, cutoff=2500):
""" Color blue/red based on cutoff area size"""
p.color = 'r'
if p.area > cutoff:
p.color = 'blue'
return p
c= mc[0]
c.pmap(red_or_blue, np.mean(c.area), inplace=True)
c.show();
mc_colored = MultiCanvas.from_labeled(c.image, 'p-blue', 'p-red', ignore='white')
mc_colored
WARNING:pyparty.utils:3-Channel converted to 1-channel (gray). WARNING:pyparty.multi:Ignore set to None but was not found on image. WARNING:pyparty.multi:length : 2 names provided but 3 unique labels were found WARNING:pyparty.tools.manager:pmin < 10 may result in errors in some particle descriptors WARNING:pyparty.tools.manager:pmin < 10 may result in errors in some particle descriptors
MultiCanvas (0x9dfe530) 37: p-blue - Canvas (0x9e07650) : 512 X 512 : 10 particles p-red - Canvas (0x9dfe830) : 512 X 512 : 27 particles
ax1, ax2 = splot(1,2)
kwargs={'attr':'area', 'colors':['blue', 'red']}
mc_colored.pie(ax1, **kwargs)
mc_colored.hist(ax2, bins=5, xlim='auto', **kwargs);
c1 = Canvas.random_circles(n=10, pcolor='yellow')
c2 = Canvas.random_triangles(n=10, pcolor='red')
c3 = c1+ c2
c3.patchshow();
print MultiCanvas.from_canvas(c3)
MultiCanvas (0x8f7fd10) 20: circle - Canvas (0xcdf6a10) : 512 X 512 : 10 particles triangle - Canvas (0xd6656b0) : 512 X 512 : 10 particles
We can use the names parameter to reassign the names to the multicanvas. It's fairly flexible in that one can enter fewer or more than the number of names already on the multicanvas; it will let you know with a warning.
print MultiCanvas.from_canvas(c3, 'yellow-circle'), '\n'
print MultiCanvas.from_canvas(c3, 'yellow-circle', 'red-triangle', 'unusedname')
WARNING:pyparty.multi:length : 1 names provided but 2 unique labels were found WARNING:pyparty.multi:length : 3 names provided but 2 unique labels were found
MultiCanvas (0x9f8bb90) 20: yellow-circle - Canvas (0x9dff230) : 512 X 512 : 10 particles triangle - Canvas (0x9ecafb0) : 512 X 512 : 10 particles MultiCanvas (0x9f8b650) 20: yellow-circle - Canvas (0x9f8bd10) : 512 X 512 : 10 particles red-triangle - Canvas (0x9eca830) : 512 X 512 : 10 particles
Colors can be set to a particular name in the multicanvas via mc.set_colors(colors, namecolor). These colors will persist through the various plots and are useful when collapsing a multicanvas (see below). These are stored in the mc.mycolors attribute.
Not all names in the multicanvas need a color; if a custom color is not found, the standard plotting color cycle is used. Calling set_colors() should be done as:
mc.set_colors('r','g'):
mc.set_colors(singles='red', dimers=(0,0,1):
mc.set_colors():
mc = MultiCanvas.from_canvas(c3)
mc.set_colors('gray', 'purple')
mc.set_names('cool-circle', 'swag-triangle')
print 'Names and Colors: %s' % mc.mycolors
ax1, ax2 = splot(1,2)
mc.hist(ax1, bins=10)
ax1.ticklabel_format(axis='x', style='sci', scilimits=(0,1))
mc.pie(ax2, attr='area');
Names and Colors: {'circle': 'gray', 'cool-circle': 'gray', 'triangle': 'purple', 'swag-triangle': 'purple'}
mc.to_canvas() changes MultiCanvas back into a Canvas. This is useful for operations that are easier to conduct in the multicanvas framework. For example, we showed how to change colors and names based on particle species.. By collapsing into a single canvas, we may preserve these changes with mapcolors and mapnames.
cout = mc.to_canvas(mapcolors=True, mapnames=True)
print cout, '\n'
print "Canvas ptypes:", cout.ptypes
cout.patchshow();
Canvas (0x90bc410): background --> (512 X 512) : default particles --> 20 particles : 2 types xygrid[225] --> (15p X 15p) : (34.1 X 34.1) [pix/tile] Canvas ptypes: ('cool-circle', 'swag-triangle')
Most collapsing operations are achievalbe from the Canvas directly. We could have just added the two Canvii and mapped the colors and new particle types to the resultant Canvas directly.
Quick Note: When merging canvii of different resolution, the resultant canvas will be of the largest resolution of the set.