def scatter(self, *args, **kwargs): """ attr1/2 default to canvas.scatter """ multi= kwargs.pop('multi', False) colors = kwargs.pop('colors', self._request_plotcolors()) annotate = kwargs.pop('annotate', False) axes, kwargs = putil._parse_ax(*args, **kwargs) # Multiplot style if multi: raise NotImplementedError # Overlaid plot style else: if not axes: fig, axes = plt.subplots() for idx, c in enumerate(self.canvii): # Only pass annotate once to avoid re-write axis labels/title if idx == 0 and annotate: addlabel = True else: addlabel = False c.scatter(axes, color=colors[idx], annotate=addlabel, **kwargs) if annotate: axes.legend(self.names) return axes
def _show(self, *args, **kwargs): """ 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: Parameters ---------- names: bool (False) Show multicanvas names at top of plot colors: bool (True): Map stored color to each particle in subplot. **kwargs: Any valid splot arg (ncols, figsize etc...) or show/patchshow args, such as cmap, grid etc... If passing a pre-constructed axes/subplots to mc.show(), it must be as a keyword. As a positional, it will not work! """ names = kwargs.pop('names', False) colors = kwargs.pop('colors', True) showstyle = kwargs.pop('showstyle', 'show') if showstyle not in ['show', 'patchshow']: raise MultiError("showstyle must be show or patchshow, " "not %s" % showstyle) axes, kwargs = putil._parse_ax(*args, **kwargs) if not axes: axes, kwargs = putil.multi_axes(len(self), **kwargs) if len(axes) < len(self): logger.warn("MultiCanvas has %s canvas, but only %s axes recieved" " in show()" % (len(self), len(axes))) upperlim = len(axes) else: upperlim = len(self) pcolors = self._request_plotcolors() for idx in range(upperlim): c = self.canvii[idx] if colors: def cmap(p): p.color = pcolors[idx] return p c = c.pmap(cmap) if names: kwargs['title'] = self.names[idx] getattr(c, showstyle)(axes[idx], **kwargs) return axes
def scatter(self, *args, **kwargs): """ Scatter plot of two particles attributes (eg area vs ccircularity). Parameters ---------- attr1: str X-attribute attr2: str Y-attribute annotate: False Adds title, x and y labels """ # Would it make more sense to default these to something attr1 = kwargs.pop('attr1', None) attr2 = kwargs.pop('attr2', None) if not attr1 or not attr2: raise CanvasPlotError('Scatter attributes must be specified as' ' keywords (IE c.scatter(attr1=area, attr2=eccentricity ...)') annotate = kwargs.pop('annotate', False) title = kwargs.pop('title', None) fancy = kwargs.pop('fancy', False) axes, kwargs = _parse_ax(*args, **kwargs) if fancy: raise NotImplementedError("Fancy kwarg not yet supported.") if not axes: fig, axes = plt.subplots() x, y = getattr(self, attr1), getattr(self, attr2) axes.scatter(x, y, **kwargs) if annotate: if not title: title = '%s - %s' % (attr1.title(), attr2.title()) axes.set_xlabel(attr1) axes.set_ylabel(attr2) if title: axes.set_title(title) return axes
def show(self, *args, **kwargs): """ Wrapper to imshow. Converts to gray to allow color maps. Notes ----- Differs from patchshow in that the collective image (bg, grid, particles) is a series of masks, so they have to be drawn onto a single ndarray and then plotted. Sicne patchshow is writing patchs, it can plot the background separate from the grid and particles, which is slightly easier""" # This will pull out "ax", leaving remaing args/kwargs axes, kwargs = _parse_ax(*args, **kwargs) title = kwargs.pop('title', None) save = kwargs.pop('save', None) bgonly = kwargs.pop('bgonly', False) annotate = kwargs.pop('annotate', False) grid = kwargs.pop('grid', False) gcolor = kwargs.pop('gcolor', None) gunder = kwargs.pop('gunder', False) gstyle = kwargs.pop('gstyle', None) #NOT USED nolabel = kwargs.pop('nolabel', False) zoom = kwargs.pop('zoom', None) if gstyle: raise CanvasPlotError('"gstyle" only valid for patchshow()') if 'pmap' in kwargs: raise CanvasPlotError('"pmap" is only valid for patchshow() method') PBINARY = False if 'cmap' in kwargs: if kwargs['cmap'] == 'pbinary' or kwargs['cmap'] == 'pbinary_r': PBINARY = kwargs['cmap'] del kwargs['cmap'] # Get the background if bgonly: if 'cmap' not in kwargs: raise CanvasPlotError('"bgonly" is only valid when a colormap is' ' passed.') bg = kwargs['cmap'](self.graybackground)[... , :3] del kwargs['cmap'] else: bg = self.background if PBINARY: if PBINARY == 'pbinary_r': bg = np.ones(bg.shape).astype(bool) else: bg = np.zeros(bg.shape).astype(bool) # grid overlay if gcolor or gunder and not grid: grid = True # If user enters gcolor, assume default grid if grid and not gcolor: gcolor = GCOLOR # Map attributes from grid (centers, corners, grid) gattr = np.zeros(bg.shape).astype(bool) #IE pass if grid: if not gcolor: gcolor = GCOLOR if grid == True: grid = 'grid' # Validate grid keyword try: gattr=getattr( self.grid, grid.lower() ) except Exception: raise CanvasPlotError('Invalid grid argument, "%s". Choose from: ' 'True, "grid", "centers", "corners", "hlines", "vlines"' % grid) gcolor = to_normrgb(gcolor) #Draw grid over or under? if gunder: bg[gattr] = gcolor image = self._draw_particles(bg, force_binary=PBINARY) else: image = self._draw_particles(bg, force_binary=PBINARY) image[gattr] = gcolor # GRAY CONVERT if 'cmap' in kwargs: image = rgb2uint(image) # Matplotlib if axes: axes.imshow(image, **kwargs) else: axes = plt.imshow(image, **kwargs).axes axes = self._annotate_plot(axes, annotate, title) # SHOW DOESNT ACTUALLY CROP ANYTHING WHEN ZOOMING if zoom: xi, yi, xf, yf = zoom axes.set_xlim(xi, xf) axes.set_ylim(yf, yi) if nolabel: axes.xaxis.set_visible(False) axes.yaxis.set_visible(False) if nolabel == 'x': axes.yaxis.set_visible(True) elif nolabel == 'y': axes.xaxis.set_visible(True) if save: path = _parse_path(save) skimage.io.imsave(path, image) return axes
def patchshow(self, *args, **kwargs): """ ... args/kwargs include alpha, edgecolors, linestyles Notes: Matplotlib API is setup that args or kwargs can be entered. Order is important for args, but the correspond to same kwargs. """ axes, kwargs = _parse_ax(*args, **kwargs) title = kwargs.pop('title', None) bgonly = kwargs.pop('bgonly', False) annotate = kwargs.pop('annotate', False) zoom = kwargs.pop('zoom', None) grid = kwargs.pop('grid', False) gcolor = kwargs.pop('gcolor', None) gstyle = kwargs.pop('gstyle', None) gunder = kwargs.pop('gunder',False) pmap = kwargs.pop('pmap', None) nolabel = kwargs.pop('nolabel', None) alpha = kwargs.get('alpha', None) edgecolor = kwargs.get('edgecolor', None) linewidth = kwargs.get('linewidth', None) linestyle = kwargs.get('linestyle', None) # Some keywords to savefig; not all supported save = kwargs.pop('save', None) dpi = kwargs.pop('dpi', None) bbox_inches = kwargs.pop('bbox_inches', None) # GET NOT POP cmap = kwargs.get('cmap', None) # Implement later if cmap in ['pbinary', 'pbinary_r']: raise CanvasPlotError('"pbinary(_r)" color map only valid for .show()') # grid defaults if gcolor or gunder or gstyle and not grid: grid = True # If user enters gcolor/gstyle, assume default grid if grid and not gcolor: gcolor = GCOLOR if grid and not gstyle: gstyle = 'solid' # Corner case, don't touch if pmap and cmap and bgonly: bgonly = False if bgonly and not cmap: raise CanvasPlotError('"bgonly" is only valid when a colormap is' ' passed.') if cmap: bg = self.graybackground else: bg = self.background if zoom: xi, yi, xf, yf = zoom bg = crop(bg, zoom) #Overwrite axes image if not axes: fig, axes = plt.subplots() else: axes.images=[] # DONT PASS ALL KWARGS axes.imshow(bg, cmap=cmap) # FOR PATICLES IN IMAGE ONLY. in_and_edges = self.pin + self.pedge # PARTICLE FACECOLOR, ALPHA and other PATCH ARGS # http://matplotlib.org/api/artist_api.html#matplotlib.patches.Patch patches = [p.particle.as_patch(facecolor=p.color, alpha=alpha, edgecolor=edgecolor, linestyle=linestyle, linewidth=linewidth) for p in in_and_edges] # If no particles or grid, just pass to avoid deep mpl exceptiosn if patches or grid: if patches: if pmap: kwargs['cmap'] = pmap if 'cmap' in kwargs and not bgonly: ppatch = PatchCollection(patches, **kwargs) #cmap and Patch Args ppatch.set_array(np.arange(len(patches))) # Use settings passed to "patches" else: ppatch = PatchCollection(patches, match_original=True, **kwargs) # # Grid under particles if gunder: axes.add_collection(self.grid.as_patch( edgecolors=gcolor, linestyles=gstyle)) if patches: axes.add_collection(ppatch) # Grid over particles else: if patches: axes.add_collection(ppatch) if grid: axes.add_collection(self.grid.as_patch( edgecolors=gcolor, linestyles=gstyle)) axes = self._annotate_plot(axes, annotate, title) if zoom: axes.set_xlim(xi, xf) axes.set_ylim(yf, yi) if nolabel: axes.xaxis.set_visible(False) axes.yaxis.set_visible(False) if nolabel == 'x': axes.yaxis.set_visible(True) elif nolabel == 'y': axes.xaxis.set_visible(True) if save: path = _parse_path(save) plt.savefig(path, dpi=dpi, bbox_inches=bbox_inches) return axes
def hist(self, *histargs, **histkwargs): """ Matplotlib histogram wrapper. Parameters ---------- **annotate:** bool - True Add general legend, title, axis labels. **attr:** str - "area" Particle attribute for data. (Also a pie chart keyword). **xlim:** range(start, stop) - None Shortcut to set x-limits. If **xlim=auto**, absolute min and absolute max of data will be used. This crops data AND sets axis limits; to only change plot axes, use *axes.set_xlim()*. Notes ----- 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. """ annotate = histkwargs.pop('annotate', True) attr = histkwargs.pop('attr', 'area') histkwargs.setdefault('stacked', True) histkwargs.setdefault('label', self.names) histkwargs.setdefault('bins', 10) metavar = histkwargs.pop('metavar', None) xlim = histkwargs.pop('xlim', None) #MPL api asymmetry with pie if 'colors' in histkwargs: histkwargs['color'] = histkwargs.pop('colors') # If not colors, set colors based on _colors if 'color' not in histkwargs: histkwargs['color'] = self._request_plotcolors() axes, histkwargs = putil._parse_ax(*histargs, **histkwargs) if not axes: fig, axes = plt.subplots() # Get list of arrays for descriptor, slice if xlim attr_list = [getattr(c, attr) for c in self.canvii] if xlim: if xlim == 'auto': xi, xf = min(map(min, attr_list)), max(map(max, attr_list)) else: xi, xf = xlim attr_list = [array[(array >= xi) & (array <= xf)] for array in attr_list] axes.set_xlim(xi, xf) # Validate attr_list for empy arrays; avoid ambiguous mpl error for idx, array in enumerate(attr_list): if len(array) == 0: raise MultiError('Empty array returned for "%s" attribute' ' on "%s" Canvas.' % (attr, self.names[idx]) ) axes.hist(attr_list, **histkwargs) if annotate: axes.set_xlabel(attr.title()) #Capitalize first letter axes.set_ylabel('Counts') if metavar: attr = metavar axes.set_title('%s Distribution (%s bins)' % (attr.title(), histkwargs['bins']) ) axes.legend() return axes
def pie(self, *chartargs, **chartkwargs): """ Pie chart wrapper to matplotlib. Parameters ---------- attr : Str or None Sum of variable to show in the pie. Generally particle descriptor (eg area). If None or "count", the particle counts by species are used. annotate : Bool (True) Add auto title to piechart. autopct : str or fcn or None Label of pie slices. Some built in short cuts include "count", "percentage", "both". Note results in no labels. See matplotlib.pie for more. usetex : bool (False) Label of pie slcies use latex rendering. If matplotlib.rcparams usetex = True, then set this to True. """ attr = chartkwargs.pop('attr', None) annotate = chartkwargs.pop('annotate', True) usetex = chartkwargs.pop('usetex', False) chartkwargs.setdefault('shadow', False) metavar = chartkwargs.pop('metavar', None) # If not colors, set colors based on _colors if 'colors' not in chartkwargs: chartkwargs['colors'] = self._request_plotcolors() if annotate: autopct = chartkwargs.get('autopct', 'percent') chartkwargs.setdefault('labels', self.names) else: autopct = chartkwargs.get('autopct', None) axes, chartkwargs = putil._parse_ax(*chartargs, **chartkwargs) if not axes: fig, axes = plt.subplots() if attr is None or attr == 'count': attr_list = [len(c) for c in self.canvii] attr = 'number' # for title else: attr_list = [sum(getattr(c, attr)) for c in self.canvii] # Percentage or true values if autopct == 'percent': if usetex: chartkwargs['autopct'] = r'\bf{%.1f\%%}' #Label size and position else: chartkwargs['autopct'] = '%.1f%%' #Label size and position elif autopct == 'count': if usetex: chartkwargs['autopct'] = \ lambda(p): r'\bf{:.0f}'.format(p * sum(attr_list) / 100) else: chartkwargs['autopct'] = \ lambda(p): '{:.0f}'.format(p * sum(attr_list) / 100) elif autopct == 'both': def double_autopct(pct): total = sum(attr_list) val = int(round(pct*total/100.0, 0)) if usetex: texstring = r'{v} ({p:.1f}\%)'.format(p=pct,v=val) return r'\bf{%s}' % texstring else: return '{v}\n({p:.1f}%)'.format(p=pct,v=val) chartkwargs['autopct'] = double_autopct axes.pie(attr_list, **chartkwargs) if annotate: if metavar: attr = metavar axes.set_title('%s Distribution' % attr.title()) return axes