from __future__ import division, generators import math, sys, warnings from numerix import absolute, arange, array, asarray, ones, divide,\ transpose, log, log10, Float, Float32, ravel, zeros,\ Int16, Int32, Int, Float64, ceil, indices, \ shape, which, where, sqrt, asum, compress, maximum, minimum, \ typecode, concatenate, newaxis, reshape, resize, repeat, cross_correlate, nonzero import numerix.ma as ma import matplotlib.mlab import artist from artist import Artist, setp from axis import XAxis, YAxis from cbook import iterable, is_string_like, flatten, enumerate, \ allequal, dict_delall, popd, popall, silent_list, is_numlike, dedent from collections import RegularPolyCollection, PolyCollection, LineCollection, \ QuadMesh, StarPolygonCollection, BrokenBarHCollection from colors import colorConverter, Normalize, Colormap, \ LinearSegmentedColormap, ListedColormap, looks_like_color, is_color_like import cm from cm import ScalarMappable from contour import ContourSet import _image from ticker import AutoLocator, LogLocator, NullLocator from ticker import ScalarFormatter, LogFormatter, LogFormatterExponent, LogFormatterMathtext, NullFormatter from image import AxesImage from legend import Legend from lines import Line2D, lineStyles, lineMarkers import lines from matplotlib.mlab import meshgrid, detrend_none, detrend_linear, \ window_none, window_hanning, linspace, prctile from matplotlib.numerix.mlab import flipud, amin, amax from matplotlib import rcParams from patches import Patch, Rectangle, Circle, Polygon, Arrow, Wedge, Shadow, FancyArrow, bbox_artist import table from text import Text, TextWithDash, Annotation, _process_text_args from transforms import Bbox, Point, Value, Affine, NonseparableTransformation from transforms import FuncXY, Func, LOG10, IDENTITY, POLAR from transforms import get_bbox_transform, unit_bbox, one, origin, zero from transforms import blend_xy_sep_transform, Interval, identity_transform from transforms import PBox, identity_transform, nonsingular from font_manager import FontProperties from quiver import Quiver, QuiverKey import matplotlib if matplotlib._havedate: from dates import AutoDateFormatter, AutoDateLocator, DateLocator, DateFormatter def delete_masked_points(*args): """ Find all masked points in a set of arguments, and return the arguments with only the unmasked points remaining. The overall mask is calculated from any masks that are present. If a mask is found, any argument that does not have the same dimensions is left unchanged; therefore the argument list may include arguments that can take string or array values, for example. Array arguments must all have the same shape, and must be one-dimensional. Written as a helper for scatter, but may be more generally useful. """ masks = [ma.getmaskarray(x) for x in args if hasattr(x, 'mask')] if len(masks) == 0: return args mask = reduce(ma.mask_or, masks) margs = [] for x in args: if shape(x) == shape(mask): margs.append(ma.masked_array(x, mask=mask).compressed()) else: margs.append(x) return margs def _process_plot_format(fmt): """ Process a matlab(TM) style color/line style format string. Return a linestyle, color tuple as a result of the processing. Default values are ('-', 'b'). Example format strings include 'ko' : black circles '.b' : blue dots 'r--' : red dashed lines See Line2D.lineStyles and GraphicsContext.colors for all possible styles and color format string. """ colors = { 'b' : 1, 'g' : 1, 'r' : 1, 'c' : 1, 'm' : 1, 'y' : 1, 'k' : 1, 'w' : 1, } linestyle = None marker = None color = None # handle the multi char special cases and strip them from the # string if fmt.find('--')>=0: linestyle = '--' fmt = fmt.replace('--', '') if fmt.find('-.')>=0: linestyle = '-.' fmt = fmt.replace('-.', '') if fmt.find(' ')>=0: linestyle = 'None' fmt = fmt.replace(' ', '') chars = [c for c in fmt] for c in chars: if lineStyles.has_key(c): if linestyle is not None: raise ValueError, 'Illegal format string "%s"; two linestyle symbols' % fmt linestyle = c elif lineMarkers.has_key(c): if marker is not None: raise ValueError, 'Illegal format string "%s"; two marker symbols' % fmt marker = c elif colors.has_key(c): if color is not None: raise ValueError, 'Illegal format string "%s"; two color symbols' % fmt color = c else: err = 'Unrecognized character %c in format string' % c raise ValueError, err if linestyle is None and marker is None: linestyle = rcParams['lines.linestyle'] if linestyle is None: linestyle = 'None' if marker is None: marker = 'None' return linestyle, marker, color class _process_plot_var_args: """ Process variable length arguments to the plot command, so that plot commands like the following are supported plot(t, s) plot(t1, s1, t2, s2) plot(t1, s1, 'ko', t2, s2) plot(t1, s1, 'ko', t2, s2, 'r--', t3, e3) an arbitrary number of x, y, fmt are allowed """ def __init__(self, command='plot'): self.command = command self._clear_color_cycle() def _clear_color_cycle(self): self.colors = ['b','g','r','c','m','y','k'] # if the default line color is a color format string, move it up # in the que try: ind = self.colors.index(rcParams['lines.color']) except ValueError: self.firstColor = rcParams['lines.color'] else: self.colors[0], self.colors[ind] = self.colors[ind], self.colors[0] self.firstColor = self.colors[0] self.Ncolors = len(self.colors) self.count = 0 def _get_next_cycle_color(self): if self.count==0: color = self.firstColor else: color = self.colors[int(self.count % self.Ncolors)] self.count += 1 return color def __call__(self, *args, **kwargs): ret = self._grab_next_args(*args, **kwargs) return ret def set_lineprops(self, line, **kwargs): assert self.command == 'plot', 'set_lineprops only works with "plot"' for key, val in kwargs.items(): funcName = "set_%s"%key if not hasattr(line,funcName): raise TypeError, 'There is no line property "%s"'%key func = getattr(line,funcName) func(val) def set_patchprops(self, fill_poly, **kwargs): assert self.command == 'fill', 'set_patchprops only works with "fill"' for key, val in kwargs.items(): funcName = "set_%s"%key if not hasattr(fill_poly,funcName): raise TypeError, 'There is no patch property "%s"'%key func = getattr(fill_poly,funcName) func(val) def _xy_from_y(self, y): y = ma.asarray(y) if len(y.shape) == 1: y = y[:,newaxis] nr, nc = y.shape x = arange(nr) return x,y def _xy_from_xy(self, x, y): x = ma.asarray(x) y = ma.asarray(y) if len(x.shape) == 1: x = x[:,newaxis] if len(y.shape) == 1: y = y[:,newaxis] nrx, ncx = x.shape nry, ncy = y.shape assert nrx == nry, 'Dimensions of x and y are incompatible' if ncx == ncy: return x, y if ncx == 1: x = repeat(x, ncy, axis=1) if ncy == 1: y = repeat(y, ncx, axis=1) assert x.shape == y.shape, 'Dimensions of x and y are incompatible' return x, y def _plot_1_arg(self, y, **kwargs): assert self.command == 'plot', 'fill needs at least 2 arguments' ret = [] x, y = self._xy_from_y(y) for j in range(y.shape[1]): color = self._get_next_cycle_color() seg = Line2D(x, y[:,j], color = color, ) self.set_lineprops(seg, **kwargs) ret.append(seg) return ret def _plot_2_args(self, tup2, **kwargs): if is_string_like(tup2[1]): assert self.command == 'plot', 'fill needs at least 2 non-string arguments' y, fmt = tup2 linestyle, marker, color = _process_plot_format(fmt) ret = [] x, y = self._xy_from_y(y) for j in range(y.shape[1]): _color = color if color is None: _color = self._get_next_cycle_color() seg = Line2D(x, y[:,j], color = _color, linestyle=linestyle, marker=marker, ) self.set_lineprops(seg, **kwargs) ret.append(seg) return ret else: x, y = self._xy_from_xy(*tup2) if self.command == 'plot': ret = [] for j in range(y.shape[1]): color = self._get_next_cycle_color() seg = Line2D(x[:,j], y[:,j], color = color, ) self.set_lineprops(seg, **kwargs) ret.append(seg) elif self.command == 'fill': ret = [] for j in range(y.shape[1]): seg = Polygon( zip(x[:,j],y[:,j]), fill=True, ) self.set_patchprops(seg, **kwargs) ret.append(seg) return ret def _plot_3_args(self, tup3, **kwargs): x, y = self._xy_from_xy(tup3[0], tup3[1]) if self.command == 'plot': fmt = tup3[2] linestyle, marker, color = _process_plot_format(fmt) ret = [] for j in range(y.shape[1]): _color = color if color is None: _color = self._get_next_cycle_color() seg = Line2D(x[:,j], y[:,j], color=_color, linestyle=linestyle, marker=marker, ) self.set_lineprops(seg, **kwargs) ret.append(seg) if self.command == 'fill': facecolor = tup3[2] ret = [] for j in range(y.shape[1]): seg = Polygon(zip(x[:,j],y[:,j]), facecolor = facecolor, fill=True, ) self.set_patchprops(seg, **kwargs) ret.append(seg) return ret def _grab_next_args(self, *args, **kwargs): remaining = args while 1: if len(remaining)==0: return if len(remaining)==1: for seg in self._plot_1_arg(remaining[0], **kwargs): yield seg remaining = [] continue if len(remaining)==2: for seg in self._plot_2_args(remaining, **kwargs): yield seg remaining = [] continue if len(remaining)==3: if not is_string_like(remaining[2]): raise ValueError, 'third arg must be a format string' for seg in self._plot_3_args(remaining, **kwargs): yield seg remaining=[] continue if is_string_like(remaining[2]): for seg in self._plot_3_args(remaining[:3], **kwargs): yield seg remaining=remaining[3:] else: for seg in self._plot_2_args(remaining[:2], **kwargs): yield seg remaining=remaining[2:] ValueType=type(zero()) def makeValue(v): if type(v) == ValueType: return v else: return Value(v) class Axes(Artist): """ The Axes contains most of the figure elements: Axis, Tick, Line2D, Text, Polygon etc, and sets the coordinate system """ scaled = {IDENTITY : 'linear', LOG10 : 'log', } def __init__(self, fig, rect, axisbg = None, # defaults to rc axes.facecolor frameon = True, sharex=None, # use Axes instance's xaxis info sharey=None, # use Axes instance's yaxis info label='', **kwargs ): """ Build an Axes instance in Figure with rect=[left, bottom, width,height in Figure coords adjustable: ['box' | 'datalim'] alpha: the alpha transparency anchor: ['C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'] aspect: ['auto' | 'equal' | aspect_ratio] autoscale_on: boolean - whether or not to autoscale the viewlim axis_bgcolor: any matplotlib color - see help(colors) axisbelow: draw the grids and ticks below the other artists cursor_props: a (float, color) tuple figure: a Figure instance frame_on: a boolean - draw the axes frame label: the axes label navigate: True|False navigate_mode: the navigation toolbar button status: 'PAN', 'ZOOM', or None position: [left, bottom, width,height in Figure coords sharex : an Axes instance to share the x-axis with sharey : an Axes instance to share the y-axis with title: the title string visible: a boolean - whether the axes is visible xlabel: the xlabel xlim: (xmin, xmax) view limits xscale: ['log' | 'linear' ] xticklabels: sequence of strings xticks: sequence of floats ylabel: the ylabel strings ylim: (ymin, ymax) view limits yscale: ['log' | 'linear'] yticklabels: sequence of strings yticks: sequence of floats """ Artist.__init__(self) self._position = map(makeValue, rect) self._originalPosition = rect self.set_aspect('auto') self.set_adjustable('box') self.set_anchor('C') # must be set before set_figure self._sharex = sharex self._sharey = sharey # Flag: True if some other Axes instance is sharing our x or y axis self._masterx = False self._mastery = False if sharex: sharex._masterx = True if sharey: sharey._mastery = True self.set_label(label) self.set_figure(fig) # this call may differ for non-sep axes, eg polar self._init_axis() if axisbg is None: axisbg = rcParams['axes.facecolor'] self._axisbg = axisbg self._frameon = frameon self._axisbelow = rcParams['axes.axisbelow'] self._hold = rcParams['axes.hold'] self._connected = {} # a dict from events to (id, func) self.cla() # funcs used to format x and y - fall back on major formatters self.fmt_xdata = None self.fmt_ydata = None self.set_cursor_props((1,'k')) # set the cursor properties for axes self._cachedRenderer = None self.set_navigate(True) self.set_navigate_mode(None) if len(kwargs): setp(self, **kwargs) def get_window_extent(self, *args, **kwargs): 'get the axes bounding box in display space; args and kwargs are empty' return self.bbox def _init_axis(self): "move this out of __init__ because non-separable axes don't use it" self.xaxis = XAxis(self) self.yaxis = YAxis(self) def set_figure(self, fig): """ Set the Axes figure ACCEPTS: a Figure instance """ Artist.set_figure(self, fig) l, b, w, h = self._position xmin = fig.bbox.ll().x() xmax = fig.bbox.ur().x() ymin = fig.bbox.ll().y() ymax = fig.bbox.ur().y() figw = xmax-xmin figh = ymax-ymin self.left = l*figw self.bottom = b*figh self.right = (l+w)*figw self.top = (b+h)*figh self.bbox = Bbox( Point(self.left, self.bottom), Point(self.right, self.top ), ) #these will be updated later as data is added self._set_lim_and_transforms() def _set_lim_and_transforms(self): """ set the dataLim and viewLim BBox attributes and the transData and transAxes Transformation attributes """ if self._sharex is not None: left=self._sharex.viewLim.ll().x() right=self._sharex.viewLim.ur().x() #dleft=self._sharex.dataLim.ll().x() #dright=self._sharex.dataLim.ur().x() else: left=zero() right=one() #dleft=zero() #dright=one() if self._sharey is not None: bottom=self._sharey.viewLim.ll().y() top=self._sharey.viewLim.ur().y() #dbottom=self._sharey.dataLim.ll().y() #dtop=self._sharey.dataLim.ur().y() else: bottom=zero() top=one() #dbottom=zero() #dtop=one() self.viewLim = Bbox(Point(left, bottom), Point(right, top)) #self.dataLim = Bbox(Point(dleft, dbottom), Point(dright, dtop)) self.dataLim = unit_bbox() self.transData = get_bbox_transform(self.viewLim, self.bbox) self.transAxes = get_bbox_transform(unit_bbox(), self.bbox) if self._sharex: self.transData.set_funcx(self._sharex.transData.get_funcx()) if self._sharey: self.transData.set_funcy(self._sharey.transData.get_funcy()) def get_position(self, original=False): 'Return the axes rectangle left, bottom, width, height' if original: return self._originalPosition[:] else: return [val.get() for val in self._position] def set_position(self, pos, which='both'): """ Set the axes position with pos = [left, bottom, width, height] in relative 0,1 coords There are two position variables: one which is ultimately used, but which may be modified by apply_aspect, and a second which is the starting point for apply_aspect. which = 'active' to change the first; 'original' to change the second; 'both' to change both ACCEPTS: len(4) sequence of floats """ if which in ('both', 'active'): # Change values within self._position--don't replace it. for num,val in zip(pos, self._position): val.set(num) if which in ('both', 'original'): self._originalPosition = pos def _set_artist_props(self, a): 'set the boilerplate props for artists added to axes' a.set_figure(self.figure) if not a.is_transform_set(): a.set_transform(self.transData) a.axes = self def cla(self): 'Clear the current axes' self.xaxis.cla() self.yaxis.cla() self.dataLim.ignore(1) if self._sharex is not None: self.xaxis.major = self._sharex.xaxis.major self.xaxis.minor = self._sharex.xaxis.minor if self._sharey is not None: self.yaxis.major = self._sharey.yaxis.major self.yaxis.minor = self._sharey.yaxis.minor self._get_lines = _process_plot_var_args() self._get_patches_for_fill = _process_plot_var_args('fill') self._gridOn = rcParams['axes.grid'] self.lines = [] self.patches = [] self.texts = [] self.tables = [] self.artists = [] self.images = [] self.legend_ = None self.collections = [] # collection.Collection instances self._autoscaleon = True self.grid(self._gridOn) self.title = Text( x=0.5, y=1.02, text='', fontproperties=FontProperties(size=rcParams['axes.titlesize']), verticalalignment='bottom', horizontalalignment='center', ) self.title.set_transform(self.transAxes) self.title.set_clip_box(None) self._set_artist_props(self.title) self.axesPatch = Rectangle( xy=(0,0), width=1, height=1, facecolor=self._axisbg, edgecolor=rcParams['axes.edgecolor'], ) self.axesPatch.set_figure(self.figure) self.axesPatch.set_transform(self.transAxes) self.axesPatch.set_linewidth(rcParams['axes.linewidth']) self.axesFrame = Line2D((0,1,1,0,0), (0,0,1,1,0), linewidth=rcParams['axes.linewidth'], color=rcParams['axes.edgecolor']) self.axesFrame.set_transform(self.transAxes) self.axesFrame.set_zorder(2.5) self.axison = True def clear(self): 'clear the axes' self.cla() def ishold(self): 'return the HOLD status of the axes' return self._hold def hold(self, b=None): """ HOLD(b=None) Set the hold state. If hold is None (default), toggle the hold state. Else set the hold state to boolean value b. Eg hold() # toggle hold hold(True) # hold is on hold(False) # hold is off When hold is True, subsequent plot commands will be added to the current axes. When hold is False, the current axes and figure will be cleared on the next plot command """ if b is None: self._hold = not self._hold else: self._hold = b def get_aspect(self): return self._aspect def set_aspect(self, aspect, adjustable=None, anchor=None): """ aspect: 'auto' - automatic; fill position rectangle with data 'normal' - same as 'auto'; deprecated 'equal' - same scaling from data to plot units for x and y num - a circle will be stretched such that the height is num times the width. aspect=1 is the same as aspect='equal'. adjustable: 'box' - change physical size of axes 'datalim' - change xlim or ylim anchor: 'C' - centered 'SW' - lower left corner 'S' - middle of bottom edge 'SE' - lower right corner etc. ACCEPTS: ['auto' | 'equal' | aspect_ratio] """ if aspect in ('normal', 'auto'): self._aspect = 'auto' elif aspect == 'equal': self._aspect = 'equal' else: self._aspect = float(aspect) # raise ValueError if necessary if adjustable is not None: self.set_adjustable(adjustable) if anchor is not None: self.set_anchor(anchor) def get_adjustable(self): return self._adjustable def set_adjustable(self, adjustable): """ ACCEPTS: ['box' | 'datalim'] """ if adjustable in ('box', 'datalim'): self._adjustable = adjustable else: raise ValueError('argument must be "box", or "datalim"') def get_anchor(self): return self._anchor def set_anchor(self, anchor): """ ACCEPTS: ['C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'] """ if anchor in PBox.coefs.keys() or len(anchor) == 2: self._anchor = anchor else: raise ValueError('argument must be among %s' % ', '.join(PBox.coefs.keys())) def apply_aspect(self, data_ratio = None): ''' Use self._aspect and self._adjustable to modify the axes box or the view limits. The data_ratio kwarg is set to 1 for polar axes. It is used only when _adjustable is 'box'. ''' if self._aspect == 'auto': self.set_position( self._originalPosition , 'active') return if self._aspect == 'equal': A = 1 else: A = self._aspect #Ensure at drawing time that any Axes involved in axis-sharing # does not have its position changed. if self._masterx or self._mastery or self._sharex or self._sharey: self._adjustable = 'datalim' figW,figH = self.get_figure().get_size_inches() fig_aspect = figH/figW #print 'figW, figH, fig_aspect', figW, figH, fig_aspect xmin,xmax = self.get_xlim() xsize = max(math.fabs(xmax-xmin), 1e-30) ymin,ymax = self.get_ylim() ysize = max(math.fabs(ymax-ymin), 1e-30) if self._adjustable == 'box': if data_ratio is None: data_ratio = ysize/xsize box_aspect = A * data_ratio pb = PBox(self._originalPosition) pb1 = pb.shrink_to_aspect(box_aspect, fig_aspect) self.set_position(pb1.anchor(self._anchor), 'active') return l,b,w,h = self.get_position(original=True) box_aspect = fig_aspect * (h/w) data_ratio = box_aspect / A #print 'box_aspect, data_ratio, ysize/xsize', box_aspect, data_ratio, ysize/xsize y_expander = (data_ratio*xsize/ysize - 1.0) #print 'y_expander', y_expander # If y_expander > 0, the dy/dx viewLim ratio needs to increase if abs(y_expander) < 0.005: #print 'good enough already' return dL = self.dataLim xr = 1.05 * dL.width() yr = 1.05 * dL.height() xmarg = xsize - xr ymarg = ysize - yr Ysize = data_ratio * xsize Xsize = ysize / data_ratio Xmarg = Xsize - xr Ymarg = Ysize - yr xm = 0 # Setting these targets to, e.g., 0.05*xr does not seem to help. ym = 0 #print 'xmin, xmax, ymin, ymax', xmin, xmax, ymin, ymax #print 'xsize, Xsize, ysize, Ysize', xsize, Xsize, ysize, Ysize changex = ((self._sharey or self._mastery) and not (self._sharex or self._masterx)) changey = ((self._sharex or self._masterx) and not (self._sharey or self._mastery)) if changex and changey: warnings.warn("adjustable='datalim' cannot work with shared x and y axes") return if changex: adjust_y = False else: #print 'xmarg, ymarg, Xmarg, Ymarg', xmarg, ymarg, Xmarg, Ymarg if xmarg > xm and ymarg > ym: adjy = ((Ymarg > 0 and y_expander < 0) or (Xmarg < 0 and y_expander > 0)) else: adjy = y_expander > 0 #print 'y_expander, adjy', y_expander, adjy adjust_y = changey or adjy #(Ymarg > xmarg) if adjust_y: yc = 0.5*(ymin+ymax) y0 = yc - Ysize/2.0 y1 = yc + Ysize/2.0 self.set_ylim((y0, y1)) #print 'New y0, y1:', y0, y1 #print 'New ysize, ysize/xsize', y1-y0, (y1-y0)/xsize else: xc = 0.5*(xmin+xmax) x0 = xc - Xsize/2.0 x1 = xc + Xsize/2.0 self.set_xlim((x0, x1)) #print 'New x0, x1:', x0, x1 #print 'New xsize, ysize/xsize', x1-x0, ysize/(x1-x0) def axis(self, *v, **kwargs): ''' Convenience method for manipulating the x and y view limits and the aspect ratio of the plot. kwargs are passed on to set_xlim and set_ylim -- see their docstrings for details ''' if len(v)==1 and is_string_like(v[0]): s = v[0].lower() if s=='on': self.set_axis_on() elif s=='off': self.set_axis_off() elif s in ('equal', 'tight', 'scaled', 'normal', 'auto', 'image'): self.set_autoscale_on(True) self.set_aspect('auto') self.autoscale_view() self.apply_aspect() if s=='equal': self.set_aspect('equal', adjustable='datalim') elif s == 'scaled': self.set_aspect('equal', adjustable='box', anchor='C') self.set_autoscale_on(False) # Req. by Mark Bakker elif s=='tight': self.autoscale_view(tight=True) self.set_autoscale_on(False) elif s == 'image': self.autoscale_view(tight=True) self.set_autoscale_on(False) self.set_aspect('equal', adjustable='box', anchor='C') else: raise ValueError('Unrecognized string %s to axis; try on or off' % s) xmin, xmax = self.get_xlim() ymin, ymax = self.get_ylim() return xmin, xmax, ymin, ymax try: v[0] except IndexError: emit = kwargs.get('emit', False) xmin = kwargs.get('xmin', None) xmax = kwargs.get('xmax', None) xmin, xmax = self.set_xlim(xmin, xmax, emit) ymin = kwargs.get('ymin', None) ymax = kwargs.get('ymax', None) ymin, ymax = self.set_ylim(ymin, ymax, emit) return xmin, xmax, ymin, ymax v = v[0] if len(v) != 4: raise ValueError('v must contain [xmin xmax ymin ymax]') self.set_xlim([v[0], v[1]]) self.set_ylim([v[2], v[3]]) return v def get_child_artists(self): """ Return a list of artists the axes contains. Deprecated """ artists = [self.title, self.axesPatch, self.xaxis, self.yaxis] artists.extend(self.lines) artists.extend(self.patches) artists.extend(self.texts) artists.extend(self.collections) artists.extend(self.images) if self.legend_ is not None: artists.append(self.legend_) return silent_list('Artist', artists) def get_frame(self): 'Return the axes Rectangle frame' return self.axesPatch def get_legend(self): 'Return the Legend instance, or None if no legend is defined' return self.legend_ def get_images(self): 'return a list of Axes images contained by the Axes' return silent_list('AxesImage', self.images) def get_lines(self): 'Return a list of lines contained by the Axes' return silent_list('Line2D', self.lines) def get_xaxis(self): 'Return the XAxis instance' return self.xaxis def get_xgridlines(self): 'Get the x grid lines as a list of Line2D instances' return silent_list('Line2D xgridline', self.xaxis.get_gridlines()) def get_xticklines(self): 'Get the xtick lines as a list of Line2D instances' return silent_list('Text xtickline', self.xaxis.get_ticklines()) def get_yaxis(self): 'Return the YAxis instance' return self.yaxis def get_ygridlines(self): 'Get the y grid lines as a list of Line2D instances' return silent_list('Line2D ygridline', self.yaxis.get_gridlines()) def get_yticklines(self): 'Get the ytick lines as a list of Line2D instances' return silent_list('Line2D ytickline', self.yaxis.get_ticklines()) #### Adding and tracking artists def has_data(self): '''Return true if any artists have been added to axes. This should not be used to determine whether the dataLim need to be updated, and may not actually be useful for anything. ''' return ( len(self.collections) + len(self.images) + len(self.lines) + len(self.patches))>0 def add_artist(self, a): 'Add any artist to the axes' a.axes = self # refer to parent self.artists.append(a) self._set_artist_props(a) def add_collection(self, collection, autolim=False): 'add a Collection instance to Axes' self.collections.append(collection) self._set_artist_props(collection) collection.set_clip_box(self.bbox) if autolim: self.update_datalim(collection.get_verts(self.transData)) def add_line(self, l): 'Add a line to the list of plot lines' self._set_artist_props(l) l.set_clip_box(self.bbox) xdata = l.get_xdata(valid_only=True) ydata = l.get_ydata(valid_only=True) if l.get_transform() != self.transData: xys = self._get_verts_in_data_coords( l.get_transform(), zip(xdata, ydata)) xdata = array([x for x,y in xys]) ydata = array([y for x,y in xys]) self.update_datalim_numerix( xdata, ydata ) label = l.get_label() if not label: l.set_label('line%d'%len(self.lines)) self.lines.append(l) def add_patch(self, p): """ Add a patch to the list of Axes patches; the clipbox will be set to the Axes clipping box. If the transform is not set, it wil be set to self.transData. """ self._set_artist_props(p) p.set_clip_box(self.bbox) xys = self._get_verts_in_data_coords( p.get_transform(), p.get_verts()) self.update_datalim(xys) self.patches.append(p) def add_table(self, tab): 'Add a table instance to the list of axes tables' self._set_artist_props(tab) self.tables.append(tab) def update_datalim(self, xys): 'Update the data lim bbox with seq of xy tups or equiv. 2-D array' # if no data is set currently, the bbox will ignore its # limits and set the bound to be the bounds of the xydata. # Otherwise, it will compute the bounds of it's current data # and the data in xydata xys = asarray(xys) self.dataLim.update_numerix_xy(xys, -1) def update_datalim_numerix(self, x, y): 'Update the data lim bbox with seq of xy tups' # if no data is set currently, the bbox will ignore it's # limits and set the bound to be the bounds of the xydata. # Otherwise, it will compute the bounds of it's current data # and the data in xydata #print type(x), type(y) self.dataLim.update_numerix(x, y, -1) def _get_verts_in_data_coords(self, trans, xys): if trans == self.transData: return xys # data is not in axis data units. We must transform it to # display and then back to data to get it in data units #xys = trans.seq_xy_tups(xys) #return [ self.transData.inverse_xy_tup(xy) for xy in xys] xys = trans.numerix_xy(asarray(xys)) return self.transData.inverse_numerix_xy(xys) def in_axes(self, xwin, ywin): 'return True is the point xwin, ywin (display coords) are in the Axes' return self.bbox.contains(xwin, ywin) def get_autoscale_on(self): """ Get whether autoscaling is applied on plot commands """ return self._autoscaleon def set_autoscale_on(self, b): """ Set whether autoscaling is applied on plot commands ACCEPTS: True|False """ self._autoscaleon = b def autoscale_view(self, tight=False, scalex=True, scaley=True): """ autoscale the view limits using the data limits. You can selectively autoscale only a single axis, eg, the xaxis by setting scaley to False. The autoscaling preserves any axis direction reversal that has already been done. """ # if image data only just use the datalim if not self._autoscaleon: return if (tight or (len(self.images)>0 and len(self.lines)==0 and len(self.patches)==0)): if scalex: self.set_xlim(self.dataLim.intervalx().get_bounds()) if scaley: self.set_ylim(self.dataLim.intervaly().get_bounds()) return if scalex: xl = self.get_xlim() XL = self.xaxis.get_major_locator().autoscale() if xl[1] < xl[0]: XL = XL[::-1] self.set_xlim(XL) if scaley: yl = self.get_ylim() YL = self.yaxis.get_major_locator().autoscale() if yl[1] < yl[0]: YL = YL[::-1] self.set_ylim(YL) #### Drawing def draw(self, renderer=None, inframe=False): "Draw everything (plot lines, axes, labels)" if renderer is None: renderer = self._cachedRenderer if renderer is None: raise RuntimeError('No renderer defined') if not self.get_visible(): return renderer.open_group('axes') self.apply_aspect() try: self.transData.freeze() # eval the lazy objects except ValueError: print >> sys.stderr, 'data freeze value error', self.get_position(), self.dataLim.get_bounds(), self.viewLim.get_bounds() raise self.transAxes.freeze() # eval the lazy objects if self.axison and self._frameon: self.axesPatch.draw(renderer) artists = [] if len(self.images)<=1 or renderer.option_image_nocomposite(): for im in self.images: im.draw(renderer) else: # make a composite image blending alpha # list of (_image.Image, ox, oy) mag = renderer.get_image_magnification() ims = [(im.make_image(mag),0,0) for im in self.images if im.get_visible()] im = _image.from_images(self.bbox.height()*mag, self.bbox.width()*mag, ims) im.is_grayscale = False l, b, w, h = self.bbox.get_bounds() # composite images need special args so they will not # respect z-order for now renderer.draw_image(l, b, im, self.bbox) artists.extend(self.collections) artists.extend(self.patches) artists.extend(self.lines) artists.extend(self.texts) artists.extend(self.artists) if self.axison and not inframe: if self._axisbelow: self.xaxis.set_zorder(0.5) self.yaxis.set_zorder(0.5) else: self.xaxis.set_zorder(2.5) self.yaxis.set_zorder(2.5) artists.extend([self.xaxis, self.yaxis]) if not inframe: artists.append(self.title) artists.extend(self.tables) if self.legend_ is not None: artists.append(self.legend_) if self.axison and self._frameon: artists.append(self.axesFrame) # keep track of i to guarantee stable sort for python 2.2 dsu = [ (a.zorder, i, a) for i, a in enumerate(artists) if not a.get_animated()] dsu.sort() for zorder, i, a in dsu: a.draw(renderer) self.transData.thaw() # release the lazy objects self.transAxes.thaw() # release the lazy objects renderer.close_group('axes') self._cachedRenderer = renderer def draw_artist(self, a): """ This method can only be used after an initial draw which caches the renderer. It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) """ assert self._cachedRenderer is not None a.draw(self._cachedRenderer) def redraw_in_frame(self): """ This method can only be used after an initial draw which caches the renderer. It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) """ assert self._cachedRenderer is not None self.draw(self._cachedRenderer, inframe=True) def get_renderer_cache(self): return self._cachedRenderer def __draw_animate(self): # ignore for now; broken if self._lastRenderer is None: raise RuntimeError('You must first call ax.draw()') dsu = [(a.zorder, a) for a in self.animated.keys()] dsu.sort() renderer = self._lastRenderer renderer.blit() for tmp, a in dsu: a.draw(renderer) #### Axes rectangle characteristics def get_frame_on(self): """ Get whether the axes rectangle patch is drawn """ return self._frameon def set_frame_on(self, b): """ Set whether the axes rectangle patch is drawn ACCEPTS: True|False """ self._frameon = b def get_axisbelow(self): """ Get whether axist below is true or not """ return self._axisbelow def set_axisbelow(self, b): """ Set whether the axis ticks and gridlines are above or below most artists ACCEPTS: True|False """ self._axisbelow = b def grid(self, b=None, **kwargs): """ GRID(self, b=None, **kwargs) Set the axes grids on or off; b is a boolean if b is None and len(kwargs)==0, toggle the grid state. if kwargs are supplied, it is assumed that you want a grid and b is thus set to True kawrgs are used to set the grid line properties, eg ax.grid(color='r', linestyle='-', linewidth=2) Valid Line2D kwargs are %(Line2D)s """ if len(kwargs): b = True self.xaxis.grid(b, **kwargs) self.yaxis.grid(b, **kwargs) grid.__doc__ = dedent(grid.__doc__) % artist.kwdocd def ticklabel_format(self, **kwargs): """ Convenience method for manipulating the ScalarFormatter used by default for linear axes. kwargs: style = 'sci' (or 'scientific') or 'plain'; plain turns off scientific notation axis = 'x', 'y', or 'both' Only the major ticks are affected. If the method is called when the ScalarFormatter is not the one being used, an AttributeError will be raised with no additional error message. Additional capabilities and/or friendlier error checking may be added. """ style = kwargs.pop('style', '').lower() axis = kwargs.pop('axis', 'both').lower() if style[:3] == 'sci': sb = True elif style in ['plain', 'comma']: sb = False if style == 'plain': cb = False else: cb = True raise NotImplementedError, "comma style remains to be added" elif style == '': sb = None else: raise ValueError, "%s is not a valid style value" if sb is not None: if axis == 'both' or axis == 'x': self.xaxis.major.formatter.set_scientific(sb) if axis == 'both' or axis == 'y': self.yaxis.major.formatter.set_scientific(sb) def set_axis_off(self): """ turn off the axis ACCEPTS: void """ self.axison = False def set_axis_on(self): """ turn on the axis ACCEPTS: void """ self.axison = True def get_axis_bgcolor(self): 'Return the axis background color' return self._axisbg def set_axis_bgcolor(self, color): """ set the axes background color ACCEPTS: any matplotlib color - see help(colors) """ self._axisbg = color self.axesPatch.set_facecolor(color) ### data limits, ticks, tick labels, and formatting def get_xlim(self): 'Get the x axis range [xmin, xmax]' return self.viewLim.intervalx().get_bounds() def set_xlim(self, xmin=None, xmax=None, emit=False): """ set_xlim(self, *args, **kwargs): Set the limits for the xaxis; v = [xmin, xmax] set_xlim((valmin, valmax)) set_xlim(valmin, valmax) set_xlim(xmin=1) # xmax unchanged set_xlim(xmax=1) # xmin unchanged Valid kwargs: xmin : the min of the xlim xmax : the max of the xlim emit : notify observers of lim change Returns the current xlimits as a length 2 tuple ACCEPTS: len(2) sequence of floats """ if xmax is None and iterable(xmin): xmin,xmax = xmin old_xmin,old_xmax = self.get_xlim() if xmin is None: xmin = old_xmin if xmax is None: xmax = old_xmax if self.transData.get_funcx().get_type()==LOG10 and min(xmin, xmax)<=0: raise ValueError('Cannot set nonpositive limits with log transform') xmin, xmax = nonsingular(xmin, xmax, increasing=False) self.viewLim.intervalx().set_bounds(xmin, xmax) if emit: self._send_xlim_event() return xmin, xmax def get_xscale(self): 'return the xaxis scale string: log or linear' return self.scaled[self.transData.get_funcx().get_type()] def set_xscale(self, value, basex = 10, subsx=None): """ SET_XSCALE(value, basex=10, subsx=None) Set the xscaling: 'log' or 'linear' If value is 'log', the additional kwargs have the following meaning * basex: base of the logarithm * subsx: a sequence of the location of the minor ticks; None defaults to autosubs, which depend on the number of decades in the plot. Eg for base 10, subsx=(1,2,5) will put minor ticks on 1,2,5,11,12,15,21, ....To turn off minor ticking, set subsx=[] ACCEPTS: ['log' | 'linear' ] """ #if subsx is None: subsx = range(2, basex) assert(value.lower() in ('log', 'linear', )) if value == 'log': self.xaxis.set_major_locator(LogLocator(basex)) self.xaxis.set_major_formatter(LogFormatterMathtext(basex)) self.xaxis.set_minor_locator(LogLocator(basex,subsx)) self.transData.get_funcx().set_type(LOG10) minx, maxx = self.get_xlim() if min(minx, maxx)<=0: self.autoscale_view() elif value == 'linear': self.xaxis.set_major_locator(AutoLocator()) self.xaxis.set_major_formatter(ScalarFormatter()) self.xaxis.set_minor_locator(NullLocator()) self.xaxis.set_minor_formatter(NullFormatter()) self.transData.get_funcx().set_type( IDENTITY ) def get_xticks(self): 'Return the x ticks as a list of locations' return self.xaxis.get_ticklocs() def set_xticks(self, ticks): """ Set the x ticks with list of ticks ACCEPTS: sequence of floats """ return self.xaxis.set_ticks(ticks) def get_xticklabels(self): 'Get the xtick labels as a list of Text instances' return silent_list('Text xticklabel', self.xaxis.get_ticklabels()) def set_xticklabels(self, labels, fontdict=None, **kwargs): """ SET_XTICKLABELS(labels, fontdict=None, **kwargs) Set the xtick labels with list of strings labels Return a list of axis text instances. kwargs set the Text properties. Valid properties are %(Text)s ACCEPTS: sequence of strings """ return self.xaxis.set_ticklabels(labels, fontdict, **kwargs) set_xticklabels.__doc__ = dedent(set_xticklabels.__doc__) % artist.kwdocd def get_ylim(self): 'Get the y axis range [ymin, ymax]' return self.viewLim.intervaly().get_bounds() def set_ylim(self, ymin=None, ymax=None, emit=False): """ set_ylim(self, *args, **kwargs): Set the limits for the yaxis; v = [ymin, ymax] set_ylim((valmin, valmax)) set_ylim(valmin, valmax) set_ylim(ymin=1) # ymax unchanged set_ylim(ymax=1) # ymin unchanged Valid kwargs: ymin : the min of the ylim ymax : the max of the ylim emit : notify observers of lim change Returns the current ylimits as a length 2 tuple ACCEPTS: len(2) sequence of floats """ if ymax is None and iterable(ymin): ymin,ymax = ymin old_ymin,old_ymax = self.get_ylim() if ymin is None: ymin = old_ymin if ymax is None: ymax = old_ymax if self.transData.get_funcy().get_type()==LOG10 and min(ymin, ymax)<=0: raise ValueError('Cannot set nonpositive limits with log transform') ymin, ymax = nonsingular(ymin, ymax, increasing=False) self.viewLim.intervaly().set_bounds(ymin, ymax) if emit: self._send_ylim_event() return ymin, ymax def get_yscale(self): 'return the yaxis scale string: log or linear' return self.scaled[self.transData.get_funcy().get_type()] def set_yscale(self, value, basey=10, subsy=None): """ SET_YSCALE(value, basey=10, subsy=None) Set the yscaling: 'log' or 'linear' If value is 'log', the additional kwargs have the following meaning * basey: base of the logarithm * subsy: a sequence of the location of the minor ticks; None defaults to autosubs, which depend on the number of decades in the plot. Eg for base 10, subsy=(1,2,5) will put minor ticks on 1,2,5,11,12,15, 21, ....To turn off minor ticking, set subsy=[] ACCEPTS: ['log' | 'linear'] """ #if subsy is None: subsy = range(2, basey) assert(value.lower() in ('log', 'linear', )) if value == 'log': self.yaxis.set_major_locator(LogLocator(basey)) self.yaxis.set_major_formatter(LogFormatterMathtext(basey)) self.yaxis.set_minor_locator(LogLocator(basey,subsy)) self.transData.get_funcy().set_type(LOG10) miny, maxy = self.get_ylim() if min(miny, maxy)<=0: self.autoscale_view() elif value == 'linear': self.yaxis.set_major_locator(AutoLocator()) self.yaxis.set_major_formatter(ScalarFormatter()) self.yaxis.set_minor_locator(NullLocator()) self.yaxis.set_minor_formatter(NullFormatter()) self.transData.get_funcy().set_type( IDENTITY ) def get_yticks(self): 'Return the y ticks as a list of locations' return self.yaxis.get_ticklocs() def set_yticks(self, ticks): """ Set the y ticks with list of ticks ACCEPTS: sequence of floats """ return self.yaxis.set_ticks(ticks) def get_yticklabels(self): 'Get the ytick labels as a list of Text instances' return silent_list('Text yticklabel', self.yaxis.get_ticklabels()) def set_yticklabels(self, labels, fontdict=None, **kwargs): """ SET_YTICKLABELS(labels, fontdict=None, **kwargs) Set the ytick labels with list of strings labels. Return a list of Text instances. kwargs set Text properties for the labels. Valid properties are %(Text)s ACCEPTS: sequence of strings """ return self.yaxis.set_ticklabels(labels, fontdict, **kwargs) set_yticklabels.__doc__ = dedent(set_yticklabels.__doc__) % artist.kwdocd def toggle_log_lineary(self): 'toggle between log and linear on the y axis' funcy = self.transData.get_funcy().get_type() if funcy==LOG10: self.set_yscale('linear') elif funcy==IDENTITY: self.set_yscale('log') def xaxis_date(self, tz=None): """Sets up x-axis ticks and labels that treat the x data as dates. tz is the time zone to use in labeling dates. Defaults to rc value. """ thislocator = self.xaxis.get_major_locator() if not isinstance(thislocator, DateLocator): locator = AutoDateLocator(tz) self.xaxis.set_major_locator(locator) thisformatter = self.xaxis.get_major_formatter() if not isinstance(thisformatter, DateFormatter): formatter = AutoDateFormatter(locator) self.xaxis.set_major_formatter(formatter) def yaxis_date(self, tz=None): """Sets up y-axis ticks and labels that treat the y data as dates. tz is the time zone to use in labeling dates. Defaults to rc value. """ thislocator = self.yaxis.get_major_locator() if not isinstance(thislocator, DateLocator): locator = AutoDateLocator(tz) self.yaxis.set_major_locator(locator) thisformatter = self.xaxis.get_major_formatter() if not isinstance(thisformatter, DateFormatter): formatter = AutoDateFormatter(locator) self.yaxis.set_major_formatter(formatter) def format_xdata(self, x): """ Return x string formatted. This function will use the attribute self.fmt_xdata if it is callable, else will fall back on the xaxis major formatter """ try: return self.fmt_xdata(x) except TypeError: func = self.xaxis.get_major_formatter().format_data_short val = func(x) return val def format_ydata(self, y): """ Return y string formatted. This function will use the attribute self.fmt_ydata if it is callable, else will fall back on the yaxis major formatter """ try: return self.fmt_ydata(y) except TypeError: func = self.yaxis.get_major_formatter().format_data_short val = func(y) return val def format_coord(self, x, y): 'return a format string formatting the x, y coord' xs = self.format_xdata(x) ys = self.format_ydata(y) return 'x=%s, y=%s'%(xs,ys) #### Interactive manipulation def get_navigate(self): """ Get whether the axes responds to navigation commands """ return self._navigate def set_navigate(self, b): """ Set whether the axes responds to navigation toolbar commands ACCEPTS: True|False """ self._navigate = b def get_navigate_mode(self): """ Get the navigation toolbar button status: 'PAN', 'ZOOM', or None """ return self._navigate_mode def set_navigate_mode(self, b): """ Set the navigation toolbar button status; this is not a user-API function. """ self._navigate_mode = b def get_cursor_props(self): """return the cursor props as a linewidth, color tuple where linewidth is a float and color is an RGBA tuple""" return self._cursorProps def set_cursor_props(self, *args): """ Set the cursor property as ax.set_cursor_props(linewidth, color) OR ax.set_cursor_props((linewidth, color)) ACCEPTS: a (float, color) tuple """ if len(args)==1: lw, c = args[0] elif len(args)==2: lw, c = args else: raise ValueError('args must be a (linewidth, color) tuple') c =colorConverter.to_rgba(c) self._cursorProps = lw, c def _send_xlim_event(self): for cid, func in self._connected.get('xlim_changed', []): func(self) def _send_ylim_event(self): for cid, func in self._connected.get('ylim_changed', []): func(self) def panx(self, numsteps): 'Pan the x axis numsteps (plus pan right, minus pan left)' self.xaxis.pan(numsteps) xmin, xmax = self.viewLim.intervalx().get_bounds() self._send_xlim_event() def pany(self, numsteps): 'Pan the x axis numsteps (plus pan up, minus pan down)' self.yaxis.pan(numsteps) self._send_ylim_event() def zoomx(self, numsteps): 'Zoom in on the x xaxis numsteps (plus for zoom in, minus for zoom out)' self.xaxis.zoom(numsteps) xmin, xmax = self.viewLim.intervalx().get_bounds() self._send_xlim_event() def zoomy(self, numsteps): 'Zoom in on the x xaxis numsteps (plus for zoom in, minus for zoom out)' self.yaxis.zoom(numsteps) self._send_ylim_event() _cid = 0 _events = ('xlim_changed', 'ylim_changed') def connect(self, s, func): """ Register observers to be notified when certain events occur. Register with callback functions with the following signatures. The function has the following signature func(ax) # where ax is the instance making the callback. The following events can be connected to: 'xlim_changed','ylim_changed' The connection id is is returned - you can use this with disconnect to disconnect from the axes event """ if s not in Axes._events: raise ValueError('You can only connect to the following axes events: %s' % ', '.join(Axes._events)) cid = Axes._cid self._connected.setdefault(s, []).append((cid, func)) Axes._cid += 1 return cid def disconnect(self, cid): 'disconnect from the Axes event.' for key, val in self._connected.items(): for item in val: if item[0] == cid: self._connected[key].remove(item) return def get_children(self): 'return a list of child artists' children = [] children.append(self.xaxis) children.append(self.yaxis) children.extend(self.lines) children.extend(self.patches) children.extend(self.texts) children.extend(self.tables) children.extend(self.artists) children.extend(self.images) if self.legend_ is not None: children.append(self.legend_) children.extend(self.collections) children.append(self.title) children.append(self.axesPatch) children.append(self.axesFrame) return children def pick(self, *args): """ pick(mouseevent) each child artist will fire a pick event if mouseevent is over the artist and the artist has picker set """ if len(args)>1: raise DeprecationWarning('New pick API implemented -- see API_CHANGES in the src distribution') mouseevent = args[0] for a in self.get_children(): a.pick(mouseevent) def __pick(self, x, y, trans=None, among=None): """ Return the artist under point that is closest to the x, y. if trans is None, x, and y are in window coords, 0,0 = lower left. Otherwise, trans is a matplotlib transform that specifies the coordinate system of x, y. The selection of artists from amongst which the pick function finds an artist can be narrowed using the optional keyword argument among. If provided, this should be either a sequence of permitted artists or a function taking an artist as its argument and returning a true value if and only if that artist can be selected. Note this algorithm calculates distance to the vertices of the polygon, so if you want to pick a patch, click on the edge! """ if trans is not None: xywin = trans.xy_tup((x,y)) else: xywin = x,y def dist_points(p1, p2): 'return the distance between two points' x1, y1 = p1 x2, y2 = p2 return math.sqrt((x1-x2)**2+(y1-y2)**2) def dist_x_y(p1, x, y): 'x and y are arrays; return the distance to the closest point' x1, y1 = p1 return min(sqrt((x-x1)**2+(y-y1)**2)) def dist(a): if isinstance(a, Text): bbox = a.get_window_extent() l,b,w,h = bbox.get_bounds() verts = (l,b), (l,b+h), (l+w,b+h), (l+w, b) xt, yt = zip(*verts) elif isinstance(a, Patch): verts = a.get_verts() tverts = a.get_transform().seq_xy_tups(verts) xt, yt = zip(*tverts) elif isinstance(a, Line2D): xdata = a.get_xdata(valid_only = True) ydata = a.get_ydata(valid_only = True) xt, yt = a.get_transform().numerix_x_y(xdata, ydata) return dist_x_y(xywin, asarray(xt), asarray(yt)) artists = self.lines + self.patches + self.texts if callable(among): artists = filter(test, artists) elif iterable(among): amongd = dict([(k,1) for k in among]) artists = [a for a in artists if a in amongd] elif among is None: pass else: raise ValueError('among must be callable or iterable') if not len(artists): return None ds = [ (dist(a),a) for a in artists] ds.sort() return ds[0][1] #### Labelling def set_title(self, label, fontdict=None, **kwargs): """ SET_TITLE(label, fontdict=None, **kwargs): Set the title for the axes. See the text docstring for information of how override and the optional args work kwargs are Text properties: %(Text)s ACCEPTS: str """ default = { 'fontsize':rcParams['axes.titlesize'], 'verticalalignment' : 'bottom', 'horizontalalignment' : 'center' } self.title.set_text(label) self.title.update(default) if fontdict is not None: self.title.update(fontdict) self.title.update(kwargs) return self.title set_title.__doc__ = dedent(set_title.__doc__) % artist.kwdocd def set_xlabel(self, xlabel, fontdict=None, **kwargs): """ SET_XLABEL(xlabel, fontdict=None, **kwargs) Set the label for the xaxis. See the text docstring for information of how override and the optional args work. Valid kwargs are Text properties: %(Text)s ACCEPTS: str """ label = self.xaxis.get_label() label.set_text(xlabel) if fontdict is not None: label.update(fontdict) label.update(kwargs) return label set_xlabel.__doc__ = dedent(set_xlabel.__doc__) % artist.kwdocd def set_ylabel(self, ylabel, fontdict=None, **kwargs): """ SET_YLABEL(ylabel, fontdict=None, **kwargs) Set the label for the yaxis See the text doctstring for information of how override and the optional args work Valid kwargs are Text properties: %(Text)s ACCEPTS: str """ label = self.yaxis.get_label() label.set_text(ylabel) if fontdict is not None: label.update(fontdict) label.update(kwargs) return label set_ylabel.__doc__ = dedent(set_ylabel.__doc__) % artist.kwdocd def text(self, x, y, s, fontdict=None, withdash=False, **kwargs): """ TEXT(x, y, s, fontdict=None, **kwargs) Add text in string s to axis at location x,y (data coords) fontdict is a dictionary to override the default text properties. If fontdict is None, the defaults are determined by your rc parameters. withdash=True will create a TextWithDash instance instead of a Text instance. Individual keyword arguments can be used to override any given parameter text(x, y, s, fontsize=12) The default transform specifies that text is in data coords, alternatively, you can specify text in axis coords (0,0 lower left and 1,1 upper right). The example below places text in the center of the axes text(0.5, 0.5,'matplotlib', horizontalalignment='center', verticalalignment='center', transform = ax.transAxes, ) You can put a rectangular box around the text instance (eg to set a background color) by using the keyword bbox. bbox is a dictionary of matplotlib.patches.Rectangle properties (see help for Rectangle for a list of these). For example text(x, y, s, bbox=dict(facecolor='red', alpha=0.5)) Valid kwargs are Text properties %(Text)s """ default = { 'verticalalignment' : 'bottom', 'horizontalalignment' : 'left', #'verticalalignment' : 'top', 'transform' : self.transData, } # At some point if we feel confident that TextWithDash # is robust as a drop-in replacement for Text and that # the performance impact of the heavier-weight class # isn't too significant, it may make sense to eliminate # the withdash kwarg and simply delegate whether there's # a dash to TextWithDash and dashlength. if withdash: t = TextWithDash( x=x, y=y, text=s, ) else: t = Text( x=x, y=y, text=s, ) self._set_artist_props(t) t.update(default) if fontdict is not None: t.update(fontdict) t.update(kwargs) self.texts.append(t) #if t.get_clip_on(): t.set_clip_box(self.bbox) if kwargs.has_key('clip_on'): t.set_clip_box(self.bbox) return t text.__doc__ = dedent(text.__doc__) % artist.kwdocd def annotate(self, *args, **kwargs): """ annotate(self, s, xyloc, textloc, xycoords='data', textcoords='data', lineprops=None, markerprops=None **props) %s """ a = Annotation(*args, **kwargs) a.set_transform(identity_transform()) self._set_artist_props(a) self.texts.append(a) return a annotate.__doc__ = dedent(annotate.__doc__) % Annotation.__doc__ #### Lines and spans def axhline(self, y=0, xmin=0, xmax=1, **kwargs): """ AXHLINE(y=0, xmin=0, xmax=1, **kwargs) Axis Horizontal Line Draw a horizontal line at y from xmin to xmax. With the default values of xmin=0 and xmax=1, this line will always span the horizontal extent of the axes, regardless of the xlim settings, even if you change them, eg with the xlim command. That is, the horizontal extent is in axes coords: 0=left, 0.5=middle, 1.0=right but the y location is in data coordinates. Return value is the Line2D instance. kwargs are the same as kwargs to plot, and can be used to control the line properties. Eg # draw a thick red hline at y=0 that spans the xrange axhline(linewidth=4, color='r') # draw a default hline at y=1 that spans the xrange axhline(y=1) # draw a default hline at y=.5 that spans the the middle half of # the xrange axhline(y=.5, xmin=0.25, xmax=0.75) Valid kwargs are Line2D properties %(Line2D)s """ trans = blend_xy_sep_transform( self.transAxes, self.transData) l, = self.plot([xmin,xmax], [y,y], transform=trans, scalex=False, **kwargs) return l axhline.__doc__ = dedent(axhline.__doc__) % artist.kwdocd def axvline(self, x=0, ymin=0, ymax=1, **kwargs): """ AXVLINE(x=0, ymin=0, ymax=1, **kwargs) Axis Vertical Line Draw a vertical line at x from ymin to ymax. With the default values of ymin=0 and ymax=1, this line will always span the vertical extent of the axes, regardless of the xlim settings, even if you change them, eg with the xlim command. That is, the vertical extent is in axes coords: 0=bottom, 0.5=middle, 1.0=top but the x location is in data coordinates. Return value is the Line2D instance. kwargs are the same as kwargs to plot, and can be used to control the line properties. Eg # draw a thick red vline at x=0 that spans the yrange l = axvline(linewidth=4, color='r') # draw a default vline at x=1 that spans the yrange l = axvline(x=1) # draw a default vline at x=.5 that spans the the middle half of # the yrange axvline(x=.5, ymin=0.25, ymax=0.75) Valid kwargs are Line2D properties %(Line2D)s """ trans = blend_xy_sep_transform( self.transData, self.transAxes ) l, = self.plot([x,x], [ymin,ymax] , transform=trans, scaley=False, **kwargs) return l axvline.__doc__ = dedent(axvline.__doc__) % artist.kwdocd def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs): """ AXHSPAN(ymin, ymax, xmin=0, xmax=1, **kwargs) Axis Horizontal Span. ycoords are in data units and x coords are in axes (relative 0-1) units Draw a horizontal span (regtangle) from ymin to ymax. With the default values of xmin=0 and xmax=1, this always span the xrange, regardless of the xlim settings, even if you change them, eg with the xlim command. That is, the horizontal extent is in axes coords: 0=left, 0.5=middle, 1.0=right but the y location is in data coordinates. kwargs are the kwargs to Patch, eg antialiased, aa linewidth, lw edgecolor, ec facecolor, fc the terms on the right are aliases Return value is the patches.Polygon instance. #draws a gray rectangle from y=0.25-0.75 that spans the horizontal #extent of the axes axhspan(0.25, 0.75, facecolor='0.5', alpha=0.5) Valid kwargs are Polygon properties %(Polygon)s """ trans = blend_xy_sep_transform( self.transAxes, self.transData ) verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) p = Polygon(verts, **kwargs) p.set_transform(trans) self.add_patch(p) return p axhspan.__doc__ = dedent(axhspan.__doc__) % artist.kwdocd def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): """ AXVSPAN(xmin, xmax, ymin=0, ymax=1, **kwargs) axvspan : Axis Vertical Span. xcoords are in data units and y coords are in axes (relative 0-1) units Draw a vertical span (regtangle) from xmin to xmax. With the default values of ymin=0 and ymax=1, this always span the yrange, regardless of the ylim settings, even if you change them, eg with the ylim command. That is, the vertical extent is in axes coords: 0=bottom, 0.5=middle, 1.0=top but the y location is in data coordinates. kwargs are the kwargs to Patch, eg antialiased, aa linewidth, lw edgecolor, ec facecolor, fc the terms on the right are aliases return value is the patches.Polygon instance. # draw a vertical green translucent rectangle from x=1.25 to 1.55 that # spans the yrange of the axes axvspan(1.25, 1.55, facecolor='g', alpha=0.5) Valid kwargs are Polygon properties %(Polygon)s """ trans = blend_xy_sep_transform( self.transData, self.transAxes ) verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] p = Polygon(verts, **kwargs) p.set_transform(trans) self.add_patch(p) return p axvspan.__doc__ = dedent(axvspan.__doc__) % artist.kwdocd def hlines(self, y, xmin, xmax, fmt='k-', **kwargs): """ HLINES(y, xmin, xmax, fmt='k-') plot horizontal lines at each y from xmin to xmax. xmin or xmax can be scalars or len(x) numpy arrays. If they are scalars, then the respective values are constant, else the widths of the lines are determined by xmin and xmax fmt is a plot format string, eg 'g--' Valid kwargs are Line2D properties: %(Line2D)s Returns a list of line instances that were added """ linestyle, marker, color = _process_plot_format(fmt) if color is None: color = 'k' if not iterable(y): y = [y] if not iterable(xmin): xmin = [xmin] if not iterable(xmax): xmax = [xmax] y = asarray(y) xmin = asarray(xmin) xmax = asarray(xmax) if len(xmin)==1: xmin = xmin*ones(y.shape, typecode(y)) if len(xmax)==1: xmax = xmax*ones(y.shape, typecode(y)) if len(xmin)!=len(y): raise ValueError, 'xmin and y are unequal sized sequences' if len(xmax)!=len(y): raise ValueError, 'xmax and y are unequal sized sequences' lines = [] for (thisY, thisMin, thisMax) in zip(y,xmin,xmax): line = Line2D( [thisMin, thisMax], [thisY, thisY], color=color, linestyle=linestyle, marker=marker, **kwargs ) self.add_line( line ) lines.append(line) return lines hlines.__doc__ = dedent(hlines.__doc__) % artist.kwdocd def vlines(self, x, ymin, ymax, fmt='k-', **kwargs): """ VLINES(x, ymin, ymax, color='k') Plot vertical lines at each x from ymin to ymax. ymin or ymax can be scalars or len(x) numpy arrays. If they are scalars, then the respective values are constant, else the heights of the lines are determined by ymin and ymax fmt is a plot format string, eg 'g--' Valid kwargs are Line2D properties: %(Line2D)s Returns a list of lines that were added """ linestyle, marker, color = _process_plot_format(fmt) if color is None: color = 'k' if not iterable(x): x = [x] if not iterable(ymin): ymin = [ymin] if not iterable(ymax): ymax = [ymax] x = asarray(x) ymin = asarray(ymin) ymax = asarray(ymax) if len(ymin)==1: ymin = ymin*ones(x.shape, typecode(x)) if len(ymax)==1: ymax = ymax*ones(x.shape, typecode(x)) if len(ymin)!=len(x): raise ValueError, 'ymin and x are unequal sized sequences' if len(ymax)!=len(x): raise ValueError, 'ymax and x are unequal sized sequences' Y = transpose(array([ymin, ymax])) lines = [] for thisX, thisY in zip(x,Y): line = Line2D( [thisX, thisX], thisY, color=color, linestyle=linestyle, marker=marker, **kwargs ) self.add_line(line) lines.append(line) return lines vlines.__doc__ = dedent(vlines.__doc__) % artist.kwdocd #### Basic plotting def plot(self, *args, **kwargs): """ PLOT(*args, **kwargs) Plot lines and/or markers to the Axes. *args is a variable length argument, allowing for multiple x,y pairs with an optional format string. For example, each of the following is legal plot(x,y) # plot x and y using the default line style and color plot(x,y, 'bo') # plot x and y using blue circle markers plot(y) # plot y using x as index array 0..N-1 plot(y, 'r+') # ditto, but with red plusses If x and/or y is 2-Dimensional, then the corresponding columns will be plotted. An arbitrary number of x, y, fmt groups can be specified, as in a.plot(x1, y1, 'g^', x2, y2, 'g-') Return value is a list of lines that were added. The following line styles are supported: - : solid line -- : dashed line -. : dash-dot line : : dotted line . : points , : pixels o : circle symbols ^ : triangle up symbols v : triangle down symbols < : triangle left symbols > : triangle right symbols s : square symbols + : plus symbols x : cross symbols D : diamond symbols d : thin diamond symbols 1 : tripod down symbols 2 : tripod up symbols 3 : tripod left symbols 4 : tripod right symbols h : hexagon symbols H : rotated hexagon symbols p : pentagon symbols | : vertical line symbols _ : horizontal line symbols steps : use gnuplot style 'steps' # kwarg only The following color strings are supported b : blue g : green r : red c : cyan m : magenta y : yellow k : black w : white Line styles and colors are combined in a single format string, as in 'bo' for blue circles. The **kwargs can be used to set line properties (any property that has a set_* method). You can use this to set a line label (for auto legends), linewidth, anitialising, marker face color, etc. Here is an example: plot([1,2,3], [1,2,3], 'go-', label='line 1', linewidth=2) plot([1,2,3], [1,4,9], 'rs', label='line 2') axis([0, 4, 0, 10]) legend() If you make multiple lines with one plot command, the kwargs apply to all those lines, eg plot(x1, y1, x2, y2, antialised=False) Neither line will be antialiased. The kwargs are Line2D properties: %(Line2D)s kwargs scalex and scaley, if defined, are passed on to autoscale_view to determine whether the x and y axes are autoscaled; default True. See Axes.autoscale_view for more information """ kwargs = kwargs.copy() scalex = popd(kwargs, 'scalex', True) scaley = popd(kwargs, 'scaley', True) if not self._hold: self.cla() lines = [] for line in self._get_lines(*args, **kwargs): self.add_line(line) lines.append(line) self.autoscale_view(scalex=scalex, scaley=scaley) return lines plot.__doc__ = dedent(plot.__doc__) % artist.kwdocd def plot_date(self, x, y, fmt='bo', tz=None, xdate=True, ydate=False, **kwargs): """ PLOT_DATE(x, y, fmt='bo', tz=None, xdate=True, ydate=False, **kwargs) Similar to the plot() command, except the x or y (or both) data is considered to be dates, and the axis is labeled accordingly. x or y (or both) can be a sequence of dates represented as float days since 0001-01-01 UTC. fmt is a plot format string. tz is the time zone to use in labelling dates. Defaults to rc value. If xdate is True, the x-axis will be labeled with dates. If ydate is True, the y-axis will be labeled with dates. Note if you are using custom date tickers and formatters, it may be necessary to set the formatters/locators after the call to plot_date since plot_date will set the default tick locator to AutoDateLocator (if the tick locator is not already set to a DateLocator instance) and the default tick formatter to AutoDateFormatter (if the tick formatter is not already set to a DateFormatter instance). Valid kwargs are Line2D properties: %(Line2D)s See matplotlib.dates for helper functions date2num, num2date and drange for help on creating the required floating point dates """ if not matplotlib._havedate: raise SystemExit('plot_date: no dates support - dates require python2.3') if not self._hold: self.cla() ret = self.plot(x, y, fmt, **kwargs) if xdate: self.xaxis_date(tz) if ydate: self.yaxis_date(tz) self.autoscale_view() return ret plot_date.__doc__ = dedent(plot_date.__doc__) % artist.kwdocd def loglog(self, *args, **kwargs): """ LOGLOG(*args, **kwargs) Make a loglog plot with log scaling on the a and y axis. The args to semilog x are the same as the args to plot. See help plot for more info. Optional keyword args supported are any of the kwargs supported by plot or set_xscale or set_yscale. Notable, for log scaling: * basex: base of the x logarithm * subsx: the location of the minor ticks; None defaults to autosubs, which depend on the number of decades in the plot; see set_xscale for details * basey: base of the y logarithm * subsy: the location of the minor yticks; None defaults to autosubs, which depend on the number of decades in the plot; see set_yscale for details The remaining valid kwargs are Line2D properties: %(Line2D)s """ if not self._hold: self.cla() kwargs = kwargs.copy() dx = {'basex': popd(kwargs,'basex', 10), 'subsx': popd(kwargs,'subsx', None), } dy = {'basey': popd(kwargs,'basey', 10), 'subsy': popd(kwargs,'subsy', None), } self.set_xscale('log', **dx) self.set_yscale('log', **dy) b = self._hold self._hold = True # we've already processed the hold l = self.plot(*args, **kwargs) self._hold = b # restore the hold return l loglog.__doc__ = dedent(loglog.__doc__) % artist.kwdocd def semilogx(self, *args, **kwargs): """ SEMILOGX(*args, **kwargs) Make a semilog plot with log scaling on the x axis. The args to semilog x are the same as the args to plot. See help plot for more info. Optional keyword args supported are any of the kwargs supported by plot or set_xscale. Notable, for log scaling: * basex: base of the logarithm * subsx: the location of the minor ticks; None defaults to autosubs, which depend on the number of decades in the plot; see set_xscale for details The remaining valid kwargs are Line2D properties: %(Line2D)s """ if not self._hold: self.cla() kwargs = kwargs.copy() d = {'basex': popd(kwargs, 'basex', 10), 'subsx': popd(kwargs, 'subsx', None), } self.set_xscale('log', **d) b = self._hold self._hold = True # we've already processed the hold l = self.plot(*args, **kwargs) self._hold = b # restore the hold return l semilogx.__doc__ = dedent(semilogx.__doc__) % artist.kwdocd def semilogy(self, *args, **kwargs): """ SEMILOGY(*args, **kwargs): Make a semilog plot with log scaling on the y axis. The args to semilogy are the same as the args to plot. See help plot for more info. Optional keyword args supported are any of the kwargs supported by plot or set_yscale. Notable, for log scaling: * basey: base of the logarithm * subsy: a sequence of the location of the minor ticks; None defaults to autosubs, which depend on the number of decades in the plot; see set_yscale for details The remaining valid kwargs are Line2D properties: %(Line2D)s """ if not self._hold: self.cla() kwargs = kwargs.copy() d = {'basey': popd(kwargs,'basey', 10), 'subsy': popd(kwargs,'subsy', None), } self.set_yscale('log', **d) b = self._hold self._hold = True # we've already processed the hold l = self.plot(*args, **kwargs) self._hold = b # restore the hold return l semilogy.__doc__ = dedent(semilogy.__doc__) % artist.kwdocd def acorr(self, x, normed=False, detrend=detrend_none, **kwargs): """ Plot the autocorrelation of x. If normed=True, normalize the data but the autocorrelation at 0-th lag. x is detrended by the detrend callable (default no normalization. data are plotted as plot(lags, c, **kwargs) return value is lags, c, line where lags are a length 2*len(x)+1 lag vector, c is the 2*len(x)+1 auto correlation vector, and line is a Line2D instance returned by plot. The default linestyle is None and the default marker is 'o', though these can be overridden with keyword args. The cross correlation is performed with numerix cross_correlate with mode=2. The valid kwargs are Line2D properties: %(Line2D)s """ return self.xcorr(x, x, normed, detrend, **kwargs) acorr.__doc__ = dedent(acorr.__doc__) % artist.kwdocd def xcorr(self, x, y, normed=False, detrend=detrend_none, **kwargs): """ Plot the cross correlation between x and y. If normed=True, normalize the data but the cross correlation at 0-th lag. x and y are detrended by the detrend callable (default no normalization. x and y must be equal length data are plotted as plot(lags, c, **kwargs) return value is lags, c, line where lags are a length 2*len(x)+1 lag vector, c is the 2*len(x)+1 cross correlation vector, and line is a Line2D instance returned by plot. The default linestyle is None and the default marker is 'o', though these can be overridden with keyword args. The cross correlation is performed with numerix cross_correlate with mode=2. The valid kwargs are Line2D properties: %(Line2D)s """ kwargs = kwargs.copy() kwargs.setdefault('marker', 'o') kwargs.setdefault('linestyle', 'None') Nx = len(x) assert(Nx==len(y)) x = detrend(asarray(x)) y = detrend(asarray(y)) c = cross_correlate(x, y, mode=2) if normed: c/=c[Nx-1] lags = arange(-Nx+1,Nx) line, = self.plot(lags, c, **kwargs) return lags, c, line xcorr.__doc__ = dedent(xcorr.__doc__) % artist.kwdocd def legend(self, *args, **kwargs): """ LEGEND(*args, **kwargs) Place a legend on the current axes at location loc. Labels are a sequence of strings and loc can be a string or an integer specifying the legend location USAGE: Make a legend with existing lines >>> legend() legend by itself will try and build a legend using the label property of the lines/patches/collections. You can set the label of a line by doing plot(x, y, label='my data') or line.set_label('my data'). If label is set to '_nolegend_', the item will not be shown in legend. # automatically generate the legend from labels legend( ('label1', 'label2', 'label3') ) # Make a legend for a list of lines and labels legend( (line1, line2, line3), ('label1', 'label2', 'label3') ) # Make a legend at a given location, using a location argument # legend( LABELS, LOC ) or # legend( LINES, LABELS, LOC ) legend( ('label1', 'label2', 'label3'), loc='upper left') legend( (line1, line2, line3), ('label1', 'label2', 'label3'), loc=2) The location codes are 'best' : 0, 'upper right' : 1, (default) 'upper left' : 2, 'lower left' : 3, 'lower right' : 4, 'right' : 5, 'center left' : 6, 'center right' : 7, 'lower center' : 8, 'upper center' : 9, 'center' : 10, If none of these are suitable, loc can be a 2-tuple giving x,y in axes coords, ie, loc = 0, 1 is left top loc = 0.5, 0.5 is center, center and so on. The following kwargs are supported: isaxes=True # whether this is an axes legend numpoints = 4 # the number of points in the legend line prop = FontProperties(size='smaller') # the font property pad = 0.2 # the fractional whitespace inside the legend border markerscale = 0.6 # the relative size of legend markers vs. original shadow # if True, draw a shadow behind legend labelsep = 0.005 # the vertical space between the legend entries handlelen = 0.05 # the length of the legend lines handletextsep = 0.02 # the space between the legend line and legend text axespad = 0.02 # the border between the axes and legend edge """ kwargs = kwargs.copy() def get_handles(): handles = self.lines handles.extend(self.patches) handles.extend([c for c in self.collections if isinstance(c, LineCollection)]) handles.extend([c for c in self.collections if isinstance(c, RegularPolyCollection)]) return handles if len(args)==0: handles = [] labels = [] for line in get_handles(): label = line.get_label() if label != '_nolegend_': handles.append(line) labels.append(label) loc = popd(kwargs, 'loc', 1) elif len(args)==1: # LABELS labels = args[0] handles = [h for h, label in zip(get_handles(), labels)] loc = popd(kwargs, 'loc', 1) elif len(args)==2: if is_string_like(args[1]) or isinstance(args[1], int): # LABELS, LOC labels, loc = args handles = [h for h, label in zip(get_handles(), labels)] else: # LINES, LABELS handles, labels = args loc = popd(kwargs, 'loc', 1) elif len(args)==3: # LINES, LABELS, LOC handles, labels, loc = args else: raise RuntimeError('Invalid arguments to legend') handles = flatten(handles) self.legend_ = Legend(self, handles, labels, loc, **kwargs) return self.legend_ #### Specialized plotting def bar(self, left, height, width=0.8, bottom=None, color=None, edgecolor=None, linewidth=None, yerr=None, xerr=None, ecolor=None, capsize=3, align='edge', orientation='vertical', log=False, **kwargs ): """ BAR(left, height, width=0.8, bottom=0, color=None, edgecolor=None, linewidth=None, yerr=None, xerr=None, ecolor=None, capsize=3, align='edge', orientation='vertical', log=False) Make a bar plot with rectangles bounded by left, left+width, bottom, bottom+height (left, right, bottom and top edges) left, height, width, and bottom can be either scalars or sequences Return value is a list of Rectangle patch instances left - the x coordinates of the left sides of the bars height - the heights of the bars Optional arguments width - the widths of the bars bottom - the y coordinates of the bottom edges of the bars color - the colors of the bars edgecolor - the colors of the bar edges linewidth - width of bar edges; None means use default linewidth; 0 means don't draw edges. xerr and yerr, if not None, will be used to generate errorbars on the bar chart ecolor specifies the color of any errorbar capsize determines the length in points of the error bar caps align = 'edge' | 'center' orientation = 'vertical' | 'horizontal' log = False | True - False (default) leaves the orientation axis as-is; True sets it to log scale For vertical bars, 'edge' aligns bars by their left edges in left, while 'center' interprets these values as the x coordinates of the bar centers. For horizontal bars, 'edge' aligns bars by their bottom edges in bottom, while 'center' interprets these values as the y coordinates of the bar centers. The optional arguments color, edgecolor, yerr, and xerr can be either scalars or sequences of length equal to the number of bars This enables you to use bar as the basis for stacked bar charts, or candlestick plots Optional kwargs: %(Rectangle)s """ if not self._hold: self.cla() def make_iterable(x): if not iterable(x): return [x] else: return x # make them safe to take len() of _left = left left = make_iterable(left) height = make_iterable(height) width = make_iterable(width) _bottom = bottom bottom = make_iterable(bottom) linewidth = make_iterable(linewidth) adjust_ylim = False adjust_xlim = False if orientation == 'vertical': if log: self.set_yscale('log') # size width and bottom according to length of left if _bottom is None: if self.get_yscale() == 'log': bottom = [1e-100] adjust_ylim = True else: bottom = [0] nbars = len(left) if len(width) == 1: width *= nbars if len(bottom) == 1: bottom *= nbars elif orientation == 'horizontal': if log: self.set_xscale('log') # size left and height according to length of bottom if _left is None: if self.get_xscale() == 'log': left = [1e-100] adjust_xlim = True else: left = [0] nbars = len(bottom) if len(left) == 1: left *= nbars if len(height) == 1: height *= nbars else: raise ValueError, 'invalid orientation: %s' % orientation left = asarray(left) height = asarray(height) width = asarray(width) bottom = asarray(bottom) if len(linewidth) == 1: linewidth = linewidth * nbars # if color looks like a color string, an RGB tuple or a # scalar, then repeat it by nbars if (is_string_like(color) or (iterable(color) and len(color)==3 and nbars!=3) or not iterable(color)): color = [color]*nbars # if edgecolor looks like a color string, an RGB tuple or a # scalar, then repeat it by nbars if (is_string_like(edgecolor) or (iterable(edgecolor) and len(edgecolor)==3 and nbars!=3) or not iterable(edgecolor)): edgecolor = [edgecolor]*nbars if yerr is not None: if not iterable(yerr): yerr = asarray([yerr]*nbars, Float) else: yerr = asarray(yerr) if xerr is not None: if not iterable(xerr): xerr = asarray([xerr]*nbars, Float) else: xerr = asarray(xerr) assert len(left)==nbars, "argument 'left' must be %d or scalar" % nbars assert len(height)==nbars, "argument 'height' must be %d or scalar" % nbars assert len(width)==nbars, "argument 'width' must be %d or scalar" % nbars assert len(bottom)==nbars, "argument 'bottom' must be %d or scalar" % nbars assert len(color)==nbars, "argument 'color' must be %d or scalar" % nbars assert len(edgecolor)==nbars, "argument 'edgecolor' must be %d or scalar" % nbars assert len(linewidth)==nbars, "argument 'linewidth' must be %d or scalar" % nbars if yerr is not None: assert len(yerr)==nbars, "bar() argument 'yerr' must be len(%s) or scalar" % nbars if xerr is not None: assert len(xerr)==nbars, "bar() argument 'xerr' must be len(%s) or scalar" % nbars patches = [] if align == 'edge': pass elif align == 'center': if orientation == 'vertical': left = left - width/2. elif orientation == 'horizontal': bottom = bottom - height/2. else: raise ValueError, 'invalid alignment: %s' % align args = zip(left, bottom, width, height, color, edgecolor, linewidth) for l, b, w, h, c, e, lw in args: if h<0: b += h h = abs(h) r = Rectangle( xy=(l, b), width=w, height=h, facecolor=c, edgecolor=e, linewidth=lw, ) r.update(kwargs) self.add_patch(r) patches.append(r) holdstate = self._hold self.hold(True) # ensure hold is on before plotting errorbars if xerr is not None or yerr is not None: if orientation == 'vertical': x, y = left+0.5*width, bottom+height elif orientation == 'horizontal': x, y = left+width, bottom+0.5*height self.errorbar( x, y, yerr=yerr, xerr=xerr, fmt=None, ecolor=ecolor, capsize=capsize) self.hold(holdstate) # restore previous hold state if adjust_xlim: xmin, xmax = self.dataLim.intervalx().get_bounds() xmin = amin(w) if xerr is not None: xmin = xmin - amax(xerr) xmin = max(xmin*0.9, 1e-100) self.dataLim.intervalx().set_bounds(xmin, xmax) if adjust_ylim: ymin, ymax = self.dataLim.intervaly().get_bounds() ymin = amin(h) if yerr is not None: ymin = ymin - amax(yerr) ymin = max(ymin*0.9, 1e-100) self.dataLim.intervaly().set_bounds(ymin, ymax) self.autoscale_view() return patches bar.__doc__ = dedent(bar.__doc__) % artist.kwdocd def barh(self, bottom, width, height=0.8, left=None, color=None, edgecolor=None, linewidth=None, xerr=None, yerr=None, ecolor=None, capsize=3, align='edge' ): """ BARH(bottom, width, height=0.8, left=0, color=None, edgecolor=None, linewidth=None, xerr=None, yerr=None, ecolor=None, capsize=3, align='edge') Make a horizontal bar plot with rectangles bounded by left, left+width, bottom, bottom+height (left, right, bottom and top edges) bottom, width, height, and left can be either scalars or sequences Return value is a list of Rectangle patch instances bottom - the vertical positions of the bottom edges of the bars width - the lengths of the bars Optional arguments height - the heights (thicknesses) of the bars left - the x coordinates of the left edges of the bars color specifies the colors of the bars edgecolor specifies the colors of the bar edges xerr and yerr, if not None, will be used to generate errorbars on the bar chart ecolor specifies the color of any errorbar capsize determines the length in points of the error bar caps align = 'edge' | 'center' 'edge' aligns the horizontal bars by their bottom edges in bottom, while 'center' interprets these values as the y coordinates of the bar centers. The optional arguments color, edgecolor, linewidth, xerr, and yerr can be either scalars or sequences of length equal to the number of bars This enables you to use barh as the basis for stacked bar charts, or candlestick plots """ patches = self.bar(left=left, height=height, width=width, bottom=bottom, color=color, edgecolor=edgecolor, linewidth=linewidth, yerr=yerr, xerr=xerr, ecolor=ecolor, capsize=capsize, align=align, orientation='horizontal' ) return patches def broken_barh(self, xranges, yrange, **kwargs): """ A collection of horizontal bars spanning yrange with a sequence of xranges xranges : sequence of (xmin, xwidth) yrange : (ymin, ywidth) kwargs are collections.BrokenBarHCollection properties %(BrokenBarHCollection)s these can either be a single argument, ie facecolors='black' or a sequence of arguments for the various bars, ie facecolors='black', 'red', 'green' """ col = BrokenBarHCollection(xranges, yrange, **kwargs) self.add_collection(col, autolim=True) self.autoscale_view() return col broken_barh.__doc__ = dedent(broken_barh.__doc__) % artist.kwdocd def stem(self, x, y, linefmt='b-', markerfmt='bo', basefmt='r-'): """ STEM(x, y, linefmt='b-', markerfmt='bo', basefmt='r-') A stem plot plots vertical lines (using linefmt) at each x location from the baseline to y, and places a marker there using markerfmt. A horizontal line at 0 is is plotted using basefmt Return value is (markerline, stemlines, baseline) . See http://www.mathworks.com/access/helpdesk/help/techdoc/ref/stem.html for details and examples/stem_plot.py for a demo. """ remember_hold=self._hold if not self._hold: self.cla() self.hold(True) markerline, = self.plot(x, y, markerfmt) stemlines = [] for thisx, thisy in zip(x, y): l, = self.plot([thisx,thisx], [0, thisy], linefmt) stemlines.append(l) baseline, = self.plot([amin(x), amax(x)], [0,0], basefmt) self.hold(remember_hold) return markerline, stemlines, baseline def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False ): """ PIE(x, explode=None, labels=None, colors=('b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'), autopct=None, pctdistance=0.6, shadow=False) Make a pie chart of array x. The fractional area of each wedge is given by x/sum(x). If sum(x)<=1, then the values of x give the fractional area directly and the array will not be normalized. - explode, if not None, is a len(x) array which specifies the fraction of the radius to offset that wedge. - colors is a sequence of matplotlib color args that the pie chart will cycle. - labels, if not None, is a len(x) list of labels. - autopct, if not None, is a string or function used to label the wedges with their numeric value. The label will be placed inside the wedge. If it is a format string, the label will be fmt%pct. If it is a function, it will be called - pctdistance is the ratio between the center of each pie slice and the start of the text generated by autopct. Ignored if autopct is None; default is 0.6. - shadow, if True, will draw a shadow beneath the pie. The pie chart will probably look best if the figure and axes are square. Eg, figure(figsize=(8,8)) ax = axes([0.1, 0.1, 0.8, 0.8]) Return value: If autopct is None, return a list of (patches, texts), where patches is a sequence of matplotlib.patches.Wedge instances and texts is a list of the label Text instnaces If autopct is not None, return (patches, texts, autotexts), where patches and texts are as above, and autotexts is a list of text instances for the numeric labels """ self.set_frame_on(False) x = asarray(x).astype(Float32) sx = float(asum(x)) if sx>1: x = divide(x,sx) if labels is None: labels = ['']*len(x) if explode is None: explode = [0]*len(x) assert(len(x)==len(labels)) assert(len(x)==len(explode)) if colors is None: colors = ('b', 'g', 'r', 'c', 'm', 'y', 'k', 'w') center = 0,0 radius = 1 theta1 = 0 i = 0 texts = [] slices = [] autotexts = [] for frac, label, expl in zip(x,labels, explode): x, y = center theta2 = theta1 + frac thetam = 2*math.pi*0.5*(theta1+theta2) x += expl*math.cos(thetam) y += expl*math.sin(thetam) w = Wedge((x,y), radius, 360.*theta1, 360.*theta2, facecolor=colors[i%len(colors)]) slices.append(w) self.add_patch(w) w.set_label(label) if shadow: # make sure to add a shadow after the call to # add_patch so the figure and transform props will be # set shad = Shadow(w, -0.02, -0.02, #props={'facecolor':w.get_facecolor()} ) shad.set_zorder(0.9*w.get_zorder()) self.add_patch(shad) xt = x + 1.1*radius*math.cos(thetam) yt = y + 1.1*radius*math.sin(thetam) t = self.text(xt, yt, label, size=rcParams['xtick.labelsize'], horizontalalignment='center', verticalalignment='center') texts.append(t) if autopct is not None: xt = x + pctdistance*radius*math.cos(thetam) yt = y + pctdistance*radius*math.sin(thetam) if is_string_like(autopct): s = autopct%(100.*frac) elif callable(autopct): s = autopct(100.*frac) else: raise TypeError('autopct must be callable or a format string') t = self.text(xt, yt, s, horizontalalignment='center', verticalalignment='center') autotexts.append(t) theta1 = theta2 i += 1 self.set_xlim((-1.25, 1.25)) self.set_ylim((-1.25, 1.25)) self.set_xticks([]) self.set_yticks([]) if autopct is None: return slices, texts else: return slices, texts, autotexts def errorbar(self, x, y, yerr=None, xerr=None, fmt='b-', ecolor=None, capsize=3, barsabove=False, **kwargs): """ ERRORBAR(x, y, yerr=None, xerr=None, fmt='b-', ecolor=None, capsize=3, barsabove=False) Plot x versus y with error deltas in yerr and xerr. Vertical errorbars are plotted if yerr is not None Horizontal errorbars are plotted if xerr is not None xerr and yerr may be any of: a rank-0, Nx1 Numpy array - symmetric errorbars +/- value an N-element list or tuple - symmetric errorbars +/- value a rank-1, Nx2 Numpy array - asymmetric errorbars -column1/+column2 Alternatively, x, y, xerr, and yerr can all be scalars, which plots a single error bar at x, y. fmt is the plot format symbol for y. if fmt is None, just plot the errorbars with no line symbols. This can be useful for creating a bar plot with errorbars ecolor is a matplotlib color arg which gives the color the errorbar lines; if None, use the marker color. capsize is the size of the error bar caps in points barsabove, if True, will plot the errorbars above the plot symbols - default is below kwargs are passed on to the plot command for the markers. So you can add additional key=value pairs to control the errorbar markers. For example, this code makes big red squares with thick green edges >>> x,y,yerr = rand(3,10) >>> errorbar(x, y, yerr, marker='s', mfc='red', mec='green', ms=20, mew=4) mfc, mec, ms and mew are aliases for the longer property names, markerfacecolor, markeredgecolor, markersize and markeredgewith. valid kwargs for the marker properties are %(Line2D)s Return value is a length 2 tuple. The first element is the Line2D instance for the y symbol lines. The second element is a list of error bar lines. """ if not self._hold: self.cla() # make sure all the args are iterable arrays if not iterable(x): x = asarray([x]) else: x = asarray(x) if not iterable(y): y = asarray([y]) else: y = asarray(y) if xerr is not None: if not iterable(xerr): xerr = asarray([xerr]) else: xerr = asarray(xerr) if yerr is not None: if not iterable(yerr): yerr = asarray([yerr]) else: yerr = asarray(yerr) l0 = None if barsabove and fmt is not None: l0, = self.plot(x,y,fmt,**kwargs) caplines = [] barlines = [] if xerr is not None: if len(xerr.shape) == 1: left = x-xerr right = x+xerr else: left = x-xerr[0] right = x+xerr[1] barlines.extend( self.hlines(y, x, left, label='_nolegend_' )) barlines.extend( self.hlines(y, x, right, label='_nolegend_') ) caplines.extend( self.plot(left, y, '|', ms=2*capsize, label='_nolegend_') ) caplines.extend( self.plot(right, y, '|', ms=2*capsize, label='_nolegend_') ) if yerr is not None: if len(yerr.shape) == 1: lower = y-yerr upper = y+yerr else: lower = y-yerr[0] upper = y+yerr[1] barlines.extend( self.vlines(x, y, upper, label='_nolegend_' ) ) barlines.extend( self.vlines(x, y, lower, label='_nolegend_' ) ) caplines.extend( self.plot(x, lower, '_', ms=2*capsize, label='_nolegend_') ) caplines.extend( self.plot(x, upper, '_', ms=2*capsize, label='_nolegend_') ) if not barsabove and fmt is not None: l0, = self.plot(x,y,fmt,**kwargs) if ecolor is None and l0 is None: ecolor = rcParams['lines.color'] elif ecolor is None: ecolor = l0.get_color() for l in barlines: l.set_color(ecolor) for l in caplines: l.set_color(ecolor) self.autoscale_view() ret = silent_list('Line2D errorbar', caplines+barlines) return (l0, ret) errorbar.__doc__ = dedent(errorbar.__doc__) % artist.kwdocd def boxplot(self, x, notch=0, sym='b+', vert=1, whis=1.5, positions=None, widths=None): """ boxplot(x, notch=0, sym='+', vert=1, whis=1.5, positions=None, widths=None) Make a box and whisker plot for each column of x or each vector in sequence x. The box extends from the lower to upper quartile values of the data, with a line at the median. The whiskers extend from the box to show the range of the data. Flier points are those past the end of the whiskers. notch = 0 (default) produces a rectangular box plot. notch = 1 will produce a notched box plot sym (default 'b+') is the default symbol for flier points. Enter an empty string ('') if you don't want to show fliers. vert = 1 (default) makes the boxes vertical. vert = 0 makes horizontal boxes. This seems goofy, but that's how Matlab did it. whis (default 1.5) defines the length of the whiskers as a function of the inner quartile range. They extend to the most extreme data point within ( whis*(75%-25%) ) data range. positions (default 1,2,...,n) sets the horizontal positions of the boxes. The ticks and limits are automatically set to match the positions. widths is either a scalar or a vector and sets the width of each box. The default is 0.5, or 0.15*(distance between extreme positions) if that is smaller. x is an array or a sequence of vectors. Returns a list of the lines added. """ if not self._hold: self.cla() holdStatus = self._hold whiskers, caps, boxes, medians, fliers = [], [], [], [], [] # convert x to a list of vectors if hasattr(x, 'shape'): if len(x.shape) == 1: if hasattr(x[0], 'shape'): x = list(x) else: x = [x,] elif len(x.shape) == 2: nr, nc = x.shape if nr == 1: x = [x] elif nc == 1: x = [ravel(x)] else: x = [x[:,i] for i in range(nc)] else: raise ValueError, "input x can have no more than 2 dimensions" if not hasattr(x[0], '__len__'): x = [x] col = len(x) # get some plot info if positions is None: positions = range(1, col + 1) if widths is None: distance = max(positions) - min(positions) widths = min(0.15*max(distance,1.0), 0.5) if isinstance(widths, float) or isinstance(widths, int): widths = ones((col,), 'd') * widths # loop through columns, adding each to plot self.hold(True) for i,pos in enumerate(positions): d = ravel(x[i]) row = len(d) # get median and quartiles q1, med, q3 = prctile(d,[25,50,75]) # get high extreme iq = q3 - q1 hi_val = q3 + whis*iq wisk_hi = compress( d <= hi_val , d ) if len(wisk_hi) == 0: wisk_hi = q3 else: wisk_hi = max(wisk_hi) # get low extreme lo_val = q1 - whis*iq wisk_lo = compress( d >= lo_val, d ) if len(wisk_lo) == 0: wisk_lo = q1 else: wisk_lo = min(wisk_lo) # get fliers - if we are showing them flier_hi = [] flier_lo = [] flier_hi_x = [] flier_lo_x = [] if len(sym) != 0: flier_hi = compress( d > wisk_hi, d ) flier_lo = compress( d < wisk_lo, d ) flier_hi_x = ones(flier_hi.shape[0]) * pos flier_lo_x = ones(flier_lo.shape[0]) * pos # get x locations for fliers, whisker, whisker cap and box sides box_x_min = pos - widths[i] * 0.5 box_x_max = pos + widths[i] * 0.5 wisk_x = ones(2) * pos cap_x_min = pos - widths[i] * 0.25 cap_x_max = pos + widths[i] * 0.25 cap_x = [cap_x_min, cap_x_max] # get y location for median med_y = [med, med] # calculate 'regular' plot if notch == 0: # make our box vectors box_x = [box_x_min, box_x_max, box_x_max, box_x_min, box_x_min ] box_y = [q1, q1, q3, q3, q1 ] # make our median line vectors med_x = [box_x_min, box_x_max] # calculate 'notch' plot else: notch_max = med + 1.57*iq/sqrt(row) notch_min = med - 1.57*iq/sqrt(row) if notch_max > q3: notch_max = q3 if notch_min < q1: notch_min = q1 # make our notched box vectors box_x = [box_x_min, box_x_max, box_x_max, cap_x_max, box_x_max, box_x_max, box_x_min, box_x_min, cap_x_min, box_x_min, box_x_min ] box_y = [q1, q1, notch_min, med, notch_max, q3, q3, notch_max, med, notch_min, q1] # make our median line vectors med_x = [cap_x_min, cap_x_max] med_y = [med, med] # vertical or horizontal plot? if vert: def doplot(*args): return self.plot(*args) else: def doplot(*args): shuffled = [] for i in range(0, len(args), 3): shuffled.extend([args[i+1], args[i], args[i+2]]) return self.plot(*shuffled) whiskers.extend(doplot(wisk_x, [q1, wisk_lo], 'b--', wisk_x, [q3, wisk_hi], 'b--')) caps.extend(doplot(cap_x, [wisk_hi, wisk_hi], 'k-', cap_x, [wisk_lo, wisk_lo], 'k-')) boxes.extend(doplot(box_x, box_y, 'b-')) medians.extend(doplot(med_x, med_y, 'r-')) fliers.extend(doplot(flier_hi_x, flier_hi, sym, flier_lo_x, flier_lo, sym)) # fix our axes/ticks up a little if 1 == vert: setticks, setlim = self.set_xticks, self.set_xlim else: setticks, setlim = self.set_yticks, self.set_ylim newlimits = min(positions)-0.5, max(positions)+0.5 setlim(newlimits) setticks(positions) # reset hold status self.hold(holdStatus) return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers) def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=1.0, linewidths=None, faceted=True, verts=None, **kwargs): """ SCATTER(x, y, s=20, c='b', marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=1.0, linewidths=None, faceted=True, **kwargs) Supported function signatures: SCATTER(x, y) - make a scatter plot of x vs y SCATTER(x, y, s) - make a scatter plot of x vs y with size in area given by s SCATTER(x, y, s, c) - make a scatter plot of x vs y with size in area given by s and colors given by c SCATTER(x, y, s, c, **kwargs) - control colormapping and scaling with keyword args; see below Make a scatter plot of x versus y. s is a size in points^2 a scalar or an array of the same length as x or y. c is a color and can be a single color format string or an length(x) array of intensities which will be mapped by the matplotlib.colors.colormap instance cmap The marker can be one of 's' : square 'o' : circle '^' : triangle up '>' : triangle right 'v' : triangle down '<' : triangle left 'd' : diamond 'p' : pentagram 'h' : hexagon '8' : octagon If marker is None and verts is not None, verts is a sequence of (x,y) vertices for a custom scatter symbol. s is a size argument in points squared. Any or all of x, y, s, and c may be masked arrays, in which case all masks will be combined and only unmasked points will be plotted. Other keyword args; the color mapping and normalization arguments will on be used if c is an array of floats * cmap = cm.jet : a colors.Colormap instance from matplotlib.cm. defaults to rc image.cmap * norm = Normalize() : matplotlib.colors.Normalize instance is used to scale luminance data to 0,1. * vmin=None and vmax=None : vmin and vmax are used in conjunction with norm to normalize luminance data. If either are None, the min and max of the color array C is used. Note if you pass a norm instance, your settings for vmin and vmax will be ignored * alpha =1.0 : the alpha value for the patches * linewidths, if None, defaults to (lines.linewidth,). Note that this is a tuple, and if you set the linewidths argument you must set it as a sequence of floats, as required by RegularPolyCollection -- see matplotlib.collections.RegularPolyCollection for details * faceted: if True, will use the default edgecolor for the markers. If False, will set the edgecolors to be the same as the facecolors Optional kwargs control the PatchCollection properties: %(PatchCollection)s """ if not self._hold: self.cla() syms = { # a dict from symbol to (numsides, angle) 's' : (4, math.pi/4.0), # square 'o' : (20, 0), # circle '^' : (3,0), # triangle up '>' : (3,math.pi/2.0), # triangle right 'v' : (3,math.pi), # triangle down '<' : (3,3*math.pi/2.0), # triangle left 'd' : (4,0), # diamond 'p' : (5,0), # pentagram 'h' : (6,0), # hexagon '8' : (8,0), # octagon } x, y, s, c = delete_masked_points(x, y, s, c) kwargs = kwargs.copy() if kwargs.has_key('color'): c = kwargs['color'] kwargs.pop('color') if not is_string_like(c) and iterable(c) and len(c)==len(x): colors = None else: colors = ( colorConverter.to_rgba(c, alpha), ) if not iterable(s): scales = (s,) else: scales = s if faceted: edgecolors = None else: edgecolors = 'None' sym = None starlike = False # to be API compatible if marker is None and not (verts is None): marker = (verts, 0) verts = None if is_string_like(marker): # the standard way to define symbols using a string character sym = syms.get(marker) if sym is None and verts is None: raise ValueError('Unknown marker symbol to scatter') numsides, rotation = syms[marker] elif iterable(marker): # accept marker to be: # (numsides, style, [angle]) # or # (verts[], style, [angle]) if len(marker)<2 or len(marker)>3: raise ValueError('Cannot create markersymbol from marker') if is_numlike(marker[0]): # (numsides, style, [angle]) if len(marker)==2: numsides, rotation = marker[0], math.pi/4. elif len(marker)==3: numsides, rotation = marker[0], marker[2] sym = True if marker[1]==1: # starlike symbol, everthing else is interpreted as solid symbol starlike = True else: verts = asarray(marker[0]) if sym is not None: if not starlike: collection = RegularPolyCollection( self.figure.dpi, numsides, rotation, scales, facecolors = colors, edgecolors = edgecolors, linewidths = linewidths, offsets = zip(x,y), transOffset = self.transData, ) else: collection = StarPolygonCollection( self.figure.dpi, numsides, rotation, scales, facecolors = colors, edgecolors = edgecolors, linewidths = linewidths, offsets = zip(x,y), transOffset = self.transData, ) else: # rescale verts rescale = sqrt(max(verts[:,0]**2+verts[:,1]**2)) verts /= rescale scales = asarray(scales) scales = sqrt(scales * self.figure.dpi.get() / 72.) if len(scales)==1: verts = [scales[0]*verts] else: # todo -- make this nx friendly verts = [verts*s for s in scales] collection = PolyCollection( verts, facecolors = colors, edgecolors = edgecolors, linewidths = linewidths, offsets = zip(x,y), transOffset = self.transData, ) collection.set_transform(identity_transform()) collection.set_alpha(alpha) collection.update(kwargs) if colors is None: if norm is not None: assert(isinstance(norm, Normalize)) if cmap is not None: assert(isinstance(cmap, Colormap)) collection.set_array(asarray(c)) collection.set_cmap(cmap) collection.set_norm(norm) if vmin is not None or vmax is not None: collection.set_clim(vmin, vmax) else: collection.autoscale() minx = amin(x) maxx = amax(x) miny = amin(y) maxy = amax(y) w = maxx-minx h = maxy-miny # the pad is a little hack to deal with the fact that we don't # want to transform all the symbols whose scales are in points # to data coords to get the exact bounding box for efficiency # reasons. It can be done right if this is deemed important padx, pady = 0.05*w, 0.05*h corners = (minx-padx, miny-pady), (maxx+padx, maxy+pady) self.update_datalim( corners) self.autoscale_view() # add the collection last self.add_collection(collection) return collection scatter.__doc__ = dedent(scatter.__doc__) % artist.kwdocd def scatter_classic(self, x, y, s=None, c='b'): """ scatter_classic is no longer available; please use scatter. To help in porting, for comparison to the scatter docstring, here is the scatter_classic docstring: SCATTER_CLASSIC(x, y, s=None, c='b') Make a scatter plot of x versus y. s is a size (in data coords) and can be either a scalar or an array of the same length as x or y. c is a color and can be a single color format string or an length(x) array of intensities which will be mapped by the colormap jet. If size is None a default size will be used """ raise NotImplementedError('scatter_classic has been removed;\n' + 'please use scatter instead') def pcolor_classic(self, *args): """ pcolor_classic is no longer available; please use pcolor, which is a drop-in replacement. """ raise NotImplementedError('pcolor_classic has been removed;\n' + 'please use pcolor instead') def arrow(self, x, y, dx, dy, **kwargs): """ Draws arrow on specified axis from (x,y) to (x+dx,y+dy). Optional kwargs control the arrow properties: %(Arrow)s """ a = FancyArrow(x, y, dx, dy, **kwargs) self.add_artist(a) return a arrow.__doc__ = dedent(arrow.__doc__) % artist.kwdocd def quiverkey(self, *args, **kw): qk = QuiverKey(*args, **kw) self.add_artist(qk) return qk quiverkey.__doc__ = QuiverKey.quiverkey_doc def quiver2(self, *args, **kw): q = Quiver(self, *args, **kw) self.add_collection(q) self.update_datalim_numerix(q.X, q.Y) self.autoscale_view() return q quiver2.__doc__ = Quiver.quiver_doc def quiver(self, *args, **kw): if (len(args) == 3 or len(args) == 5) and not iterable(args[-1]): return self.quiver_classic(*args, **kw) c = kw.get('color', None) if c is not None: try: if not is_color_like(c): assert shape(asarray(c)) == shape(asarray(args[-1])) return self.quiver_classic(*args, **kw) except: pass return self.quiver2(*args, **kw) quiver.__doc__ = Quiver.quiver_doc def quiver_classic(self, U, V, *args, **kwargs ): """ QUIVER( X, Y, U, V ) QUIVER( U, V ) QUIVER( X, Y, U, V, S) QUIVER( U, V, S ) QUIVER( ..., color=None, width=1.0, cmap=None, norm=None ) Make a vector plot (U, V) with arrows on a grid (X, Y) If X and Y are not specified, U and V must be 2D arrays. Equally spaced X and Y grids are then generated using the meshgrid command. color can be a color value or an array of colors, so that the arrows can be colored according to another dataset. If cmap is specified and color is 'length', the colormap is used to give a color according to the vector's length. If color is a scalar field, the colormap is used to map the scalar to a color If a colormap is specified and color is an array of color triplets, then the colormap is ignored width is a scalar that controls the width of the arrows if S is specified it is used to scale the vectors. Use S=0 to disable automatic scaling. If S!=0, vectors are scaled to fit within the grid and then are multiplied by S. """ msg = '''This version of quiver is obsolete and will be phased out; please use the new quiver. ''' warnings.warn(msg, DeprecationWarning) if not self._hold: self.cla() do_scale = True S = 1.0 if len(args)==0: # ( U, V ) U = asarray(U) V = asarray(V) X,Y = meshgrid( arange(U.shape[1]), arange(U.shape[0]) ) elif len(args)==1: # ( U, V, S ) U = asarray(U) V = asarray(V) X,Y = meshgrid( arange(U.shape[1]), arange(U.shape[0]) ) S = float(args[0]) do_scale = ( S != 0.0 ) elif len(args)==2: # ( X, Y, U, V ) X = asarray(U) Y = asarray(V) U = asarray(args[0]) V = asarray(args[1]) elif len(args)==3: # ( X, Y, U, V ) X = asarray(U) Y = asarray(V) U = asarray(args[0]) V = asarray(args[1]) S = float(args[2]) do_scale = ( S != 0.0 ) assert U.shape == V.shape assert X.shape == Y.shape assert U.shape == X.shape U = ravel(U) V = ravel(V) X = ravel(X) Y = ravel(Y) arrows = [] N = sqrt( U**2+V**2 ) if do_scale: Nmax = maximum.reduce(N) or 1 # account for div by zero U = U*(S/Nmax) V = V*(S/Nmax) N = N*Nmax kwargs = kwargs.copy() alpha = popd(kwargs,'alpha', 1.0) width = popd(kwargs,'width', .5) norm = popd(kwargs,'norm', None) cmap = popd(kwargs,'cmap', None) vmin = popd(kwargs,'vmin', None) vmax = popd(kwargs,'vmax', None) color = popd(kwargs,'color', None) shading = popd(kwargs,'shading', 'faceted') if len(kwargs): raise TypeError, "quiver() got an unexpected keyword argument '%s'"%kwargs.keys()[0] C = None if color == 'length' or color is True: if color is True: warnings.warn('''Use "color='length'", not "color=True"''', DeprecationWarning) C = N elif color is None: color = (0,0,0,1) else: clr = ravel(asarray(color)) if clr.shape == U.shape: C = clr I = U.shape[0] #arrows = [] #for i in xrange(I): # arrows.append( FancyArrow(X[i],Y[i],U[i],V[i],0.1*S ).get_verts() ) arrows = [FancyArrow(X[i],Y[i],U[i],V[i],0.1*S ).get_verts() for i in xrange(I)] collection = PolyCollection( arrows, edgecolors = 'None', antialiaseds = (1,), linewidths = (width,), ) if C is not None: collection.set_array( ravel(C) ) collection.set_cmap(cmap) collection.set_norm(norm) if norm is not None: collection.set_clim( vmin, vmax ) else: collection.set_facecolor(color) self.add_collection( collection ) lims = asarray(arrows) _max = maximum.reduce( maximum.reduce( lims )) _min = minimum.reduce( minimum.reduce( lims )) self.update_datalim( [ tuple(_min), tuple(_max) ] ) self.autoscale_view() return collection def fill(self, *args, **kwargs): """ FILL(*args, **kwargs) plot filled polygons. *args is a variable length argument, allowing for multiple x,y pairs with an optional color format string; see plot for details on the argument parsing. For example, all of the following are legal, assuming ax is an Axes instance: ax.fill(x,y) # plot polygon with vertices at x,y ax.fill(x,y, 'b' ) # plot polygon with vertices at x,y in blue An arbitrary number of x, y, color groups can be specified, as in ax.fill(x1, y1, 'g', x2, y2, 'r') Return value is a list of patches that were added The same color strings that plot supports are supported by the fill format string. kwargs control the Polygon properties: %(Polygon)s """ if not self._hold: self.cla() patches = [] for poly in self._get_patches_for_fill(*args, **kwargs): self.add_patch( poly ) patches.append( poly ) self.autoscale_view() return patches fill.__doc__ = dedent(fill.__doc__) % artist.kwdocd #### plotting z(x,y): imshow, pcolor and relatives, contour def imshow(self, X, cmap = None, norm = None, aspect=None, interpolation=None, alpha=1.0, vmin = None, vmax = None, origin=None, extent=None, shape=None, filternorm=1, filterrad=4.0, imlim=None, **kwargs): """ IMSHOW(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=1.0, vmin=None, vmax=None, origin=None, extent=None) IMSHOW(X) - plot image X to current axes, resampling to scale to axes size (X may be numarray/Numeric array or PIL image) IMSHOW(X, **kwargs) - Use keyword args to control image scaling, colormapping etc. See below for details Display the image in X to current axes. X may be a float array, a UInt8 array or a PIL image. If X is an array, X can have the following shapes: MxN : luminance (grayscale, float array only) MxNx3 : RGB (float or UInt8 array) MxNx4 : RGBA (float or UInt8 array) The value for each component of MxNx3 and MxNx4 float arrays should be in the range 0.0 to 1.0; MxN float arrays may be normalised. A matplotlib.image.AxesImage instance is returned The following kwargs are allowed: * cmap is a cm colormap instance, eg cm.jet. If None, default to rc image.cmap value (Ignored when X has RGB(A) information) * aspect is one of: auto, equal, or a number. If None, default to rc image.aspect value * interpolation is one of: 'nearest', 'bilinear', 'bicubic', 'spline16', 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos', 'blackman' if interpolation is None, default to rc image.interpolation. See also th the filternorm and filterrad parameters * norm is a matplotlib.colors.Normalize instance; default is normalization(). This scales luminance -> 0-1 (only used for an MxN float array). * vmin and vmax are used to scale a luminance image to 0-1. If either is None, the min and max of the luminance values will be used. Note if you pass a norm instance, the settings for vmin and vmax will be ignored. * alpha = 1.0 : the alpha blending value * origin is either upper or lower, which indicates where the [0,0] index of the array is in the upper left or lower left corner of the axes. If None, default to rc image.origin * extent is a data xmin, xmax, ymin, ymax for making image plots registered with data plots. Default is the image dimensions in pixels * shape is for raw buffer images * filternorm is a parameter for the antigrain image resize filter. From the antigrain documentation, if normalize=1, the filter normalizes integer values and corrects the rounding errors. It doesn't do anything with the source floating point values, it corrects only integers according to the rule of 1.0 which means that any sum of pixel weights must be equal to 1.0. So, the filter function must produce a graph of the proper shape. * filterrad: the filter radius for filters that have a radius parameter, ie when interpolation is one of: 'sinc', 'lanczos' or 'blackman' Additional kwargs are matplotlib.artist properties """ if not self._hold: self.cla() if norm is not None: assert(isinstance(norm, Normalize)) if cmap is not None: assert(isinstance(cmap, Colormap)) if aspect is None: aspect = rcParams['image.aspect'] self.set_aspect(aspect) im = AxesImage(self, cmap, norm, interpolation, origin, extent, filternorm=filternorm, filterrad=filterrad, **kwargs) im.set_data(X) im.set_alpha(alpha) self._set_artist_props(im) #if norm is None and shape is None: # im.set_clim(vmin, vmax) if vmin is not None or vmax is not None: im.set_clim(vmin, vmax) else: im.autoscale() xmin, xmax, ymin, ymax = im.get_extent() corners = (xmin, ymin), (xmax, ymax) self.update_datalim(corners) if self._autoscaleon: self.set_xlim((xmin, xmax)) self.set_ylim((ymin, ymax)) self.images.append(im) return im def pcolor(self, *args, **kwargs): """ pcolor(*args, **kwargs): pseudocolor plot of a 2-D array Function signatures pcolor(C, **kwargs) pcolor(X, Y, C, **kwargs) C is the array of color values X and Y, if given, specify the (x,y) coordinates of the colored quadrilaterals; the quadrilateral for C[i,j] has corners at (X[i,j],Y[i,j]), (X[i,j+1],Y[i,j+1]), (X[i+1,j],Y[i+1,j]), (X[i+1,j+1],Y[i+1,j+1]). Ideally the dimensions of X and Y should be one greater than those of C; if the dimensions are the same, then the last row and column of C will be ignored. Note that the the column index corresponds to the x-coordinate, and the row index corresponds to y; for details, see the "Grid Orientation" section below. If either or both of X and Y are 1-D arrays or column vectors, they will be expanded as needed into the appropriate 2-D arrays, making a rectangular grid. X,Y and C may be masked arrays. If either C[i,j], or one of the vertices surrounding C[i,j] (X or Y at [i,j],[i+1,j], [i,j+1],[i=1,j+1]) is masked, nothing is plotted. Optional keyword args are shown with their defaults below (you must use kwargs for these): * cmap = cm.jet : a cm Colormap instance from matplotlib.cm. defaults to cm.jet * norm = Normalize() : matplotlib.colors.Normalize instance is used to scale luminance data to 0,1. * vmin=None and vmax=None : vmin and vmax are used in conjunction with norm to normalize luminance data. If either are None, the min and max of the color array C is used. If you pass a norm instance, vmin and vmax will be None * shading = 'flat' : or 'faceted'. If 'faceted', a black grid is drawn around each rectangle; if 'flat', edges are not drawn * alpha=1.0 : the alpha blending value Return value is a matplotlib.collections.PatchCollection object Grid Orientation The orientation follows the Matlab(TM) convention: an array C with shape (nrows, ncolumns) is plotted with the column number as X and the row number as Y, increasing up; hence it is plotted the way the array would be printed, except that the Y axis is reversed. That is, C is taken as C(y,x). Similarly for meshgrid: x = arange(5) y = arange(3) X, Y = meshgrid(x,y) is equivalent to X = array([[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]) Y = array([[0, 0, 0, 0, 0], [1, 1, 1, 1, 1], [2, 2, 2, 2, 2]]) so if you have C = rand( len(x), len(y)) then you need pcolor(X, Y, transpose(C)) or pcolor(transpose(C)) Dimensions Matlab pcolor always discards the last row and column of C, but matplotlib displays the last row and column if X and Y are not specified, or if X and Y have one more row and column than C. kwargs can be used to control the PolyCollection properties: %(PolyCollection)s """ if not self._hold: self.cla() alpha = kwargs.pop('alpha', 1.0) norm = kwargs.pop('norm', None) cmap = kwargs.pop('cmap', None) vmin = kwargs.pop('vmin', None) vmax = kwargs.pop('vmax', None) shading = kwargs.pop('shading', 'faceted') if len(args)==1: C = args[0] numRows, numCols = C.shape X, Y = meshgrid(arange(numCols+1), arange(numRows+1) ) elif len(args)==3: X, Y, C = args numRows, numCols = C.shape else: raise TypeError, 'Illegal arguments to pcolor; see help(pcolor)' Nx = X.shape[-1] Ny = Y.shape[0] if len(X.shape) <> 2 or X.shape[0] == 1: X = resize(ravel(X), (Ny, Nx)) if len(Y.shape) <> 2 or Y.shape[1] == 1: Y = transpose(resize(ravel(Y), (Nx, Ny))) # convert to MA, if necessary. C = ma.asarray(C) X = ma.asarray(X) Y = ma.asarray(Y) mask = ma.getmaskarray(X)+ma.getmaskarray(Y) xymask = mask[0:-1,0:-1]+mask[1:,1:]+mask[0:-1,1:]+mask[1:,0:-1] # don't plot if C or any of the surrounding vertices are masked. mask = ma.getmaskarray(C)[0:Ny-1,0:Nx-1]+xymask X1 = compress(ravel(mask==0),ravel(ma.filled(X[0:-1,0:-1]))) Y1 = compress(ravel(mask==0),ravel(ma.filled(Y[0:-1,0:-1]))) X2 = compress(ravel(mask==0),ravel(ma.filled(X[1:,0:-1]))) Y2 = compress(ravel(mask==0),ravel(ma.filled(Y[1:,0:-1]))) X3 = compress(ravel(mask==0),ravel(ma.filled(X[1:,1:]))) Y3 = compress(ravel(mask==0),ravel(ma.filled(Y[1:,1:]))) X4 = compress(ravel(mask==0),ravel(ma.filled(X[0:-1,1:]))) Y4 = compress(ravel(mask==0),ravel(ma.filled(Y[0:-1,1:]))) npoly = len(X1) xy = concatenate((X1[:,newaxis], Y1[:,newaxis], X2[:,newaxis], Y2[:,newaxis], X3[:,newaxis], Y3[:,newaxis], X4[:,newaxis], Y4[:,newaxis]), axis=1) verts = reshape(xy, (npoly, 4, 2)) #verts = zip(zip(X1,Y1),zip(X2,Y2),zip(X3,Y3),zip(X4,Y4)) C = compress(ravel(mask==0),ravel(ma.filled(C[0:Ny-1,0:Nx-1]))) if shading == 'faceted': edgecolors = (0,0,0,1), else: edgecolors = 'None' collection = PolyCollection( verts, edgecolors = edgecolors, antialiaseds = (0,), linewidths = (0.25,), **kwargs ) collection.set_alpha(alpha) collection.set_array(C) if norm is not None: assert(isinstance(norm, Normalize)) if cmap is not None: assert(isinstance(cmap, Colormap)) collection.set_cmap(cmap) collection.set_norm(norm) if vmin is not None or vmax is not None: collection.set_clim(vmin, vmax) else: collection.autoscale() self.grid(False) x = X.compressed() y = Y.compressed() minx = amin(x) maxx = amax(x) miny = amin(y) maxy = amax(y) corners = (minx, miny), (maxx, maxy) self.update_datalim( corners) self.autoscale_view() self.add_collection(collection) return collection pcolor.__doc__ = dedent(pcolor.__doc__) % artist.kwdocd def pcolormesh(self, *args, **kwargs): """ PCOLORMESH(*args, **kwargs) Function signatures PCOLORMESH(C) - make a pseudocolor plot of matrix C PCOLORMESH(X, Y, C) - a pseudo color plot of C on the matrices X and Y PCOLORMESH(C, **kwargs) - Use keyword args to control colormapping and scaling; see below C may be a masked array, but X and Y may not. Masked array support is implemented via cmap and norm; in contrast, pcolor simply does not draw quadrilaterals with masked colors or vertices. Optional keyword args are shown with their defaults below (you must use kwargs for these): * cmap = cm.jet : a cm Colormap instance from matplotlib.cm. defaults to cm.jet * norm = Normalize() : matplotlib.colors.Normalize instance is used to scale luminance data to 0,1. Instantiate it with clip=False if C is a masked array. * vmin=None and vmax=None : vmin and vmax are used in conjunction with norm to normalize luminance data. If either are None, the min and max of the color array C is used. * shading = 'flat' : or 'faceted'. If 'faceted', a black grid is drawn around each rectangle; if 'flat', edge colors are same as face colors * alpha=1.0 : the alpha blending value Return value is a matplotlib.collections.PatchCollection object See pcolor for an explantion of the grid orientation and the expansion of 1-D X and/or Y to 2-D arrays. kwargs can be used to control the QuadMesh polygon collection properties: %(QuadMesh)s """ if not self._hold: self.cla() alpha = kwargs.pop('alpha', 1.0) norm = kwargs.pop('norm', None) cmap = kwargs.pop('cmap', None) vmin = kwargs.pop('vmin', None) vmax = kwargs.pop('vmax', None) shading = kwargs.pop('shading', 'faceted') if len(args)==1: C = args[0] numRows, numCols = C.shape X, Y = meshgrid(arange(numCols+1), arange(numRows+1) ) elif len(args)==3: X, Y, C = args numRows, numCols = C.shape else: raise TypeError, 'Illegal arguments to pcolormesh; see help(pcolormesh)' Nx = X.shape[-1] Ny = Y.shape[0] if len(X.shape) <> 2 or X.shape[0] == 1: X = resize(ravel(X), (Ny, Nx)) if len(Y.shape) <> 2 or Y.shape[1] == 1: Y = transpose(resize(ravel(Y), (Nx, Ny))) # convert to one dimensional arrays C = ma.ravel(C[0:Ny-1, 0:Nx-1]) # data point in each cell is value at lower left corner X = ravel(X) Y = ravel(Y) coords = zeros(((Nx * Ny), 2), Float32) # Numeric and numpy refuse to cast the Float64 arrays # to Float32 with simple assignment, so we do it explicitly. coords[:, 0] = X.astype(Float32) coords[:, 1] = Y.astype(Float32) if shading == 'faceted': showedges = 1 else: showedges = 0 collection = QuadMesh(Nx - 1, Ny - 1, coords, showedges, **kwargs) collection.set_alpha(alpha) collection.set_array(C) if norm is not None: assert(isinstance(norm, Normalize)) if cmap is not None: assert(isinstance(cmap, Colormap)) collection.set_cmap(cmap) collection.set_norm(norm) if vmin is not None or vmax is not None: collection.set_clim(vmin, vmax) else: collection.autoscale() self.grid(False) minx = amin(X) maxx = amax(X) miny = amin(Y) maxy = amax(Y) corners = (minx, miny), (maxx, maxy) self.update_datalim( corners) self.autoscale_view() self.add_collection(collection) return collection pcolormesh.__doc__ = dedent(pcolormesh.__doc__) % artist.kwdocd def contour(self, *args, **kwargs): kwargs['filled'] = False return ContourSet(self, *args, **kwargs) contour.__doc__ = ContourSet.contour_doc def contourf(self, *args, **kwargs): kwargs['filled'] = True return ContourSet(self, *args, **kwargs) contourf.__doc__ = ContourSet.contour_doc def clabel(self, CS, *args, **kwargs): return CS.clabel(*args, **kwargs) clabel.__doc__ = ContourSet.clabel.__doc__ def table(self, **kwargs): """ TABLE(cellText=None, cellColours=None, cellLoc='right', colWidths=None, rowLabels=None, rowColours=None, rowLoc='left', colLabels=None, colColours=None, colLoc='center', loc='bottom', bbox=None): Add a table to the current axes. Returns a table instance. For finer grained control over tables, use the Table class and add it to the axes with add_table. Thanks to John Gill for providing the class and table. kwargs control the Table properties: %(Table)s """ return table.table(self, **kwargs) table.__doc__ = dedent(table.__doc__) % artist.kwdocd #### Data analysis def hist(self, x, bins=10, normed=0, bottom=None, align='edge', orientation='vertical', width=None, log=False, **kwargs): """ HIST(x, bins=10, normed=0, bottom=None, align='edge', orientation='vertical', width=None, log=False, **kwargs) Compute the histogram of x. bins is either an integer number of bins or a sequence giving the bins. x are the data to be binned. The return values is (n, bins, patches) If normed is true, the first element of the return tuple will be the counts normalized to form a probability density, ie, n/(len(x)*dbin). In a probability density, the integral of the histogram should be one (we assume equally spaced bins); you can verify that with # trapezoidal integration of the probability density function from matplotlib.mlab import trapz pdf, bins, patches = ax.hist(...) print trapz(bins, pdf) align = 'edge' | 'center'. Interprets bins either as edge or center values orientation = 'horizontal' | 'vertical'. If horizontal, barh will be used and the "bottom" kwarg will be the left edges. width: the width of the bars. If None, automatically compute the width. log: if True, the histogram axis will be set to a log scale kwargs are used to update the properties of the hist Rectangles: %(Rectangle)s """ if not self._hold: self.cla() n, bins = matplotlib.mlab.hist(x, bins, normed) if width is None: width = 0.9*(bins[1]-bins[0]) if orientation == 'horizontal': patches = self.barh(bins, n, height=width, left=bottom, align=align, log=log) elif orientation == 'vertical': patches = self.bar(bins, n, width=width, bottom=bottom, align=align, log=log) else: raise ValueError, 'invalid orientation: %s' % orientation for p in patches: p.update(kwargs) return n, bins, silent_list('Patch', patches) hist.__doc__ = dedent(hist.__doc__) % artist.kwdocd def psd(self, x, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0, **kwargs): """ PSD(x, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0, **kwargs) The power spectral density by Welches average periodogram method. The vector x is divided into NFFT length segments. Each segment is detrended by function detrend and windowed by function window. noperlap gives the length of the overlap between segments. The absolute(fft(segment))**2 of each segment are averaged to compute Pxx, with a scaling to correct for power loss due to windowing. Fs is the sampling frequency. NFFT is the length of the fft segment; must be a power of 2 Fs is the sampling frequency. detrend - the function applied to each segment before fft-ing, designed to remove the mean or linear trend. Unlike in matlab, where the detrend parameter is a vector, in matplotlib is it a function. The mlab module defines detrend_none, detrend_mean, detrend_linear, but you can use a custom function as well. window - the function used to window the segments. window is a function, unlike in matlab(TM) where it is a vector. mlab defines window_none, window_hanning, but you can use a custom function as well. noverlap gives the length of the overlap between segments. Returns the tuple Pxx, freqs For plotting, the power is plotted as 10*log10(pxx)) for decibels, though pxx itself is returned Refs: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) kwargs control the Line2D properties: %(Line2D)s """ if not self._hold: self.cla() pxx, freqs = matplotlib.mlab.psd(x, NFFT, Fs, detrend, window, noverlap) pxx.shape = len(freqs), self.plot(freqs, 10*log10(pxx), **kwargs) self.set_xlabel('Frequency') self.set_ylabel('Power Spectrum (dB)') self.grid(True) vmin, vmax = self.viewLim.intervaly().get_bounds() intv = vmax-vmin logi = int(log10(intv)) if logi==0: logi=.1 step = 10*logi #print vmin, vmax, step, intv, math.floor(vmin), math.ceil(vmax)+1 ticks = arange(math.floor(vmin), math.ceil(vmax)+1, step) self.set_yticks(ticks) return pxx, freqs psd.__doc__ = dedent(psd.__doc__) % artist.kwdocd def csd(self, x, y, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0, **kwargs): """ CSD(x, y, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0, **kwargs) The cross spectral density Pxy by Welches average periodogram method. The vectors x and y are divided into NFFT length segments. Each segment is detrended by function detrend and windowed by function window. The product of the direct FFTs of x and y are averaged over each segment to compute Pxy, with a scaling to correct for power loss due to windowing. See the PSD help for a description of the optional parameters. Returns the tuple Pxy, freqs. Pxy is the cross spectrum (complex valued), and 10*log10(|Pxy|) is plotted Refs: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) kwargs control the Line2D properties: %(Line2D)s """ if not self._hold: self.cla() pxy, freqs = matplotlib.mlab.csd(x, y, NFFT, Fs, detrend, window, noverlap) pxy.shape = len(freqs), # pxy is complex self.plot(freqs, 10*log10(absolute(pxy)), **kwargs) self.set_xlabel('Frequency') self.set_ylabel('Cross Spectrum Magnitude (dB)') self.grid(True) vmin, vmax = self.viewLim.intervaly().get_bounds() intv = vmax-vmin step = 10*int(log10(intv)) ticks = arange(math.floor(vmin), math.ceil(vmax)+1, step) self.set_yticks(ticks) return pxy, freqs csd.__doc__ = dedent(csd.__doc__) % artist.kwdocd def cohere(self, x, y, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0, **kwargs): """ COHERE(x, y, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0, **kwargs) cohere the coherence between x and y. Coherence is the normalized cross spectral density Cxy = |Pxy|^2/(Pxx*Pyy) The return value is (Cxy, f), where f are the frequencies of the coherence vector. See the PSD help for a description of the optional parameters. kwargs are applied to the lines Returns the tuple Cxy, freqs Refs: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) kwargs control the Line2D properties of the coherence plot: %(Line2D)s """ if not self._hold: self.cla() cxy, freqs = matplotlib.mlab.cohere(x, y, NFFT, Fs, detrend, window, noverlap) self.plot(freqs, cxy, **kwargs) self.set_xlabel('Frequency') self.set_ylabel('Coherence') self.grid(True) return cxy, freqs cohere.__doc__ = dedent(cohere.__doc__) % artist.kwdocd def specgram(self, x, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=128, cmap = None, xextent=None): """ SPECGRAM(x, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=128, cmap=None, xextent=None) Compute a spectrogram of data in x. Data are split into NFFT length segements and the PSD of each section is computed. The windowing function window is applied to each segment, and the amount of overlap of each segment is specified with noverlap. * cmap is a colormap; if None use default determined by rc * xextent is the image extent in the xaxes xextent=xmin, xmax - default 0, max(bins), 0, max(freqs) where bins is the return value from matplotlib.matplotlib.mlab.specgram * See help(psd) for information on the other keyword arguments. Return value is (Pxx, freqs, bins, im), where bins are the time points the spectrogram is calculated over freqs is an array of frequencies Pxx is a len(times) x len(freqs) array of power im is a matplotlib.image.AxesImage. Note: If x is real (i.e. non-complex) only the positive spectrum is shown. If x is complex both positive and negative parts of the spectrum are shown. """ if not self._hold: self.cla() Pxx, freqs, bins = matplotlib.mlab.specgram(x, NFFT, Fs, detrend, window, noverlap) Z = 10*log10(Pxx) Z = flipud(Z) if xextent is None: xextent = 0, amax(bins) xmin, xmax = xextent extent = xmin, xmax, amin(freqs), amax(freqs) im = self.imshow(Z, cmap, extent=extent) self.axis('auto') return Pxx, freqs, bins, im def spy(self, Z, precision=None, marker=None, markersize=None, aspect='equal', **kwargs): """ spy(Z) plots the sparsity pattern of the 2-D array Z If precision is None, any non-zero value will be plotted; else, values of absolute(Z)>precision will be plotted. The array will be plotted as it would be printed, with the first index (row) increasing down and the second index (column) increasing to the right. By default aspect is 'equal' so that each array element occupies a square space; set the aspect kwarg to 'auto' to allow the plot to fill the plot box, or to any scalar number to specify the aspect ratio of an array element directly. Two plotting styles are available: image or marker. Both are available for full arrays, but only the marker style works for scipy.sparse.spmatrix instances. If marker and markersize are None, an image will be returned and any remaining kwargs are passed to imshow; else, a Line2D object will be returned with the value of marker determining the marker type, and any remaining kwargs passed to the axes plot method. If marker and markersize are None, useful kwargs include: cmap alpha See documentation for imshow() for details. For controlling colors, e.g. cyan background and red marks, use: cmap = matplotlib.colors.ListedColormap(['c','r']) If marker or markersize is not None, useful kwargs include: marker markersize color See documentation for plot() for details. Useful values for marker include: 's' square (default) 'o' circle '.' point ',' pixel """ if marker is None and markersize is None: if hasattr(Z, 'tocoo'): raise TypeError, "Image mode does not support scipy.sparse arrays" Z = asarray(Z) if precision is None: mask = Z!=0. else: mask = absolute(Z)>precision if 'cmap' not in kwargs: kwargs['cmap'] = ListedColormap(['w', 'k'], name='binary') nr, nc = Z.shape extent = [-0.5, nc-0.5, nr-0.5, -0.5] return self.imshow(mask, interpolation='nearest', aspect=aspect, extent=extent, origin='upper', **kwargs) else: if hasattr(Z, 'tocoo'): c = Z.tocoo() y = c.row x = c.col z = c.data else: Z = asarray(Z) if precision is None: mask = Z!=0. else: mask = absolute(Z)>precision y,x,z = matplotlib.mlab.get_xyz_where(mask, mask) if marker is None: marker = 's' if markersize is None: markersize = 10 lines = self.plot(x, y, linestyle='None', marker=marker, markersize=markersize, **kwargs) nr, nc = Z.shape self.set_xlim(xmin=-0.5, xmax=nc-0.5) self.set_ylim(ymin=nr-0.5, ymax=-0.5) self.set_aspect(aspect) return lines class SubplotBase: """ Emulate matlab's(TM) subplot command, creating axes with Subplot(numRows, numCols, plotNum) where plotNum=1 is the first plot number and increasing plotNums fill rows first. max(plotNum)==numRows*numCols You can leave out the commas if numRows<=numCols<=plotNum<10, as in Subplot(211) # 2 rows, 1 column, first (upper) plot """ def __init__(self, fig, *args): """ fig is a figure instance args is a varargs to specify the subplot """ self.figure = fig if len(args)==1: s = str(args[0]) if len(s) != 3: raise ValueError('Argument to subplot must be a 3 digits long') rows, cols, num = map(int, s) elif len(args)==3: rows, cols, num = args else: raise ValueError( 'Illegal argument to subplot') total = rows*cols num -= 1 # convert from matlab to python indexing ie num in range(0,total) if num >= total: raise ValueError( 'Subplot number exceeds total subplots') self._rows = rows self._cols = cols self._num = num self.update_params() def get_geometry(self): 'get the subplot geometry, eg 2,2,3' return self._rows, self._cols, self._num+1 def change_geometry(self, numrows, numcols, num): 'change subplot geometry, eg from 1,1,1 to 2,2,3' self._rows = numrows self._cols = numcols self._num = num-1 self.update_params() self.set_position([self.figLeft, self.figBottom, self.figW, self.figH]) def update_params(self): 'update the subplot position from fig.subplotpars' rows = self._rows cols = self._cols num = self._num pars = self.figure.subplotpars left = pars.left right = pars.right bottom = pars.bottom top = pars.top wspace = pars.wspace hspace = pars.hspace totWidth = right-left totHeight = top-bottom figH = totHeight/(rows + hspace*(rows-1)) sepH = hspace*figH figW = totWidth/(cols + wspace*(cols-1)) sepW = wspace*figW rowNum, colNum = divmod(num, cols) figBottom = top - (rowNum+1)*figH - rowNum*sepH figLeft = left + colNum*(figW + sepW) self.figBottom = figBottom self.figLeft = figLeft self.figW = figW self.figH = figH self.rowNum = rowNum self.colNum = colNum self.numRows = rows self.numCols = cols if 0: print 'rcn', rows, cols, num print 'lbrt', left, bottom, right, top print 'self.figBottom', self.figBottom print 'self.figLeft', self.figLeft print 'self.figW', self.figW print 'self.figH', self.figH print 'self.rowNum', self.rowNum print 'self.colNum', self.colNum print 'self.numRows', self.numRows print 'self.numCols', self.numCols def is_first_col(self): return self.colNum==0 def is_first_row(self): return self.rowNum==0 def is_last_row(self): return self.rowNum==self.numRows-1 def is_last_col(self): return self.colNum==self.numCols-1 def label_outer(self): """ set the visible property on ticklabels so xticklabels are visible only if the subplot is in the last row and yticklabels are visible only if the subplot is in the first column """ lastrow = self.is_last_row() firstcol = self.is_first_col() for label in self.get_xticklabels(): label.set_visible(lastrow) for label in self.get_yticklabels(): label.set_visible(firstcol) class Subplot(SubplotBase, Axes): """ Emulate matlab's(TM) subplot command, creating axes with Subplot(numRows, numCols, plotNum) where plotNum=1 is the first plot number and increasing plotNums fill rows first. max(plotNum)==numRows*numCols You can leave out the commas if numRows<=numCols<=plotNum<10, as in Subplot(211) # 2 rows, 1 column, first (upper) plot """ def __init__(self, fig, *args, **kwargs): """ See Axes base class documentation for args and kwargs """ SubplotBase.__init__(self, fig, *args) Axes.__init__(self, fig, [self.figLeft, self.figBottom, self.figW, self.figH], **kwargs) class PolarAxes(Axes): """ Make a PolarAxes. The rectangular bounding box of the axes is given by PolarAxes(position=[left, bottom, width, height]) where all the arguments are fractions in [0,1] which specify the fraction of the total figure window. axisbg is the color of the axis background Attributes: thetagridlines : a list of Line2D for the theta grids rgridlines : a list of Line2D for the radial grids thetagridlabels : a list of Text for the theta grid labels rgridlabels : a list of Text for the theta grid labels """ RESOLUTION = 200 def __init__(self, *args, **kwarg): """ See Axes base class for args and kwargs documentation """ Axes.__init__(self, *args, **kwarg) self.set_aspect('equal', adjustable='box', anchor='C') self.cla() def _init_axis(self): "nuthin to do" pass def _set_lim_and_transforms(self): """ set the dataLim and viewLim BBox attributes and the transData and transAxes Transformation attributes """ # the lim are theta, r self.dataLim = Bbox( Point( Value(5/4.*math.pi), Value(math.sqrt(2))), Point( Value(1/4.*math.pi), Value(math.sqrt(2)))) self.viewLim = Bbox( Point( Value(5/4.*math.pi), Value(math.sqrt(2))), Point( Value(1/4.*math.pi), Value(math.sqrt(2)))) self.transData = NonseparableTransformation(self.viewLim, self.bbox, FuncXY(POLAR)) self.transAxes = get_bbox_transform(unit_bbox(), self.bbox) def cla(self): 'Clear the current axes' # init these w/ some arbitrary numbers - they'll be updated as # data is added to the axes self._get_lines = _process_plot_var_args() self._get_patches_for_fill = _process_plot_var_args('fill') self._gridOn = rcParams['polaraxes.grid'] self.thetagridlabels = [] self.thetagridlines = [] self.rgridlabels = [] self.rgridlines = [] self.lines = [] self.images = [] self.patches = [] self.artists = [] self.collections = [] self.texts = [] # text in axis coords self.grid(self._gridOn) self.title = Text( x=0.5, y=1.05, text='', fontproperties=FontProperties(size=rcParams['axes.titlesize']), verticalalignment='bottom', horizontalalignment='center', ) self.title.set_transform(self.transAxes) self._set_artist_props(self.title) self.thetas = linspace(0,2*math.pi, self.RESOLUTION) verts = zip(self.thetas, ones(self.RESOLUTION)) self.axesPatch = Polygon( verts, facecolor=self._axisbg, edgecolor=rcParams['axes.edgecolor'], ) self.axesPatch.set_figure(self.figure) self.axesPatch.set_transform(self.transData) self.axesPatch.set_linewidth(rcParams['axes.linewidth']) self.axison = True # we need to set a view and data interval from 0->rmax to make # the formatter and locator work correctly self.rintv = Interval(Value(0), Value(1)) self.rintd = Interval(Value(0), Value(1)) self.rformatter = ScalarFormatter() self.rformatter.set_view_interval(self.rintv) self.rformatter.set_data_interval(self.rintd) self.rlocator = AutoLocator() self.rlocator.set_view_interval(self.rintv) self.rlocator.set_data_interval(self.rintd) angles = arange(0, 360, 45) radii = arange(0.2, 1.1, 0.2) self.set_thetagrids(angles) self.set_rgrids(radii) def grid(self, b): 'Set the axes grids on or off; b is a boolean' self._gridOn = b def autoscale_view(self, scalex=True, scaley=True): 'set the view limits to include all the data in the axes' self.rintd.set_bounds(0, self.get_rmax()) rmin, rmax = self.rlocator.autoscale() self.rintv.set_bounds(rmin, rmax) self.axesPatch.xy = zip(self.thetas, rmax*ones(self.RESOLUTION)) val = rmax*math.sqrt(2) self.viewLim.intervaly().set_bounds(val, val) ticks = self.rlocator() self.set_rgrids(ticks) for t in self.thetagridlabels: t.set_y(1.05*rmax) r = linspace(0, rmax, self.RESOLUTION) for l in self.thetagridlines: l.set_ydata(r) def set_rgrids(self, radii, labels=None, angle=22.5, **kwargs): """ set the radial locations and labels of the r grids The labels will appear at radial distances radii at angle labels, if not None, is a len(radii) list of strings of the labels to use at each angle. if labels is None, the self.rformatter will be used Return value is a list of lines, labels where the lines are matplotlib.Line2D instances and the labels are matplotlib.Text instances kwargs control the rgrid Text label properties: %(Text)s ACCEPTS: sequence of floats """ popall(self.rgridlines) theta = linspace(0,2*math.pi, self.RESOLUTION) ls = rcParams['grid.linestyle'] color = rcParams['grid.color'] lw = rcParams['grid.linewidth'] for r in radii: r = ones(self.RESOLUTION)*r line = Line2D(theta, r, linestyle=ls, color=color, linewidth=lw) line.set_transform(self.transData) self.rgridlines.append(line) popall(self.rgridlabels) color = rcParams['xtick.color'] props=FontProperties(size=rcParams['xtick.labelsize']) if labels is None: labels = [self.rformatter(r,0) for r in radii] for r,l in zip(radii, labels): t = Text(angle/180.*math.pi, r, l, fontproperties=props, color=color, horizontalalignment='center', verticalalignment='center') t.set_transform(self.transData) t.update(kwargs) self._set_artist_props(t) t.set_clip_on(False) self.rgridlabels.append(t) return self.rgridlines, self.rgridlabels set_rgrids.__doc__ = dedent(set_rgrids.__doc__) % artist.kwdocd def set_thetagrids(self, angles, labels=None, fmt='%d', frac = 1.1, **kwargs): """ set the angles at which to place the theta grids (these gridlines are equal along the theta dimension). angles is in degrees labels, if not None, is a len(angles) list of strings of the labels to use at each angle. if labels is None, the labels with be fmt%%angle frac is the fraction of the polar axes radius at which to place the label (1 is the edge).Eg 1.05 isd outside the axes and 0.95 is inside the axes Return value is a list of lines, labels where the lines are matplotlib.Line2D instances and the labels are matplotlib.Text instances: kwargs are optional text properties for the labels %(Text)s ACCEPTS: sequence of floats """ popall(self.thetagridlines) ox, oy = 0,0 ls = rcParams['grid.linestyle'] color = rcParams['grid.color'] lw = rcParams['grid.linewidth'] r = linspace(0, self.get_rmax(), self.RESOLUTION) for a in angles: theta = ones(self.RESOLUTION)*a/180.*math.pi line = Line2D(theta, r, linestyle=ls, color=color, linewidth=lw) line.set_transform(self.transData) self.thetagridlines.append(line) popall(self.thetagridlabels) color = rcParams['xtick.color'] props=FontProperties(size=rcParams['xtick.labelsize']) r = frac*self.get_rmax() if labels is None: labels = [fmt%a for a in angles] for a,l in zip(angles, labels): t = Text(a/180.*math.pi, r, l, fontproperties=props, color=color, horizontalalignment='center', verticalalignment='center') t.set_transform(self.transData) t.update(kwargs) self._set_artist_props(t) t.set_clip_on(False) self.thetagridlabels.append(t) return self.thetagridlines, self.thetagridlabels set_thetagrids.__doc__ = dedent(set_thetagrids.__doc__) % artist.kwdocd def get_rmax(self): 'get the maximum radius in the view limits dimension' vmin, vmax = self.dataLim.intervaly().get_bounds() return max(vmin, vmax) def draw(self, renderer): if not self.get_visible(): return renderer.open_group('polar_axes') self.apply_aspect(1) self.transData.freeze() # eval the lazy objects self.transAxes.freeze() # eval the lazy objects #self._update_axes() if self.axison: if self._frameon: self.axesPatch.draw(renderer) if self._gridOn: for l in self.rgridlines+self.thetagridlines: l.draw(renderer) for t in self.thetagridlabels+self.rgridlabels: t.draw(renderer) artists = [] artists.extend(self.lines) artists.extend(self.texts) artists.extend(self.collections) artists.extend(self.patches) artists.extend(self.artists) dsu = [ (a.zorder, a) for a in artists] dsu.sort() for zorder, a in dsu: a.draw(renderer) self.title.draw(renderer) self.transData.thaw() # release the lazy objects self.transAxes.thaw() # release the lazy objects renderer.close_group('polar_axes') def format_coord(self, theta, r): 'return a format string formatting the coordinate' theta /= math.pi return 'theta=%1.2fpi, r=%1.3f'%(theta, r) def has_data(self): 'return true if any artists have been added to axes' return len(self.lines)+len(self.collections) def set_xlabel(self, xlabel, fontdict=None, **kwargs): 'xlabel not implemented' raise NotImplementedError('xlabel not defined for polar axes (yet)') def set_ylabel(self, ylabel, fontdict=None, **kwargs): 'ylabel not implemented' raise NotImplementedError('ylabel not defined for polar axes (yet)') def set_xlim(self, v, emit=True): """ SET_XLIM(v, emit=True) A do nothing impl until we can figure out how to handle interaction ACCEPTS: len(2) sequence of floats """ #warnings.warn('Navigation set_ylim not implemented for polar') self.viewLim.intervalx().set_bounds(*v) if emit: self._send_xlim_event() def set_ylim(self, v, emit=True): """ SET_YLIM(v, emit=True) ACCEPTS: len(2) sequence of floats """ #warnings.warn('Navigation set_xlim not implemented for polar') self.viewLim.intervaly().set_bounds(*v) if emit: self._send_ylim_event() def get_xscale(self): 'return the xaxis scale string' return 'polar' def get_yscale(self): 'return the yaxis scale string' return 'polar' def toggle_log_lineary(self): 'toggle between log and linear axes ignored for polar' pass def legend(self, *args, **kwargs): """ LEGEND(*args, **kwargs) Not implemented for polar yet -- use figlegend """ raise NotImplementedError('legend not implemented for polar yet -- use figlegend') def table(self, *args, **kwargs): """ TABLE(*args, **kwargs) Not implemented for polar axes """ raise NotImplementedError('table not implemented for polar axes') class PolarSubplot(SubplotBase, PolarAxes): """ Create a polar subplot with PolarSubplot(numRows, numCols, plotNum) where plotNum=1 is the first plot number and increasing plotNums fill rows first. max(plotNum)==numRows*numCols You can leave out the commas if numRows<=numCols<=plotNum<10, as in Subplot(211) # 2 rows, 1 column, first (upper) plot """ def __init__(self, fig, *args, **kwargs): SubplotBase.__init__(self, fig, *args) PolarAxes.__init__(self, fig, [self.figLeft, self.figBottom, self.figW, self.figH], **kwargs) artist.kwdocd['Axes'] = artist.kwdocd['Subplot'] = artist.kwdoc(Axes) """ # this is some discarded code I was using to find the minimum positive # data point for some log scaling fixes. I realized there was a # cleaner way to do it, but am keeping this around as an example for # how to get the data out of the axes. Might want to make something # like this a method one day, or better yet make get_verts and Artist # method minx, maxx = self.get_xlim() if minx<=0 or maxx<=0: # find the min pos value in the data xs = [] for line in self.lines: xs.extend(line.get_xdata(valid_only = True)) for patch in self.patches: xs.extend([x for x,y in patch.get_verts()]) for collection in self.collections: xs.extend([x for x,y in collection.get_verts()]) posx = [x for x in xs if x>0] if len(posx): minx = min(posx) maxx = max(posx) # warning, probably breaks inverted axis self.set_xlim((0.1*minx, maxx)) """