def append_axes(self, position, size, pad=None, add_to_figure=True, **kwargs): """ create an axes at the given *position* with the same height (or width) of the main axes. *position* ["left"|"right"|"bottom"|"top"] *size* and *pad* should be axes_grid.axes_size compatible. """ if position == "left": ax = self.new_horizontal(size, pad, pack_start=True, **kwargs) elif position == "right": ax = self.new_horizontal(size, pad, pack_start=False, **kwargs) elif position == "bottom": ax = self.new_vertical(size, pad, pack_start=True, **kwargs) elif position == "top": ax = self.new_vertical(size, pad, pack_start=False, **kwargs) else: cbook._check_in_list(["left", "right", "bottom", "top"], position=position) if add_to_figure: self._fig.add_axes(ax) return ax
def set_anchor(self, anchor): """ Parameters ---------- anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'} anchor position ===== ============ value description ===== ============ 'C' Center 'SW' bottom left 'S' bottom 'SE' bottom right 'E' right 'NE' top right 'N' top 'NW' top left 'W' left ===== ============ """ if len(anchor) != 2: cbook._check_in_list(mtransforms.Bbox.coefs, anchor=anchor) self._anchor = anchor
def _single_spectrum_helper(x, mode, Fs=None, window=None, pad_to=None, sides=None): ''' This is a helper function that implements the commonality between the complex, magnitude, angle, and phase spectrums. It is *NOT* meant to be used outside of mlab and may change at any time. ''' cbook._check_in_list(['complex', 'magnitude', 'angle', 'phase'], mode=mode) if pad_to is None: pad_to = len(x) spec, freqs, _ = _spectral_helper(x=x, y=None, NFFT=len(x), Fs=Fs, detrend_func=detrend_none, window=window, noverlap=0, pad_to=pad_to, sides=sides, scale_by_freq=False, mode=mode) if mode != 'complex': spec = spec.real if spec.ndim == 2 and spec.shape[1] == 1: spec = spec[:, 0] return spec, freqs
def _h_arrows(self, length): """Length is in arrow width units.""" # It might be possible to streamline the code # and speed it up a bit by using complex (x,y) # instead of separate arrays; but any gain would be slight. minsh = self.minshaft * self.headlength N = len(length) length = length.reshape(N, 1) # This number is chosen based on when pixel values overflow in Agg # causing rendering errors # length = np.minimum(length, 2 ** 16) np.clip(length, 0, 2 ** 16, out=length) # x, y: normal horizontal arrow x = np.array([0, -self.headaxislength, -self.headlength, 0], np.float64) x = x + np.array([0, 1, 1, 1]) * length y = 0.5 * np.array([1, 1, self.headwidth, 0], np.float64) y = np.repeat(y[np.newaxis, :], N, axis=0) # x0, y0: arrow without shaft, for short vectors x0 = np.array([0, minsh - self.headaxislength, minsh - self.headlength, minsh], np.float64) y0 = 0.5 * np.array([1, 1, self.headwidth, 0], np.float64) ii = [0, 1, 2, 3, 2, 1, 0, 0] X = x[:, ii] Y = y[:, ii] Y[:, 3:-1] *= -1 X0 = x0[ii] Y0 = y0[ii] Y0[3:-1] *= -1 shrink = length / minsh if minsh != 0. else 0. X0 = shrink * X0[np.newaxis, :] Y0 = shrink * Y0[np.newaxis, :] short = np.repeat(length < minsh, 8, axis=1) # Now select X0, Y0 if short, otherwise X, Y np.copyto(X, X0, where=short) np.copyto(Y, Y0, where=short) if self.pivot == 'middle': X -= 0.5 * X[:, 3, np.newaxis] elif self.pivot == 'tip': # numpy bug? using -= does not work here unless we multiply by a # float first, as with 'mid'. X = X - X[:, 3, np.newaxis] elif self.pivot != 'tail': cbook._check_in_list(["middle", "tip", "tail"], pivot=self.pivot) tooshort = length < self.minlength if tooshort.any(): # Use a heptagonal dot: th = np.arange(0, 8, 1, np.float64) * (np.pi / 3.0) x1 = np.cos(th) * self.minlength * 0.5 y1 = np.sin(th) * self.minlength * 0.5 X1 = np.repeat(x1[np.newaxis, :], N, axis=0) Y1 = np.repeat(y1[np.newaxis, :], N, axis=0) tooshort = np.repeat(tooshort, 8, 1) np.copyto(X, X1, where=tooshort) np.copyto(Y, Y1, where=tooshort) # Mask handling is deferred to the caller, _make_verts. return X, Y
def set_variant(self, variant): """ Set the font variant. Values are: 'normal' or 'small-caps'. """ if variant is None: variant = rcParams['font.variant'] cbook._check_in_list(['normal', 'small-caps'], variant=variant) self._variant = variant
def __init__(self, axis, nonpos='mask'): """ *nonpos*: {'mask', 'clip'} values beyond ]0, 1[ can be masked as invalid, or clipped to a number very close to 0 or 1 """ cbook._check_in_list(['mask', 'clip'], nonpos=nonpos) self._transform = LogitTransform(nonpos)
def set_style(self, style): """ Set the font style. Values are: 'normal', 'italic' or 'oblique'. """ if style is None: style = rcParams['font.style'] cbook._check_in_list(['normal', 'italic', 'oblique'], style=style) self._slant = style
def __init__(self, ax, *args, scale=None, headwidth=3, headlength=5, headaxislength=4.5, minshaft=1, minlength=1, units='width', scale_units=None, angles='uv', width=None, color='k', pivot='tail', **kw): """ The constructor takes one required argument, an Axes instance, followed by the args and kwargs described by the following pyplot interface documentation: %s """ self.ax = ax X, Y, U, V, C = _parse_args(*args) self.X = X self.Y = Y self.XY = np.column_stack((X, Y)) self.N = len(X) self.scale = scale self.headwidth = headwidth self.headlength = float(headlength) self.headaxislength = headaxislength self.minshaft = minshaft self.minlength = minlength self.units = units self.scale_units = scale_units self.angles = angles self.width = width if pivot.lower() == 'mid': pivot = 'middle' self.pivot = pivot.lower() cbook._check_in_list(self._PIVOT_VALS, pivot=self.pivot) self.transform = kw.pop('transform', ax.transData) kw.setdefault('facecolors', color) kw.setdefault('linewidths', (0,)) mcollections.PolyCollection.__init__(self, [], offsets=self.XY, transOffset=self.transform, closed=False, **kw) self.polykw = kw self.set_UVC(U, V, C) self._initialized = False # try to prevent closure over the real self weak_self = weakref.ref(self) def on_dpi_change(fig): self_weakref = weak_self() if self_weakref is not None: self_weakref._new_UV = True # vertices depend on width, span # which in turn depend on dpi self_weakref._initialized = False # simple brute force update # works because _init is # called at the start of # draw. self._cid = self.ax.figure.callbacks.connect('dpi_changed', on_dpi_change)
def update_viewlim(self): viewlim = self._parent_axes.viewLim.frozen() mode = self.get_viewlim_mode() if mode is None: pass elif mode == "equal": self.axes.viewLim.set(viewlim) elif mode == "transform": self.axes.viewLim.set( viewlim.transformed(self.transAux.inverted())) else: cbook._check_in_list([None, "equal", "transform"], mode=mode)
def scale_factory(scale, axis, **kwargs): """ Return a scale class by name. Parameters ---------- scale : {%(names)s} axis : Axis """ scale = scale.lower() cbook._check_in_list(_scale_mapping, scale=scale) return _scale_mapping[scale](axis, **kwargs)
def set_axislabel_direction(self, label_direction): r""" Adjust the direction of the axislabel. Note that the *label_direction*\s '+' and '-' are relative to the direction of the increasing coordinate. Parameters ---------- tick_direction : {"+", "-"} """ cbook._check_in_list(["+", "-"], label_direction=label_direction) self._axislabel_add_angle = {"+": 0, "-": 180}[label_direction]
def set_image_mode(self, mode): """ Set the image mode for any subsequent images which will be sent to the clients. The modes may currently be either 'full' or 'diff'. Note: diff images may not contain transparency, therefore upon draw this mode may be changed if the resulting image has any transparent component. """ cbook._check_in_list(['full', 'diff'], mode=mode) if self._current_image_mode != mode: self._current_image_mode = mode self.handle_send_image_mode(None)
def append_size(self, position, size): if position == "left": self._horizontal.insert(0, size) self._xrefindex += 1 elif position == "right": self._horizontal.append(size) elif position == "bottom": self._vertical.insert(0, size) self._yrefindex += 1 elif position == "top": self._vertical.append(size) else: cbook._check_in_list(["left", "right", "bottom", "top"], position=position)
def __init__(self, axis, **kwargs): """ *basex*/*basey*: The base of the logarithm *nonposx*/*nonposy*: {'mask', 'clip'} non-positive values in *x* or *y* can be masked as invalid, or clipped to a very small positive number *subsx*/*subsy*: Where to place the subticks between each major tick. Should be a sequence of integers. For example, in a log10 scale: ``[2, 3, 4, 5, 6, 7, 8, 9]`` will place 8 logarithmically spaced minor ticks between each major tick. """ if axis.axis_name == 'x': base = kwargs.pop('basex', 10.0) subs = kwargs.pop('subsx', None) nonpos = kwargs.pop('nonposx', 'clip') cbook._check_in_list(['mask', 'clip'], nonposx=nonpos) else: base = kwargs.pop('basey', 10.0) subs = kwargs.pop('subsy', None) nonpos = kwargs.pop('nonposy', 'clip') cbook._check_in_list(['mask', 'clip'], nonposy=nonpos) if len(kwargs): raise ValueError(("provided too many kwargs, can only pass " "{'basex', 'subsx', nonposx'} or " "{'basey', 'subsy', nonposy'}. You passed ") + "{!r}".format(kwargs)) if base <= 0 or base == 1: raise ValueError('The log base cannot be <= 0 or == 1') if base == 10.0: self._transform = self.Log10Transform(nonpos) elif base == 2.0: self._transform = self.Log2Transform(nonpos) elif base == np.e: self._transform = self.NaturalLogTransform(nonpos) else: self._transform = self.LogTransform(base, nonpos) self.base = base self.subs = subs
def __init__(self, legend, use_blit=False, update="loc"): """ Wrapper around a `.Legend` to support mouse dragging. Parameters ---------- legend : `.Legend` The `.Legend` instance to wrap. use_blit : bool, optional Use blitting for faster image composition. For details see :ref:`func-animation`. update : {'loc', 'bbox'}, optional If "loc", update the *loc* parameter of the legend upon finalizing. If "bbox", update the *bbox_to_anchor* parameter. """ self.legend = legend cbook._check_in_list(["loc", "bbox"], update=update) self._update = update DraggableOffsetBox.__init__(self, legend, legend._legend_box, use_blit=use_blit)
def set_axis_direction(self, d): """ Adjust the text angle and text alignment of axis label according to the matplotlib convention. ===================== ========== ========= ========== ========== property left bottom right top ===================== ========== ========= ========== ========== axislabel angle 180 0 0 180 axislabel va center top center bottom axislabel ha right center right center ===================== ========== ========= ========== ========== Note that the text angles are actually relative to (90 + angle of the direction to the ticklabel), which gives 0 for bottom axis. """ cbook._check_in_list(["left", "right", "top", "bottom"], d=d) self.set_default_alignment(d) self.set_default_angle(d)
def get_cmap(name=None, lut=None): """ Get a colormap instance, defaulting to rc values if *name* is None. Colormaps added with :func:`register_cmap` take precedence over built-in colormaps. If *name* is a :class:`matplotlib.colors.Colormap` instance, it will be returned. If *lut* is not None it must be an integer giving the number of entries desired in the lookup table, and *name* must be a standard mpl colormap name. """ if name is None: name = mpl.rcParams['image.cmap'] if isinstance(name, colors.Colormap): return name cbook._check_in_list(sorted(cmap_d), name=name) if lut is None: return cmap_d[name] else: return cmap_d[name]._resample(lut)
def set_axis_direction(self, axis_direction): """ Adjust the direction, text angle, text alignment of ticklabels, labels following the matplotlib convention for the rectangle axes. The *axis_direction* must be one of [left, right, bottom, top]. ===================== ========== ========= ========== ========== property left bottom right top ===================== ========== ========= ========== ========== ticklabels location "-" "+" "+" "-" axislabel location "-" "+" "+" "-" ticklabels angle 90 0 -90 180 ticklabel va center baseline center baseline ticklabel ha right center right center axislabel angle 180 0 0 180 axislabel va center top center bottom axislabel ha right center right center ===================== ========== ========= ========== ========== Note that the direction "+" and "-" are relative to the direction of the increasing coordinate. Also, the text angles are actually relative to (90 + angle of the direction to the ticklabel), which gives 0 for bottom axis. """ cbook._check_in_list(["left", "right", "top", "bottom"], axis_direction=axis_direction) self._axis_direction = axis_direction if axis_direction in ["left", "top"]: self.set_ticklabel_direction("-") self.set_axislabel_direction("-") else: self.set_ticklabel_direction("+") self.set_axislabel_direction("+") self.major_ticklabels.set_axis_direction(axis_direction) self.label.set_axis_direction(axis_direction)
def set_axis_direction(self, label_direction): """ Adjust the text angle and text alignment of ticklabels according to the matplotlib convention. The *label_direction* must be one of [left, right, bottom, top]. ===================== ========== ========= ========== ========== property left bottom right top ===================== ========== ========= ========== ========== ticklabels angle 90 0 -90 180 ticklabel va center baseline center baseline ticklabel ha right center right center ===================== ========== ========= ========== ========== Note that the text angles are actually relative to (90 + angle of the direction to the ticklabel), which gives 0 for bottom axis. """ cbook._check_in_list(["left", "right", "top", "bottom"], label_direction=label_direction) self._axis_direction = label_direction self.set_default_alignment(label_direction) self.set_default_angle(label_direction)
def get_yaxis_transform(self, which='grid'): cbook._check_in_list(['tick1', 'tick2', 'grid'], which=which) return self._yaxis_transform
def __init__(self, fig, rect, nrows_ncols, ngrids=None, direction="row", axes_pad=0.02, add_all=True, share_all=False, share_x=True, share_y=True, label_mode="L", axes_class=None, *, aspect=False, ): """ Parameters ---------- fig : `.Figure` The parent figure. rect : (float, float, float, float) or int The axes position, as a ``(left, bottom, width, height)`` tuple or as a three-digit subplot position code (e.g., "121"). nrows_ncols : (int, int) Number of rows and columns in the grid. ngrids : int or None, default: None If not None, only the first *ngrids* axes in the grid are created. direction : {"row", "column"}, default: "row" Whether axes are created in row-major ("row by row") or column-major order ("column by column"). axes_pad : float or (float, float), default: 0.02 Padding or (horizontal padding, vertical padding) between axes, in inches. add_all : bool, default: True Whether to add the axes to the figure using `.Figure.add_axes`. This parameter is deprecated. share_all : bool, default: False Whether all axes share their x- and y-axis. Overrides *share_x* and *share_y*. share_x : bool, default: True Whether all axes of a column share their x-axis. share_y : bool, default: True Whether all axes of a row share their y-axis. label_mode : {"L", "1", "all"}, default: "L" Determines which axes will get tick labels: - "L": All axes on the left column get vertical tick labels; all axes on the bottom row get horizontal tick labels. - "1": Only the bottom left axes is labelled. - "all": all axes are labelled. axes_class : subclass of `matplotlib.axes.Axes`, default: None aspect : bool, default: False Whether the axes aspect ratio follows the aspect ratio of the data limits. """ self._nrows, self._ncols = nrows_ncols if ngrids is None: ngrids = self._nrows * self._ncols else: if not 0 < ngrids <= self._nrows * self._ncols: raise Exception("") self.ngrids = ngrids self._horiz_pad_size, self._vert_pad_size = map( Size.Fixed, np.broadcast_to(axes_pad, 2)) cbook._check_in_list(["column", "row"], direction=direction) self._direction = direction if axes_class is None: axes_class = self._defaultAxesClass kw = dict(horizontal=[], vertical=[], aspect=aspect) if isinstance(rect, (str, Number, SubplotSpec)): self._divider = SubplotDivider(fig, rect, **kw) elif len(rect) == 3: self._divider = SubplotDivider(fig, *rect, **kw) elif len(rect) == 4: self._divider = Divider(fig, rect, **kw) else: raise Exception("") rect = self._divider.get_position() axes_array = np.full((self._nrows, self._ncols), None, dtype=object) for i in range(self.ngrids): col, row = self._get_col_row(i) if share_all: sharex = sharey = axes_array[0, 0] else: sharex = axes_array[0, col] if share_x else None sharey = axes_array[row, 0] if share_y else None axes_array[row, col] = axes_class( fig, rect, sharex=sharex, sharey=sharey) self.axes_all = axes_array.ravel().tolist() self.axes_column = axes_array.T.tolist() self.axes_row = axes_array.tolist() self.axes_llc = self.axes_column[0][-1] self._init_locators() if add_all: for ax in self.axes_all: fig.add_axes(ax) self.set_label_mode(label_mode)
def __init__(self, nonpos='mask'): Transform.__init__(self) cbook._check_in_list(['mask', 'clip'], nonpos=nonpos) self._nonpos = nonpos self._clip = {"clip": True, "mask": False}[nonpos]
def get_yaxis_transform(self, which='grid'): cbook._check_in_list(['tick1', 'tick2', 'grid'], which=which) return self._yaxis_transform
def set_default_alignment(self, d): cbook._check_in_list(["left", "right", "top", "bottom"], d=d) va, ha = self._default_alignments[d] self.set_va(va) self.set_ha(ha)
def set_default_angle(self, d): cbook._check_in_list(["left", "right", "top", "bottom"], d=d) self.set_rotation(self._default_angles[d])
def __init__(self, ax, *args, scale=None, headwidth=3, headlength=5, headaxislength=4.5, minshaft=1, minlength=1, units='width', scale_units=None, angles='uv', width=None, color='k', pivot='tail', **kw): """ The constructor takes one required argument, an Axes instance, followed by the args and kwargs described by the following pyplot interface documentation: %s """ self.ax = ax X, Y, U, V, C = _parse_args(*args) self.X = X self.Y = Y self.XY = np.column_stack((X, Y)) self.N = len(X) self.scale = scale self.headwidth = headwidth self.headlength = float(headlength) self.headaxislength = headaxislength self.minshaft = minshaft self.minlength = minlength self.units = units self.scale_units = scale_units self.angles = angles self.width = width if pivot.lower() == 'mid': pivot = 'middle' self.pivot = pivot.lower() cbook._check_in_list(self._PIVOT_VALS, pivot=self.pivot) self.transform = kw.pop('transform', ax.transData) kw.setdefault('facecolors', color) kw.setdefault('linewidths', (0, )) mcollections.PolyCollection.__init__(self, [], offsets=self.XY, transOffset=self.transform, closed=False, **kw) self.polykw = kw self.set_UVC(U, V, C) self._initialized = False # try to prevent closure over the real self weak_self = weakref.ref(self) def on_dpi_change(fig): self_weakref = weak_self() if self_weakref is not None: self_weakref._new_UV = True # vertices depend on width, span # which in turn depend on dpi self_weakref._initialized = False # simple brute force update # works because _init is # called at the start of # draw. self._cid = self.ax.figure.callbacks.connect('dpi_changed', on_dpi_change)
def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, mode=None): ''' This is a helper function that implements the commonality between the psd, csd, spectrogram and complex, magnitude, angle, and phase spectrums. It is *NOT* meant to be used outside of mlab and may change at any time. ''' if y is None: # if y is None use x for y same_data = True else: # The checks for if y is x are so that we can use the same function to # implement the core of psd(), csd(), and spectrogram() without doing # extra calculations. We return the unaveraged Pxy, freqs, and t. same_data = y is x if Fs is None: Fs = 2 if noverlap is None: noverlap = 0 if detrend_func is None: detrend_func = detrend_none if window is None: window = window_hanning # if NFFT is set to None use the whole signal if NFFT is None: NFFT = 256 if mode is None or mode == 'default': mode = 'psd' cbook._check_in_list( ['default', 'psd', 'complex', 'magnitude', 'angle', 'phase'], mode=mode) if not same_data and mode != 'psd': raise ValueError("x and y must be equal if mode is not 'psd'") # Make sure we're dealing with a numpy array. If y and x were the same # object to start with, keep them that way x = np.asarray(x) if not same_data: y = np.asarray(y) if sides is None or sides == 'default': if np.iscomplexobj(x): sides = 'twosided' else: sides = 'onesided' cbook._check_in_list(['default', 'onesided', 'twosided'], sides=sides) # zero pad x and y up to NFFT if they are shorter than NFFT if len(x) < NFFT: n = len(x) x = np.resize(x, NFFT) x[n:] = 0 if not same_data and len(y) < NFFT: n = len(y) y = np.resize(y, NFFT) y[n:] = 0 if pad_to is None: pad_to = NFFT if mode != 'psd': scale_by_freq = False elif scale_by_freq is None: scale_by_freq = True # For real x, ignore the negative frequencies unless told otherwise if sides == 'twosided': numFreqs = pad_to if pad_to % 2: freqcenter = (pad_to - 1)//2 + 1 else: freqcenter = pad_to//2 scaling_factor = 1. elif sides == 'onesided': if pad_to % 2: numFreqs = (pad_to + 1)//2 else: numFreqs = pad_to//2 + 1 scaling_factor = 2. if not np.iterable(window): window = window(np.ones(NFFT, x.dtype)) if len(window) != NFFT: raise ValueError( "The window length must match the data's first dimension") result = stride_windows(x, NFFT, noverlap, axis=0) result = detrend(result, detrend_func, axis=0) result = result * window.reshape((-1, 1)) result = np.fft.fft(result, n=pad_to, axis=0)[:numFreqs, :] freqs = np.fft.fftfreq(pad_to, 1/Fs)[:numFreqs] if not same_data: # if same_data is False, mode must be 'psd' resultY = stride_windows(y, NFFT, noverlap) resultY = detrend(resultY, detrend_func, axis=0) resultY = resultY * window.reshape((-1, 1)) resultY = np.fft.fft(resultY, n=pad_to, axis=0)[:numFreqs, :] result = np.conj(result) * resultY elif mode == 'psd': result = np.conj(result) * result elif mode == 'magnitude': result = np.abs(result) / np.abs(window).sum() elif mode == 'angle' or mode == 'phase': # we unwrap the phase later to handle the onesided vs. twosided case result = np.angle(result) elif mode == 'complex': result /= np.abs(window).sum() if mode == 'psd': # Also include scaling factors for one-sided densities and dividing by # the sampling frequency, if desired. Scale everything, except the DC # component and the NFFT/2 component: # if we have a even number of frequencies, don't scale NFFT/2 if not NFFT % 2: slc = slice(1, -1, None) # if we have an odd number, just don't scale DC else: slc = slice(1, None, None) result[slc] *= scaling_factor # MATLAB divides by the sampling frequency so that density function # has units of dB/Hz and can be integrated by the plotted frequency # values. Perform the same scaling here. if scale_by_freq: result /= Fs # Scale the spectrum by the norm of the window to compensate for # windowing loss; see Bendat & Piersol Sec 11.5.2. result /= (np.abs(window)**2).sum() else: # In this case, preserve power in the segment, not amplitude result /= np.abs(window).sum()**2 t = np.arange(NFFT/2, len(x) - NFFT/2 + 1, NFFT - noverlap)/Fs if sides == 'twosided': # center the frequency range at zero freqs = np.concatenate((freqs[freqcenter:], freqs[:freqcenter])) result = np.concatenate((result[freqcenter:, :], result[:freqcenter, :]), 0) elif not pad_to % 2: # get the last value correctly, it is negative otherwise freqs[-1] *= -1 # we unwrap the phase here to handle the onesided vs. twosided case if mode == 'phase': result = np.unwrap(result, axis=0) return result, freqs, t
def __init__( self, fig, rect, nrows_ncols, ngrids=None, direction="row", axes_pad=0.02, add_all=True, share_all=False, share_x=True, share_y=True, #aspect=True, label_mode="L", axes_class=None, ): """ Parameters ---------- fig : `.Figure` The parent figure. rect : (float, float, float, float) or int The axes position, as a ``(left, bottom, width, height)`` tuple or as a three-digit subplot position code (e.g., "121"). direction : {"row", "column"}, default: "row" axes_pad : float or (float, float), default: 0.02 Padding or (horizontal padding, vertical padding) between axes, in inches. add_all : bool, default: True share_all : bool, default: False share_x : bool, default: True share_y : bool, default: True label_mode : {"L", "1", "all"}, default: "L" Determines which axes will get tick labels: - "L": All axes on the left column get vertical tick labels; all axes on the bottom row get horizontal tick labels. - "1": Only the bottom left axes is labelled. - "all": all axes are labelled. axes_class : subclass of `matplotlib.axes.Axes`, default: None """ self._nrows, self._ncols = nrows_ncols if ngrids is None: ngrids = self._nrows * self._ncols else: if not 0 < ngrids <= self._nrows * self._ncols: raise Exception("") self.ngrids = ngrids self._init_axes_pad(axes_pad) cbook._check_in_list(["column", "row"], direction=direction) self._direction = direction if axes_class is None: axes_class = self._defaultAxesClass kw = dict(horizontal=[], vertical=[], aspect=False) if isinstance(rect, (str, Number, SubplotSpec)): self._divider = SubplotDivider(fig, rect, **kw) elif len(rect) == 3: self._divider = SubplotDivider(fig, *rect, **kw) elif len(rect) == 4: self._divider = Divider(fig, rect, **kw) else: raise Exception("") rect = self._divider.get_position() axes_array = np.full((self._nrows, self._ncols), None, dtype=object) for i in range(self.ngrids): col, row = self._get_col_row(i) if share_all: sharex = sharey = axes_array[0, 0] else: sharex = axes_array[0, col] if share_x else None sharey = axes_array[row, 0] if share_y else None axes_array[row, col] = axes_class(fig, rect, sharex=sharex, sharey=sharey) self.axes_all = axes_array.ravel().tolist() self.axes_column = axes_array.T.tolist() self.axes_row = axes_array.tolist() self.axes_llc = self.axes_column[0][-1] self._update_locators() if add_all: for ax in self.axes_all: fig.add_axes(ax) self.set_label_mode(label_mode)
def stackplot(axes, x, *args, labels=(), colors=None, baseline='zero', **kwargs): """ Draw a stacked area plot. Parameters ---------- x : 1d array of dimension N y : 2d array (dimension MxN), or sequence of 1d arrays (each dimension 1xN) The data is assumed to be unstacked. Each of the following calls is legal:: stackplot(x, y) # where y is MxN stackplot(x, y1, y2, y3, y4) # where y1, y2, y3, y4, are all 1xNm baseline : {'zero', 'sym', 'wiggle', 'weighted_wiggle'} Method used to calculate the baseline: - ``'zero'``: Constant zero baseline, i.e. a simple stacked plot. - ``'sym'``: Symmetric around zero and is sometimes called 'ThemeRiver'. - ``'wiggle'``: Minimizes the sum of the squared slopes. - ``'weighted_wiggle'``: Does the same but weights to account for size of each layer. It is also called 'Streamgraph'-layout. More details can be found at http://leebyron.com/streamgraph/. labels : Length N sequence of strings Labels to assign to each data series. colors : Length N sequence of colors A list or tuple of colors. These will be cycled through and used to colour the stacked areas. **kwargs All other keyword arguments are passed to `Axes.fill_between()`. Returns ------- list : list of `.PolyCollection` A list of `.PolyCollection` instances, one for each element in the stacked area plot. """ y = np.row_stack(args) labels = iter(labels) if colors is not None: axes.set_prop_cycle(color=colors) # Assume data passed has not been 'stacked', so stack it here. # We'll need a float buffer for the upcoming calculations. stack = np.cumsum(y, axis=0, dtype=np.promote_types(y.dtype, np.float32)) cbook._check_in_list(['zero', 'sym', 'wiggle', 'weighted_wiggle'], baseline=baseline) if baseline == 'zero': first_line = 0. elif baseline == 'sym': first_line = -np.sum(y, 0) * 0.5 stack += first_line[None, :] elif baseline == 'wiggle': m = y.shape[0] first_line = (y * (m - 0.5 - np.arange(m)[:, None])).sum(0) first_line /= -m stack += first_line elif baseline == 'weighted_wiggle': total = np.sum(y, 0) # multiply by 1/total (or zero) to avoid infinities in the division: inv_total = np.zeros_like(total) mask = total > 0 inv_total[mask] = 1.0 / total[mask] increase = np.hstack((y[:, 0:1], np.diff(y))) below_size = total - stack below_size += 0.5 * y move_up = below_size * inv_total move_up[:, 0] = 0.5 center = (move_up - 0.5) * increase center = np.cumsum(center.sum(0)) first_line = center - 0.5 * total stack += first_line # Color between x = 0 and the first array. color = axes._get_lines.get_next_color() coll = axes.fill_between(x, first_line, stack[0, :], facecolor=color, label=next(labels, None), **kwargs) coll.sticky_edges.y[:] = [0] r = [coll] # Color between array i-1 and array i for i in range(len(y) - 1): color = axes._get_lines.get_next_color() r.append(axes.fill_between(x, stack[i, :], stack[i + 1, :], facecolor=color, label=next(labels, None), **kwargs)) return r
def __init__(self, artist_list, w_or_h): self._artist_list = artist_list cbook._check_in_list(["width", "height"], w_or_h=w_or_h) self._w_or_h = w_or_h
def subplots(self, *, sharex=False, sharey=False, squeeze=True, subplot_kw=None): """ Add all subplots specified by this `GridSpec` to its parent figure. This utility wrapper makes it convenient to create common layouts of subplots in a single call. Parameters ---------- sharex, sharey : bool or {'none', 'all', 'row', 'col'}, default: False Controls sharing of properties among x (*sharex*) or y (*sharey*) axes: - True or 'all': x- or y-axis will be shared among all subplots. - False or 'none': each subplot x- or y-axis will be independent. - 'row': each subplot row will share an x- or y-axis. - 'col': each subplot column will share an x- or y-axis. When subplots have a shared x-axis along a column, only the x tick labels of the bottom subplot are created. Similarly, when subplots have a shared y-axis along a row, only the y tick labels of the first column subplot are created. To later turn other subplots' ticklabels on, use `~matplotlib.axes.Axes.tick_params`. squeeze : bool, optional, default: True - If True, extra dimensions are squeezed out from the returned array of Axes: - if only one subplot is constructed (nrows=ncols=1), the resulting single Axes object is returned as a scalar. - for Nx1 or 1xM subplots, the returned object is a 1D numpy object array of Axes objects. - for NxM, subplots with N>1 and M>1 are returned as a 2D array. - If False, no squeezing at all is done: the returned Axes object is always a 2D array containing Axes instances, even if it ends up being 1x1. subplot_kw : dict, optional Dict with keywords passed to the `~.Figure.add_subplot` call used to create each subplot. Returns ------- ax : `~.axes.Axes` object or array of Axes objects. *ax* can be either a single `~matplotlib.axes.Axes` object or an array of Axes objects if more than one subplot was created. The dimensions of the resulting array can be controlled with the squeeze keyword, see above. See Also -------- .pyplot.subplots .Figure.add_subplot .pyplot.subplot """ figure = self.figure if figure is None: raise ValueError("GridSpec.subplots() only works for GridSpecs " "created with a parent figure") if isinstance(sharex, bool): sharex = "all" if sharex else "none" if isinstance(sharey, bool): sharey = "all" if sharey else "none" # This check was added because it is very easy to type # `subplots(1, 2, 1)` when `subplot(1, 2, 1)` was intended. # In most cases, no error will ever occur, but mysterious behavior # will result because what was intended to be the subplot index is # instead treated as a bool for sharex. if isinstance(sharex, Integral): cbook._warn_external( "sharex argument to subplots() was an integer. Did you " "intend to use subplot() (without 's')?") cbook._check_in_list(["all", "row", "col", "none"], sharex=sharex, sharey=sharey) if subplot_kw is None: subplot_kw = {} # don't mutate kwargs passed by user... subplot_kw = subplot_kw.copy() # Create array to hold all axes. axarr = np.empty((self._nrows, self._ncols), dtype=object) for row in range(self._nrows): for col in range(self._ncols): shared_with = { "none": None, "all": axarr[0, 0], "row": axarr[row, 0], "col": axarr[0, col] } subplot_kw["sharex"] = shared_with[sharex] subplot_kw["sharey"] = shared_with[sharey] axarr[row, col] = figure.add_subplot(self[row, col], **subplot_kw) # turn off redundant tick labeling if sharex in ["col", "all"]: # turn off all but the bottom row for ax in axarr[:-1, :].flat: ax.xaxis.set_tick_params(which='both', labelbottom=False, labeltop=False) ax.xaxis.offsetText.set_visible(False) if sharey in ["row", "all"]: # turn off all but the first column for ax in axarr[:, 1:].flat: ax.yaxis.set_tick_params(which='both', labelleft=False, labelright=False) ax.yaxis.offsetText.set_visible(False) if squeeze: # Discarding unneeded dimensions that equal 1. If we only have one # subplot, just return it instead of a 1-element array. return axarr.item() if axarr.size == 1 else axarr.squeeze() else: # Returned axis array will be always 2-d, even if nrows=ncols=1. return axarr
def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, mode=None): ''' This is a helper function that implements the commonality between the psd, csd, spectrogram and complex, magnitude, angle, and phase spectrums. It is *NOT* meant to be used outside of mlab and may change at any time. ''' if y is None: # if y is None use x for y same_data = True else: # The checks for if y is x are so that we can use the same function to # implement the core of psd(), csd(), and spectrogram() without doing # extra calculations. We return the unaveraged Pxy, freqs, and t. same_data = y is x if Fs is None: Fs = 2 if noverlap is None: noverlap = 0 if detrend_func is None: detrend_func = detrend_none if window is None: window = window_hanning # if NFFT is set to None use the whole signal if NFFT is None: NFFT = 256 if mode is None or mode == 'default': mode = 'psd' cbook._check_in_list( ['default', 'psd', 'complex', 'magnitude', 'angle', 'phase'], mode=mode) if not same_data and mode != 'psd': raise ValueError("x and y must be equal if mode is not 'psd'") # Make sure we're dealing with a numpy array. If y and x were the same # object to start with, keep them that way x = np.asarray(x) if not same_data: y = np.asarray(y) if sides is None or sides == 'default': if np.iscomplexobj(x): sides = 'twosided' else: sides = 'onesided' cbook._check_in_list(['default', 'onesided', 'twosided'], sides=sides) # zero pad x and y up to NFFT if they are shorter than NFFT if len(x) < NFFT: n = len(x) x = np.resize(x, NFFT) x[n:] = 0 if not same_data and len(y) < NFFT: n = len(y) y = np.resize(y, NFFT) y[n:] = 0 if pad_to is None: pad_to = NFFT if mode != 'psd': scale_by_freq = False elif scale_by_freq is None: scale_by_freq = True # For real x, ignore the negative frequencies unless told otherwise if sides == 'twosided': numFreqs = pad_to if pad_to % 2: freqcenter = (pad_to - 1) // 2 + 1 else: freqcenter = pad_to // 2 scaling_factor = 1. elif sides == 'onesided': if pad_to % 2: numFreqs = (pad_to + 1) // 2 else: numFreqs = pad_to // 2 + 1 scaling_factor = 2. if not np.iterable(window): window = window(np.ones(NFFT, x.dtype)) if len(window) != NFFT: raise ValueError( "The window length must match the data's first dimension") result = stride_windows(x, NFFT, noverlap, axis=0) result = detrend(result, detrend_func, axis=0) result = result * window.reshape((-1, 1)) result = np.fft.fft(result, n=pad_to, axis=0)[:numFreqs, :] freqs = np.fft.fftfreq(pad_to, 1 / Fs)[:numFreqs] if not same_data: # if same_data is False, mode must be 'psd' resultY = stride_windows(y, NFFT, noverlap) resultY = detrend(resultY, detrend_func, axis=0) resultY = resultY * window.reshape((-1, 1)) resultY = np.fft.fft(resultY, n=pad_to, axis=0)[:numFreqs, :] result = np.conj(result) * resultY elif mode == 'psd': result = np.conj(result) * result elif mode == 'magnitude': result = np.abs(result) / np.abs(window).sum() elif mode == 'angle' or mode == 'phase': # we unwrap the phase later to handle the onesided vs. twosided case result = np.angle(result) elif mode == 'complex': result /= np.abs(window).sum() if mode == 'psd': # Also include scaling factors for one-sided densities and dividing by # the sampling frequency, if desired. Scale everything, except the DC # component and the NFFT/2 component: # if we have a even number of frequencies, don't scale NFFT/2 if not NFFT % 2: slc = slice(1, -1, None) # if we have an odd number, just don't scale DC else: slc = slice(1, None, None) result[slc] *= scaling_factor # MATLAB divides by the sampling frequency so that density function # has units of dB/Hz and can be integrated by the plotted frequency # values. Perform the same scaling here. if scale_by_freq: result /= Fs # Scale the spectrum by the norm of the window to compensate for # windowing loss; see Bendat & Piersol Sec 11.5.2. result /= (np.abs(window)**2).sum() else: # In this case, preserve power in the segment, not amplitude result /= np.abs(window).sum()**2 t = np.arange(NFFT / 2, len(x) - NFFT / 2 + 1, NFFT - noverlap) / Fs if sides == 'twosided': # center the frequency range at zero freqs = np.concatenate((freqs[freqcenter:], freqs[:freqcenter])) result = np.concatenate( (result[freqcenter:, :], result[:freqcenter, :]), 0) elif not pad_to % 2: # get the last value correctly, it is negative otherwise freqs[-1] *= -1 # we unwrap the phase here to handle the onesided vs. twosided case if mode == 'phase': result = np.unwrap(result, axis=0) return result, freqs, t
def stackplot(axes, x, *args, labels=(), colors=None, baseline='zero', **kwargs): """ Draw a stacked area plot. Parameters ---------- x : 1d array of dimension N y : 2d array (dimension MxN), or sequence of 1d arrays (each dimension 1xN) The area_data is assumed to be unstacked. Each of the following calls is legal:: stackplot(x, y) # where y is MxN stackplot(x, y1, y2, y3, y4) # where y1, y2, y3, y4, are all 1xNm baseline : {'zero', 'sym', 'wiggle', 'weighted_wiggle'} Method used to calculate the baseline: - ``'zero'``: Constant zero baseline, i.e. a simple stacked plot. - ``'sym'``: Symmetric around zero and is sometimes called 'ThemeRiver'. - ``'wiggle'``: Minimizes the sum of the squared slopes. - ``'weighted_wiggle'``: Does the same but weights to account for size of each layer. It is also called 'Streamgraph'-layout. More details can be found at http://leebyron.com/streamgraph/. labels : Length N sequence of strings Labels to assign to each area_data series. colors : Length N sequence of colors A list or tuple of colors. These will be cycled through and used to colour the stacked areas. **kwargs All other keyword arguments are passed to `.Axes.fill_between`. Returns ------- list of `.PolyCollection` A list of `.PolyCollection` instances, one for each element in the stacked area plot. """ y = np.row_stack(args) labels = iter(labels) if colors is not None: axes.set_prop_cycle(color=colors) # Assume area_data passed has not been 'stacked', so stack it here. # We'll need a float buffer for the upcoming calculations. stack = np.cumsum(y, axis=0, dtype=np.promote_types(y.dtype, np.float32)) cbook._check_in_list(['zero', 'sym', 'wiggle', 'weighted_wiggle'], baseline=baseline) if baseline == 'zero': first_line = 0. elif baseline == 'sym': first_line = -np.sum(y, 0) * 0.5 stack += first_line[None, :] elif baseline == 'wiggle': m = y.shape[0] first_line = (y * (m - 0.5 - np.arange(m)[:, None])).sum(0) first_line /= -m stack += first_line elif baseline == 'weighted_wiggle': total = np.sum(y, 0) # multiply by 1/total (or zero) to avoid infinities in the division: inv_total = np.zeros_like(total) mask = total > 0 inv_total[mask] = 1.0 / total[mask] increase = np.hstack((y[:, 0:1], np.diff(y))) below_size = total - stack below_size += 0.5 * y move_up = below_size * inv_total move_up[:, 0] = 0.5 center = (move_up - 0.5) * increase center = np.cumsum(center.sum(0)) first_line = center - 0.5 * total stack += first_line # Color between x = 0 and the first array. color = axes._get_lines.get_next_color() coll = axes.fill_between(x, first_line, stack[0, :], facecolor=color, label=next(labels, None), **kwargs) coll.sticky_edges.y[:] = [0] r = [coll] # Color between array i-1 and array i for i in range(len(y) - 1): color = axes._get_lines.get_next_color() r.append( axes.fill_between(x, stack[i, :], stack[i + 1, :], facecolor=color, label=next(labels, None), **kwargs)) return r
def set_default_alignment(self, d): cbook._check_in_list(["left", "right", "top", "bottom"], d=d) va, ha = self._default_alignments[d] self.set_va(va) self.set_ha(ha)
def __init__(self, nonpositive='mask'): super().__init__() cbook._check_in_list(['mask', 'clip'], nonpositive=nonpositive) self._nonpositive = nonpositive self._clip = {"clip": True, "mask": False}[nonpositive]
def set_viewlim_mode(self, mode): cbook._check_in_list([None, "equal", "transform"], mode=mode) self._viewlim_mode = mode
def __init__( self, fig, rect, nrows_ncols, ngrids=None, direction="row", axes_pad=0.02, add_all=True, share_all=False, aspect=True, label_mode="L", cbar_mode=None, cbar_location="right", cbar_pad=None, cbar_size="5%", cbar_set_cax=True, axes_class=None, ): """ Parameters ---------- fig : `.Figure` The parent figure. rect : (float, float, float, float) or int The axes position, as a ``(left, bottom, width, height)`` tuple or as a three-digit subplot position code (e.g., "121"). direction : {"row", "column"}, default: "row" axes_pad : float or (float, float), default: 0.02 Padding or (horizontal padding, vertical padding) between axes, in inches. add_all : bool, default: True share_all : bool, default: False aspect : bool, default: True label_mode : {"L", "1", "all"}, default: "L" Determines which axes will get tick labels: - "L": All axes on the left column get vertical tick labels; all axes on the bottom row get horizontal tick labels. - "1": Only the bottom left axes is labelled. - "all": all axes are labelled. cbar_mode : {"each", "single", "edge", None }, default: None cbar_location : {"left", "right", "bottom", "top"}, default: "right" cbar_pad : float, default: None cbar_size : size specification (see `.Size.from_any`), default: "5%" cbar_set_cax : bool, default: True If True, each axes in the grid has a *cax* attribute that is bound to associated *cbar_axes*. axes_class : subclass of `matplotlib.axes.Axes`, default: None """ self._nrows, self._ncols = nrows_ncols if ngrids is None: ngrids = self._nrows * self._ncols else: if not 0 < ngrids <= self._nrows * self._ncols: raise Exception self.ngrids = ngrids self._init_axes_pad(axes_pad) self._colorbar_mode = cbar_mode self._colorbar_location = cbar_location if cbar_pad is None: # horizontal or vertical arrangement? if cbar_location in ("left", "right"): self._colorbar_pad = self._horiz_pad_size.fixed_size else: self._colorbar_pad = self._vert_pad_size.fixed_size else: self._colorbar_pad = cbar_pad self._colorbar_size = cbar_size cbook._check_in_list(["column", "row"], direction=direction) self._direction = direction if axes_class is None: axes_class = self._defaultAxesClass kw = dict(horizontal=[], vertical=[], aspect=aspect) if isinstance(rect, (str, Number, SubplotSpec)): self._divider = SubplotDivider(fig, rect, **kw) elif len(rect) == 3: self._divider = SubplotDivider(fig, *rect, **kw) elif len(rect) == 4: self._divider = Divider(fig, rect, **kw) else: raise Exception("") rect = self._divider.get_position() axes_array = np.full((self._nrows, self._ncols), None, dtype=object) for i in range(self.ngrids): col, row = self._get_col_row(i) if share_all: sharex = sharey = axes_array[0, 0] else: sharex = axes_array[0, col] sharey = axes_array[row, 0] axes_array[row, col] = axes_class(fig, rect, sharex=sharex, sharey=sharey) self.axes_all = axes_array.ravel().tolist() self.axes_column = axes_array.T.tolist() self.axes_row = axes_array.tolist() self.axes_llc = self.axes_column[0][-1] self.cbar_axes = [ self._defaultCbarAxesClass(fig, rect, orientation=self._colorbar_location) for _ in range(self.ngrids) ] self._update_locators() if add_all: for ax in self.axes_all + self.cbar_axes: fig.add_axes(ax) if cbar_set_cax: if self._colorbar_mode == "single": for ax in self.axes_all: ax.cax = self.cbar_axes[0] elif self._colorbar_mode == "edge": for index, ax in enumerate(self.axes_all): col, row = self._get_col_row(index) if self._colorbar_location in ("left", "right"): ax.cax = self.cbar_axes[row] else: ax.cax = self.cbar_axes[col] else: for ax, cax in zip(self.axes_all, self.cbar_axes): ax.cax = cax self.set_label_mode(label_mode)
def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=None, start_points=None, maxlength=4.0, integration_direction='both'): """ Draw streamlines of a vector flow. Parameters ---------- x, y : 1D arrays An evenly spaced grid. u, v : 2D arrays *x* and *y*-velocities. The number of rows and columns must match the length of *y* and *x*, respectively. density : float or (float, float) Controls the closeness of streamlines. When ``density = 1``, the domain is divided into a 30x30 grid. *density* linearly scales this grid. Each cell in the grid can have, at most, one traversing streamline. For different densities in each direction, use a tuple (density_x, density_y). linewidth : float or 2D array The width of the stream lines. With a 2D array the line width can be varied across the grid. The array must have the same shape as *u* and *v*. color : color or 2D array The streamline color. If given an array, its values are converted to colors using *cmap* and *norm*. The array must have the same shape as *u* and *v*. cmap : `~matplotlib.colors.Colormap` Colormap used to plot streamlines and arrows. This is only used if *color* is an array. norm : `~matplotlib.colors.Normalize` Normalize object used to scale luminance data to 0, 1. If ``None``, stretch (min, max) to (0, 1). This is only used if *color* is an array. arrowsize : float Scaling factor for the arrow size. arrowstyle : str Arrow style specification. See `~matplotlib.patches.FancyArrowPatch`. minlength : float Minimum length of streamline in axes coordinates. start_points : Nx2 array Coordinates of starting points for the streamlines in data coordinates (the same coordinates as the *x* and *y* arrays). zorder : int The zorder of the stream lines and arrows. Artists with lower zorder values are drawn first. maxlength : float Maximum length of streamline in axes coordinates. integration_direction : {'forward', 'backward', 'both'}, default: 'both' Integrate the streamline in forward, backward or both directions. Returns ------- stream_container : StreamplotSet Container object with attributes - ``lines``: `.LineCollection` of streamlines - ``arrows``: `.PatchCollection` containing `.FancyArrowPatch` objects representing the arrows half-way along stream lines. This container will probably change in the future to allow changes to the colormap, alpha, etc. for both lines and arrows, but these changes should be backward compatible. """ grid = Grid(x, y) mask = StreamMask(density) dmap = DomainMap(grid, mask) if zorder is None: zorder = mlines.Line2D.zorder # default to data coordinates if transform is None: transform = axes.transData if color is None: color = axes._get_lines.get_next_color() if linewidth is None: linewidth = matplotlib.rcParams['lines.linewidth'] line_kw = {} arrow_kw = dict(arrowstyle=arrowstyle, mutation_scale=10 * arrowsize) cbook._check_in_list(['both', 'forward', 'backward'], integration_direction=integration_direction) if integration_direction == 'both': maxlength /= 2. use_multicolor_lines = isinstance(color, np.ndarray) if use_multicolor_lines: if color.shape != grid.shape: raise ValueError("If 'color' is given, it must match the shape of " "'Grid(x, y)'") line_colors = [] color = np.ma.masked_invalid(color) else: line_kw['color'] = color arrow_kw['color'] = color if isinstance(linewidth, np.ndarray): if linewidth.shape != grid.shape: raise ValueError("If 'linewidth' is given, it must match the " "shape of 'Grid(x, y)'") line_kw['linewidth'] = [] else: line_kw['linewidth'] = linewidth arrow_kw['linewidth'] = linewidth line_kw['zorder'] = zorder arrow_kw['zorder'] = zorder # Sanity checks. if u.shape != grid.shape or v.shape != grid.shape: raise ValueError("'u' and 'v' must match the shape of 'Grid(x, y)'") u = np.ma.masked_invalid(u) v = np.ma.masked_invalid(v) integrate = get_integrator(u, v, dmap, minlength, maxlength, integration_direction) trajectories = [] if start_points is None: for xm, ym in _gen_starting_points(mask.shape): if mask[ym, xm] == 0: xg, yg = dmap.mask2grid(xm, ym) t = integrate(xg, yg) if t is not None: trajectories.append(t) else: sp2 = np.asanyarray(start_points, dtype=float).copy() # Check if start_points are outside the data boundaries for xs, ys in sp2: if not (grid.x_origin <= xs <= grid.x_origin + grid.width and grid.y_origin <= ys <= grid.y_origin + grid.height): raise ValueError("Starting point ({}, {}) outside of data " "boundaries".format(xs, ys)) # Convert start_points from data to array coords # Shift the seed points from the bottom left of the data so that # data2grid works properly. sp2[:, 0] -= grid.x_origin sp2[:, 1] -= grid.y_origin for xs, ys in sp2: xg, yg = dmap.data2grid(xs, ys) t = integrate(xg, yg) if t is not None: trajectories.append(t) if use_multicolor_lines: if norm is None: norm = mcolors.Normalize(color.min(), color.max()) if cmap is None: cmap = cm.get_cmap(matplotlib.rcParams['image.cmap']) else: cmap = cm.get_cmap(cmap) streamlines = [] arrows = [] for t in trajectories: tgx = np.array(t[0]) tgy = np.array(t[1]) # Rescale from grid-coordinates to data-coordinates. tx, ty = dmap.grid2data(*np.array(t)) tx += grid.x_origin ty += grid.y_origin points = np.transpose([tx, ty]).reshape(-1, 1, 2) streamlines.extend(np.hstack([points[:-1], points[1:]])) # Add arrows half way along each trajectory. s = np.cumsum(np.hypot(np.diff(tx), np.diff(ty))) n = np.searchsorted(s, s[-1] / 2.) arrow_tail = (tx[n], ty[n]) arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2])) if isinstance(linewidth, np.ndarray): line_widths = interpgrid(linewidth, tgx, tgy)[:-1] line_kw['linewidth'].extend(line_widths) arrow_kw['linewidth'] = line_widths[n] if use_multicolor_lines: color_values = interpgrid(color, tgx, tgy)[:-1] line_colors.append(color_values) arrow_kw['color'] = cmap(norm(color_values[n])) p = patches.FancyArrowPatch( arrow_tail, arrow_head, transform=transform, **arrow_kw) axes.add_patch(p) arrows.append(p) lc = mcollections.LineCollection( streamlines, transform=transform, **line_kw) lc.sticky_edges.x[:] = [grid.x_origin, grid.x_origin + grid.width] lc.sticky_edges.y[:] = [grid.y_origin, grid.y_origin + grid.height] if use_multicolor_lines: lc.set_array(np.ma.hstack(line_colors)) lc.set_cmap(cmap) lc.set_norm(norm) axes.add_collection(lc) axes.autoscale_view() ac = matplotlib.collections.PatchCollection(arrows) stream_container = StreamplotSet(lc, ac) return stream_container
def set_default_angle(self, d): cbook._check_in_list(["left", "right", "top", "bottom"], d=d) self.set_rotation(self._default_angles[d])
def set_viewlim_mode(self, mode): cbook._check_in_list([None, "equal", "transform"], mode=mode) self._viewlim_mode = mode
def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=None, start_points=None, maxlength=4.0, integration_direction='both'): """ Draw streamlines of a vector flow. Parameters ---------- x, y : 1D arrays An evenly spaced grid. u, v : 2D arrays *x* and *y*-velocities. The number of rows and columns must match the length of *y* and *x*, respectively. density : float or (float, float) Controls the closeness of streamlines. When ``density = 1``, the domain is divided into a 30x30 grid. *density* linearly scales this grid. Each cell in the grid can have, at most, one traversing streamline. For different densities in each direction, use a tuple (density_x, density_y). linewidth : float or 2D array The width of the stream lines. With a 2D array the line width can be varied across the grid. The array must have the same shape as *u* and *v*. color : matplotlib color code, or 2D array The streamline color. If given an array, its values are converted to colors using *cmap* and *norm*. The array must have the same shape as *u* and *v*. cmap : `~matplotlib.colors.Colormap` Colormap used to plot streamlines and arrows. This is only used if *color* is an array. norm : `~matplotlib.colors.Normalize` Normalize object used to scale luminance data to 0, 1. If ``None``, stretch (min, max) to (0, 1). This is only used if *color* is an array. arrowsize : float Scaling factor for the arrow size. arrowstyle : str Arrow style specification. See `~matplotlib.patches.FancyArrowPatch`. minlength : float Minimum length of streamline in axes coordinates. start_points : Nx2 array Coordinates of starting points for the streamlines in data coordinates (the same coordinates as the *x* and *y* arrays). zorder : int The zorder of the stream lines and arrows. Artists with lower zorder values are drawn first. maxlength : float Maximum length of streamline in axes coordinates. integration_direction : {'forward', 'backward', 'both'} Integrate the streamline in forward, backward or both directions. default is ``'both'``. Returns ------- stream_container : StreamplotSet Container object with attributes - ``lines``: `.LineCollection` of streamlines - ``arrows``: `.PatchCollection` containing `.FancyArrowPatch` objects representing the arrows half-way along stream lines. This container will probably change in the future to allow changes to the colormap, alpha, etc. for both lines and arrows, but these changes should be backward compatible. """ grid = Grid(x, y) mask = StreamMask(density) dmap = DomainMap(grid, mask) if zorder is None: zorder = mlines.Line2D.zorder # default to data coordinates if transform is None: transform = axes.transData if color is None: color = axes._get_lines.get_next_color() if linewidth is None: linewidth = matplotlib.rcParams['lines.linewidth'] line_kw = {} arrow_kw = dict(arrowstyle=arrowstyle, mutation_scale=10 * arrowsize) cbook._check_in_list(['both', 'forward', 'backward'], integration_direction=integration_direction) if integration_direction == 'both': maxlength /= 2. use_multicolor_lines = isinstance(color, np.ndarray) if use_multicolor_lines: if color.shape != grid.shape: raise ValueError( "If 'color' is given, must have the shape of 'Grid(x,y)'") line_colors = [] color = np.ma.masked_invalid(color) else: line_kw['color'] = color arrow_kw['color'] = color if isinstance(linewidth, np.ndarray): if linewidth.shape != grid.shape: raise ValueError( "If 'linewidth' is given, must have the shape of 'Grid(x,y)'") line_kw['linewidth'] = [] else: line_kw['linewidth'] = linewidth arrow_kw['linewidth'] = linewidth line_kw['zorder'] = zorder arrow_kw['zorder'] = zorder ## Sanity checks. if u.shape != grid.shape or v.shape != grid.shape: raise ValueError("'u' and 'v' must be of shape 'Grid(x,y)'") u = np.ma.masked_invalid(u) v = np.ma.masked_invalid(v) integrate = get_integrator(u, v, dmap, minlength, maxlength, integration_direction) trajectories = [] if start_points is None: for xm, ym in _gen_starting_points(mask.shape): if mask[ym, xm] == 0: xg, yg = dmap.mask2grid(xm, ym) t = integrate(xg, yg) if t is not None: trajectories.append(t) else: sp2 = np.asanyarray(start_points, dtype=float).copy() # Check if start_points are outside the data boundaries for xs, ys in sp2: if not (grid.x_origin <= xs <= grid.x_origin + grid.width and grid.y_origin <= ys <= grid.y_origin + grid.height): raise ValueError("Starting point ({}, {}) outside of data " "boundaries".format(xs, ys)) # Convert start_points from data to array coords # Shift the seed points from the bottom left of the data so that # data2grid works properly. sp2[:, 0] -= grid.x_origin sp2[:, 1] -= grid.y_origin for xs, ys in sp2: xg, yg = dmap.data2grid(xs, ys) t = integrate(xg, yg) if t is not None: trajectories.append(t) if use_multicolor_lines: if norm is None: norm = mcolors.Normalize(color.min(), color.max()) if cmap is None: cmap = cm.get_cmap(matplotlib.rcParams['image.cmap']) else: cmap = cm.get_cmap(cmap) streamlines = [] arrows = [] for t in trajectories: tgx = np.array(t[0]) tgy = np.array(t[1]) # Rescale from grid-coordinates to data-coordinates. tx, ty = dmap.grid2data(*np.array(t)) tx += grid.x_origin ty += grid.y_origin points = np.transpose([tx, ty]).reshape(-1, 1, 2) streamlines.extend(np.hstack([points[:-1], points[1:]])) # Add arrows half way along each trajectory. s = np.cumsum(np.hypot(np.diff(tx), np.diff(ty))) n = np.searchsorted(s, s[-1] / 2.) arrow_tail = (tx[n], ty[n]) arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2])) if isinstance(linewidth, np.ndarray): line_widths = interpgrid(linewidth, tgx, tgy)[:-1] line_kw['linewidth'].extend(line_widths) arrow_kw['linewidth'] = line_widths[n] if use_multicolor_lines: color_values = interpgrid(color, tgx, tgy)[:-1] line_colors.append(color_values) arrow_kw['color'] = cmap(norm(color_values[n])) p = patches.FancyArrowPatch( arrow_tail, arrow_head, transform=transform, **arrow_kw) axes.add_patch(p) arrows.append(p) lc = mcollections.LineCollection( streamlines, transform=transform, **line_kw) lc.sticky_edges.x[:] = [grid.x_origin, grid.x_origin + grid.width] lc.sticky_edges.y[:] = [grid.y_origin, grid.y_origin + grid.height] if use_multicolor_lines: lc.set_array(np.ma.hstack(line_colors)) lc.set_cmap(cmap) lc.set_norm(norm) axes.add_collection(lc) axes.autoscale_view() ac = matplotlib.collections.PatchCollection(arrows) stream_container = StreamplotSet(lc, ac) return stream_container
def __init__(self, ax, direction): cbook._check_in_list(self._get_func_map, direction=direction) self._ax_list = [ax] if isinstance(ax, Axes) else ax self._direction = direction
def __init__(self, artist_list, w_or_h): self._artist_list = artist_list cbook._check_in_list(["width", "height"], w_or_h=w_or_h) self._w_or_h = w_or_h
def tripcolor(ax, *args, alpha=1.0, norm=None, cmap=None, vmin=None, vmax=None, shading='flat', facecolors=None, **kwargs): """ Create a pseudocolor plot of an unstructured triangular grid. The triangulation can be specified in one of two ways; either:: tripcolor(triangulation, ...) where triangulation is a :class:`matplotlib.tri.Triangulation` object, or :: tripcolor(x, y, ...) tripcolor(x, y, triangles, ...) tripcolor(x, y, triangles=triangles, ...) tripcolor(x, y, mask=mask, ...) tripcolor(x, y, triangles, mask=mask, ...) in which case a Triangulation object will be created. See :class:`~matplotlib.tri.Triangulation` for a explanation of these possibilities. The next argument must be *C*, the array of color values, either one per point in the triangulation if color values are defined at points, or one per triangle in the triangulation if color values are defined at triangles. If there are the same number of points and triangles in the triangulation it is assumed that color values are defined at points; to force the use of color values at triangles use the kwarg ``facecolors=C`` instead of just ``C``. *shading* may be 'flat' (the default) or 'gouraud'. If *shading* is 'flat' and C values are defined at points, the color values used for each triangle are from the mean C of the triangle's three points. If *shading* is 'gouraud' then color values must be defined at points. The remaining kwargs are the same as for :meth:`~matplotlib.axes.Axes.pcolor`. """ cbook._check_in_list(['flat', 'gouraud'], shading=shading) tri, args, kwargs = Triangulation.get_from_args_and_kwargs(*args, **kwargs) # C is the colors array defined at either points or faces (i.e. triangles). # If facecolors is None, C are defined at points. # If facecolors is not None, C are defined at faces. if facecolors is not None: C = facecolors else: C = np.asarray(args[0]) # If there are a different number of points and triangles in the # triangulation, can omit facecolors kwarg as it is obvious from # length of C whether it refers to points or faces. # Do not do this for gouraud shading. if (facecolors is None and len(C) == len(tri.triangles) and len(C) != len(tri.x) and shading != 'gouraud'): facecolors = C # Check length of C is OK. if ((facecolors is None and len(C) != len(tri.x)) or (facecolors is not None and len(C) != len(tri.triangles))): raise ValueError('Length of color values array must be the same ' 'as either the number of triangulation points ' 'or triangles') # Handling of linewidths, shading, edgecolors and antialiased as # in Axes.pcolor linewidths = (0.25,) if 'linewidth' in kwargs: kwargs['linewidths'] = kwargs.pop('linewidth') kwargs.setdefault('linewidths', linewidths) edgecolors = 'none' if 'edgecolor' in kwargs: kwargs['edgecolors'] = kwargs.pop('edgecolor') ec = kwargs.setdefault('edgecolors', edgecolors) if 'antialiased' in kwargs: kwargs['antialiaseds'] = kwargs.pop('antialiased') if 'antialiaseds' not in kwargs and ec.lower() == "none": kwargs['antialiaseds'] = False if shading == 'gouraud': if facecolors is not None: raise ValueError('Gouraud shading does not support the use ' 'of facecolors kwarg') if len(C) != len(tri.x): raise ValueError('For gouraud shading, the length of color ' 'values array must be the same as the ' 'number of triangulation points') collection = TriMesh(tri, **kwargs) else: # Vertices of triangles. maskedTris = tri.get_masked_triangles() verts = np.stack((tri.x[maskedTris], tri.y[maskedTris]), axis=-1) # Color values. if facecolors is None: # One color per triangle, the mean of the 3 vertex color values. C = C[maskedTris].mean(axis=1) elif tri.mask is not None: # Remove color values of masked triangles. C = C[~tri.mask] collection = PolyCollection(verts, **kwargs) collection.set_alpha(alpha) collection.set_array(C) if norm is not None and not isinstance(norm, Normalize): raise ValueError("'norm' must be an instance of 'Normalize'") 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_None() ax.grid(False) minx = tri.x.min() maxx = tri.x.max() miny = tri.y.min() maxy = tri.y.max() corners = (minx, miny), (maxx, maxy) ax.update_datalim(corners) ax.autoscale_view() ax.add_collection(collection) return collection
def __init__(self, ax, direction): cbook._check_in_list(self._get_func_map, direction=direction) self._ax_list = [ax] if isinstance(ax, Axes) else ax self._direction = direction
def get_spine_transform(self): """Return the spine transform.""" self._ensure_position_is_set() position = self._position if isinstance(position, str): if position == 'center': position = ('axes', 0.5) elif position == 'zero': position = ('data', 0) assert len(position) == 2, 'position should be 2-tuple' position_type, amount = position cbook._check_in_list(['axes', 'outward', 'data'], position_type=position_type) if self.spine_type in ['left', 'right']: base_transform = self.axes.get_yaxis_transform(which='grid') elif self.spine_type in ['top', 'bottom']: base_transform = self.axes.get_xaxis_transform(which='grid') else: raise ValueError(f'unknown spine spine_type: {self.spine_type!r}') if position_type == 'outward': if amount == 0: # short circuit commonest case return base_transform else: offset_vec = { 'left': (-1, 0), 'right': (1, 0), 'bottom': (0, -1), 'top': (0, 1), }[self.spine_type] # calculate x and y offset in dots offset_dots = amount * np.array(offset_vec) / 72 return (base_transform + mtransforms.ScaledTranslation( *offset_dots, self.figure.dpi_scale_trans)) elif position_type == 'axes': if self.spine_type in ['left', 'right']: # keep y unchanged, fix x at amount return ( mtransforms.Affine2D.from_values(0, 0, 0, 1, amount, 0) + base_transform) elif self.spine_type in ['bottom', 'top']: # keep x unchanged, fix y at amount return ( mtransforms.Affine2D.from_values(1, 0, 0, 0, 0, amount) + base_transform) elif position_type == 'data': if self.spine_type in ('right', 'top'): # The right and top spines have a default position of 1 in # axes coordinates. When specifying the position in data # coordinates, we need to calculate the position relative to 0. amount -= 1 if self.spine_type in ('left', 'right'): return mtransforms.blended_transform_factory( mtransforms.Affine2D().translate(amount, 0) + self.axes.transData, self.axes.transData) elif self.spine_type in ('bottom', 'top'): return mtransforms.blended_transform_factory( self.axes.transData, mtransforms.Affine2D().translate(0, amount) + self.axes.transData)