def __init__(self, artist_list, w_or_h): self._artist_list = artist_list _api.check_in_list(["width", "height"], w_or_h=w_or_h) self._w_or_h = w_or_h
def __init__( self, fig, rect, nrows_ncols, ngrids=None, direction="row", axes_pad=0.02, *, 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"). This also affects the order in which axes are accessed using indexing (``grid[index]``). axes_pad : float or (float, float), default: 0.02 Padding or (horizontal padding, vertical padding) between axes, in inches. 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)) _api.check_in_list(["column", "row"], direction=direction) self._direction = direction if axes_class is None: axes_class = self._defaultAxesClass elif isinstance(axes_class, (list, tuple)): cls, kwargs = axes_class axes_class = functools.partial(cls, **kwargs) 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( order="C" if self._direction == "row" else "F").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() for ax in self.axes_all: fig.add_axes(ax) self.set_label_mode(label_mode)
def get_yaxis_transform(self, which='grid'): _api.check_in_list(['tick1', 'tick2', 'grid'], which=which) return self._yaxis_transform
def stackplot(axes, x, *args, labels=(), colors=None, baseline='zero', **kwargs): """ Draw a stacked area plot. Parameters ---------- x : (N,) array-like y : (M, N) array-like The data is assumed to be unstacked. Each of the following calls is legal:: stackplot(x, y) # where y has shape (M, N) stackplot(x, y1, y2, y3) # where y1, y2, y3, y4 have length N 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 : list of str, optional A sequence of labels to assign to each data series. If unspecified, then no labels will be applied to artists. colors : list of color, optional A sequence of colors to be cycled through and used to color the stacked areas. The sequence need not be exactly the same length as the number of provided *y*, in which case the colors will repeat from the beginning. If not specified, the colors from the Axes property cycle will be used. **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 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)) _api.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 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', broken_streamlines=True): """ Draw streamlines of a vector flow. Parameters ---------- x, y : 1D/2D arrays Evenly spaced strictly increasing arrays to make a grid. If 2D, all rows of *x* must be equal and all columns of *y* must be equal; i.e., they must be as if generated by ``np.meshgrid(x_1d, y_1d)``. 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. data : indexable object, optional DATA_PARAMETER_PLACEHOLDER broken_streamlines : boolean, default: True If False, forces streamlines to continue until they leave the plot domain. If True, they may be terminated if they come too close to another streamline. Returns ------- 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) _api.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 " "the (x, y) grid") line_colors = [[]] # Empty entry allows concatenation of zero arrays. 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 the (x, y) grid") 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 the (x, y) grid") 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, broken_streamlines) 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) # Floating point issues can cause xg, yg to be slightly out of # bounds for xs, ys on the upper boundaries. Because we have # already checked that the starting points are within the original # grid, clip the xg, yg to the grid to work around this issue xg = np.clip(xg, 0, grid.nx - 1) yg = np.clip(yg, 0, grid.ny - 1) t = integrate(xg, yg, broken_streamlines) if t is not None: trajectories.append(t) if use_multicolor_lines: if norm is None: norm = mcolors.Normalize(color.min(), color.max()) cmap = cm.get_cmap(cmap) streamlines = [] arrows = [] for t in trajectories: tgx, tgy = t.T # Rescale from grid-coordinates to data-coordinates. tx, ty = dmap.grid2data(tgx, tgy) 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) 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) ac = matplotlib.collections.PatchCollection(arrows) # Adding the collection itself is broken; see #2341. for p in arrows: axes.add_patch(p) axes.autoscale_view() stream_container = StreamplotSet(lc, ac) return stream_container
def __init__(self, nonpositive='mask'): super().__init__() _api.check_in_list(['mask', 'clip'], nonpositive=nonpositive) self._nonpositive = nonpositive self._clip = {"clip": True, "mask": False}[nonpositive]
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._axes = ax # The attr actually set by the Artist.axes property. X, Y, U, V, C = _parse_args(*args, caller_name='quiver()') 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() _api.check_in_list(self._PIVOT_VALS, pivot=self.pivot) self.transform = kw.pop('transform', ax.transData) kw.setdefault('facecolors', color) kw.setdefault('linewidths', (0, )) super().__init__([], offsets=self.XY, transOffset=self.transform, closed=False, **kw) self.polykw = kw self.set_UVC(U, V, C) self._initialized = False weak_self = weakref.ref(self) # Prevent closure over the real self. def on_dpi_change(fig): self_weakref = weak_self() if self_weakref is not None: # vertices depend on width, span which in turn depend on dpi self_weakref._new_UV = True # simple brute force update works because _init is called at # the start of draw. self_weakref._initialized = False self._cid = ax.figure.callbacks.connect('dpi_changed', on_dpi_change)
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 _api.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)
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): """ Private helper implementing the common parts between the psd, csd, spectrogram and complex, magnitude, angle, and phase spectrums. """ 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' _api.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' _api.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.roll(freqs, -freqcenter, axis=0) result = np.roll(result, -freqcenter, axis=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 set_viewlim_mode(self, mode): _api.check_in_list([None, "equal", "transform"], mode=mode) self._viewlim_mode = mode
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. Call signatures:: tripcolor(triangulation, C, *, ...) tripcolor(x, y, C, *, [triangles=triangles], [mask=mask], ...) The triangular grid can be specified either by passing a `.Triangulation` object as the first parameter, or by passing the points *x*, *y* and optionally the *triangles* and a *mask*. See `.Triangulation` for an explanation of these parameters. If neither of *triangulation* or *triangles* are given, the triangulation is calculated on the fly. In this case, it does not make sense to provide colors at the triangle faces via *C* or *facecolors* because there are multiple possible triangulations for a group of points and you don't know which triangles will be constructed. Parameters ---------- triangulation : `.Triangulation` An already created triangular grid. x, y, triangles, mask Parameters defining the triangular grid. See `.Triangulation`. This is mutually exclusive with specifying *triangulation*. C : array-like The color values, either for the points or for the triangles. Which one is automatically inferred from the length of *C*, i.e. does it match the number of points or the number of 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 keyword argument ``facecolors=C`` instead of just ``C``. This parameter is position-only. facecolors : array-like, optional Can be used alternatively to *C* to specify colors at the triangle faces. This parameter takes precedence over *C*. shading : {'flat', 'gouraud'}, default: 'flat' If 'flat' and the color values *C* 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. other_parameters All other parameters are the same as for `~.Axes.pcolor`. Notes ----- It is possible to pass the triangles positionally, i.e. ``tripcolor(x, y, triangles, C, ...)``. However, this is discouraged. For more clarity, pass *triangles* via keyword argument. """ _api.check_in_list(['flat', 'gouraud'], shading=shading) tri, args, kwargs = Triangulation.get_from_args_and_kwargs(*args, **kwargs) # Parse the color to be in one of (the other variable will be None): # - facecolors: if specified at the triangle faces # - point_colors: if specified at the points if facecolors is not None: if args: _api.warn_external( "Positional parameter C has no effect when the keyword " "facecolors is given") point_colors = None if len(facecolors) != len(tri.triangles): raise ValueError("The length of facecolors must match the number " "of triangles") else: # Color from positional parameter C if not args: raise TypeError( "tripcolor() missing 1 required positional argument: 'C'; or " "1 required keyword-only argument: 'facecolors'") elif len(args) > 1: _api.warn_deprecated( "3.6", message=f"Additional positional parameters " f"{args[1:]!r} are ignored; support for them is deprecated " f"since %(since)s and will be removed %(removal)s") C = np.asarray(args[0]) if len(C) == len(tri.x): # having this before the len(tri.triangles) comparison gives # precedence to nodes if there are as many nodes as triangles point_colors = C facecolors = None elif len(C) == len(tri.triangles): point_colors = None facecolors = C else: raise ValueError('The length of C must match either the number ' 'of points or the number of 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 _api.check_isinstance((Normalize, None), norm=norm) if shading == 'gouraud': if facecolors is not None: raise ValueError( "shading='gouraud' can only be used when the colors " "are specified at the points, not at the faces.") collection = TriMesh(tri, alpha=alpha, array=point_colors, cmap=cmap, norm=norm, **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. colors = point_colors[maskedTris].mean(axis=1) elif tri.mask is not None: # Remove color values of masked triangles. colors = facecolors[~tri.mask] else: colors = facecolors collection = PolyCollection(verts, alpha=alpha, array=colors, cmap=cmap, norm=norm, **kwargs) collection._scale_norm(norm, vmin, vmax) 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 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`. When subplots have a shared axis that has units, calling `~matplotlib.axis.Axis.set_units` will update each axis with the new units. 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')?") _api.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 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 `.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 `.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 `~.Axes.pcolor`. """ _api.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) cbook._check_isinstance((Normalize, None), norm=norm) collection.set_cmap(cmap) collection.set_norm(norm) collection._scale_norm(norm, vmin, vmax) 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 subplots(self, *, sharex=False, sharey=False, squeeze=True, subplot_kw=None): """ Add all subplots specified by this `GridSpec` to its parent figure. See `.Figure.subplots` for detailed documentation. """ 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. This check should go away # once sharex becomes kwonly. if isinstance(sharex, Integral): _api.warn_external( "sharex argument to subplots() was an integer. Did you " "intend to use subplot() (without 's')?") _api.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