def add_collection3d(self, col, zs=0, zdir='z'): ''' Add a 3d collection object to the plot. 2D collection types are converted to a 3D version by modifying the object and adding z coordinate information. Supported are: - PolyCollection - LineColleciton - PatchCollection ''' zvals = np.atleast_1d(zs) if len(zvals) > 0 : zsortval = min(zvals) else : zsortval = 0 # FIXME: Fairly arbitrary. Is there a better value? if type(col) is collections.PolyCollection: art3d.poly_collection_2d_to_3d(col, zs=zs, zdir=zdir) col.set_sort_zpos(zsortval) elif type(col) is collections.LineCollection: art3d.line_collection_2d_to_3d(col, zs=zs, zdir=zdir) col.set_sort_zpos(zsortval) elif type(col) is collections.PatchCollection: art3d.patch_collection_2d_to_3d(col, zs=zs, zdir=zdir) col.set_sort_zpos(zsortval) Axes.add_collection(self, col)
def plot_thresholded_envelope(self, ax_main: Axes, ax_dist: Axes): # e_t, with segment bands, and summary stats to the right :) logger.info("Plotting thresholded envelope..") self.plot_signal(self.e_t, ax=ax_main, color=envelope_color, lw=thicc_lw) rm = self.reference_maker ax_main.hlines(rm.ripple_threshold_high, *self.time_range, lw=thin_lw) ax_main.hlines(rm.ripple_threshold_low, *self.time_range, lw=thin_lw) add_scalebar(ax_main) add_title(ax_main, "Thresholds $T$", threshold_color, y=0.58) segs = self.reference_segs_test visible_segs = segs.intersection(ax_main.get_xlim()) bars = BrokenBarHCollection( xranges=[ tup for tup in zip(visible_segs.start, visible_segs.duration) ], yrange=(0, rm.ripple_threshold_low), facecolors=segment_color, alpha=segment_alpha, ) ax_main.add_collection(bars) # Find and plot crossings of lower threshold crossings_ix = nonzero(diff(self.e_t > rm.ripple_threshold_low))[0] crossings_t = crossings_ix / self.fs crossings_y = [rm.ripple_threshold_low] * len(crossings_t) ax_main.plot(crossings_t, crossings_y, ".", c="black") logger.info("Done") logger.info("Plotting envelope density..") self.plot_envelope_dist(ax_dist) logger.info("Done") ax_dist.set_ylim(ax_main.get_ylim())
def add_poly_meshplot(ax: Axes, points: np.ndarray, triangles: List[Tuple[int]], values: np.ndarray, vmax: Optional[float] = None, vmin: Optional[float] = None, cmap: str = 'coolwarm', rasterized: bool = True): """ Add meshes with faces colored by the values :param Axes ax: The axis to add a meshplot to :param ndarray points: The n x 2 array of points :param list[tuple[int]]: The set of all perimeter indicies for this mesh :param ndarray values: The values to color the perimeters with """ if vmin is None: if isinstance(values, dict): vmin = np.percentile(list(values.values()), 10) else: vmin = np.percentile(list(values), 10) if vmax is None: if isinstance(values, dict): vmax = np.percentile(list(values.values()), 90) else: vmax = np.percentile(list(values), 90) cmap = mplcm.get_cmap(cmap) norm = mplcm.colors.Normalize(vmax=vmax, vmin=vmin) scores = [] patches = [] points = np.array(points) max_idx = points.shape[0] for indices in triangles: tri = [] score = [] for i in indices: if i < 0 or i > max_idx: continue tri.append(points[i, :]) score.append(values[i]) if len(tri) < 3: continue mean = np.nanmean(score) if np.isnan(mean): continue scores.append(norm(mean)) patch = Polygon(tri, closed=True, edgecolor='none') patches.append(patch) colors = cmap(scores) collection = PatchCollection(patches) ax.add_collection(collection) collection.set_color(colors) return ax
def geometric_realization(self, dim: int, ax: Axes) -> None: """ Draw simplices of one dimension. Args: dim (int): simplex dimension ax (matplotlib.axes.Axes): axes to draw on Returns: None """ if dim > self.simplex_dim: raise ValueError( f"Dimensionality is too high: max dim for this complex is {self.simplex_dim} but got {dim} as dim" ) ax.set_xlim(self.vertices[:, 0].min() - 1, self.vertices[:, 0].max() + 1) ax.set_ylim(self.vertices[:, 1].min() - 1, self.vertices[:, 1].max() + 1) if dim == 0: # points ax.scatter(self.vertices[:, 0], self.vertices[:, 1]) return coords = [ self._simplex_to_point_set(simplex) for simplex in self.simplices[dim] ] if dim == 1: # edges ax.add_collection(LineCollection(coords)) else: # volumes for polygon in coords: ax.fill(polygon[:, 0], polygon[:, 1], color=(0, 0, 1, 0.1))
def _render_poly_array(self, ax: Axes, poly_array: np.array, mpl_kw: dict): """Render the poly array. Args: ax (Axes): The axis poly_array (np.array): The poly mpl_kw (dict): The parameters dictionary """ if len(poly_array) > 0: poly_array = to_poly_patch(poly_array) ax.add_collection(PatchCollection(poly_array, **mpl_kw))
def render_path(self, table: pd.DataFrame, ax: Axes, subtracted: bool = False, extra_kw: dict = None): """Render a table of path geometry. Args: table (DataFrame): Element table ax (matplotlib.axes.Axes): Axis to render on kw (dict): Style params """ if len(table) < 1: return # mask for all non zero width paths # TODO: could there be a problem with float vs int here? mask = (table.width == 0) | table.width.isna() # print(f'subtracted={subtracted}\n\n') # display(table) # display(imask) # convert to polys - handle non zero width table1 = table[~mask] mask2 = (table1.fillet == 0) table2 = table1[~mask2] for index, row in table2[table2.fillet.notnull()].iterrows(): table1.loc[index, 'geometry'] = self.fillet_path(row) if len(table1) > 0: table1.geometry = table1[['geometry', 'width']].apply(lambda x: x[ 0].buffer(distance=float(x[1]) / 2., cap_style=CAP_STYLE.flat, join_style=JOIN_STYLE.mitre, resolution=int(self.options['resolution'])), axis=1) kw = self.get_style('poly', subtracted=subtracted, extra=extra_kw) # render components self.render_poly(table1, ax, subtracted=subtracted, extra_kw=kw) # handle zero width table1 = table[mask] # best way to plot? # TODO: speed and vectorize? if len(table1) > 0: kw = self.get_style('path', subtracted=subtracted, extra=extra_kw) line_segments = LineCollection(table1.geometry) ax.add_collection(line_segments)
def _draw_edges(self, graph: Graph, positions: Dict[float], ax: Axes, theme: MindMapTheme) -> None: edge_collection = [] for a, b in graph.edges: a, b = positions[a], positions[b] if not theme.rectangular: edge_collection.append((a, b)) else: c = (a[0], b[1]) edge_collection.append((a, c)) edge_collection.append((c, b)) ax.add_collection( LineCollection(edge_collection, linewidths=theme.edgewidth, colors=(theme.edgecolor, )))
def add_wedge_patches_to_axis(W : Dict[int, List[KrSector]], ax : Axes, cmap : Colormap, alpha : float, rmax : float, scale : float, cr : Sequence[float], clims : Tuple[float, float])->PatchCollection: for sector, krws in W.items(): wedges = [wedge_from_sector_(krw, rmax=rmax, scale=scale) for krw in krws] colors = set_map_sequential_colors(wedges, sector, cr) p = PatchCollection(wedges, cmap=cmap, alpha=alpha) p.set_array(np.array(colors)) ax.add_collection(p) p.set_clim(clims) return p
def add_meshplot(ax: Axes, points: np.ndarray, mesh: Dict[int, List[int]], linewidth: float = 1, markersize: float = 2, color: str = 'r', rasterized: bool = True): """ Add a plot of the mesh :param Axes ax: The axis to add a meshplot to :param ndarray points: The n x 2 array of points :param dict[int, list] mesh: The dictionary mapping point indices to their neighbors in the mesh """ points = np.array(points) meshlines = [] # Merge all the links into a line collection for i, point in enumerate(points): if i not in mesh: continue connected_points = mesh[i] for j in connected_points: meshlines.append( np.array([ [points[i, 0], points[i, 1]], [points[j, 0], points[j, 1]], ])) if rasterized: ax.set_rasterization_zorder(2.1) collection = LineCollection(meshlines, colors=color, linewidths=linewidth) collection.set_zorder(2) ax.add_collection(collection) ax.plot(points[:, 0], points[:, 1], linestyle='', marker='o', color=color, markersize=markersize, zorder=0) return ax
def add_map_values_to_axis_(W : Dict[int, List[KrSector]], M : Dict[int, List[float]], ax : Axes, cmap : Colormap, alpha : float, rmax : float, scale : float, clims : Tuple[float, float])->PatchCollection: for sector, krws in W.items(): wedges = [wedge_from_sector_(krw, rmax=rmax, scale=scale) for krw in krws] colors = [M[sector][i] for i in range(len(wedges)) ] #print(colors) p = PatchCollection(wedges, cmap=cmap, alpha=alpha) p.set_array(np.array(colors)) ax.add_collection(p) p.set_clim(clims) return p
def add_collection3d(self, col, zs=0, zdir='z'): ''' Add a 3d collection object to the plot. 2D collection types are converted to a 3D version by modifying the object and adding z coordinate information. Supported are: - PolyCollection - LineColleciton - PatchCollection ''' if type(col) is collections.PolyCollection: art3d.poly_collection_2d_to_3d(col, zs=zs, zdir=zdir) col.set_sort_zpos(min(zs)) elif type(col) is collections.LineCollection: art3d.line_collection_2d_to_3d(col, zs=zs, zdir=zdir) col.set_sort_zpos(min(zs)) elif type(col) is collections.PatchCollection: art3d.patch_collection_2d_to_3d(col, zs=zs, zdir=zdir) col.set_sort_zpos(min(zs)) Axes.add_collection(self, col)
def _plot_spots(x: np.ndarray, y: np.ndarray, c: Union[str, np.ndarray], s: float, ax: Axes, vmin: float = None, vmax: float = None, **kwargs) -> PatchCollection: """This function is simplified from https://github.com/theislab/scanpy/blob/0dfd353abd968f3ecaafd5fac77a50a7e0dd87ee/scanpy/plotting/_utils.py#L1063-L1117, which originates from: https://gist.github.com/syrte/592a062c562cd2a98a83. The original code at gist is under `The BSD 3-Clause License <http://opensource.org/licenses/BSD-3-Clause>`_. x, y: coordinates; c: either a string representing one color or an array of real values for cmap; s: spot radius; vmin, vmax: colormap lower/upper limits; kwargs: all other parameters. """ spots = PatchCollection( [Circle((x_, y_), s_) for x_, y_, s_ in np.broadcast(x, y, s)], **kwargs) if isinstance(c, str): spots.set_facecolor(c) else: spots.set_array(c) spots.set_clim(vmin, vmax) ax.add_collection(spots) return spots
def render_path(self, table: pd.DataFrame, ax: Axes, subtracted: bool = False, extra_kw: dict = None): """Render a table of path geometry. Args: table (DataFrame): Element table ax (matplotlib.axes.Axes): Axis to render on kw (dict): Style params """ if len(table) < 1: return # mask for all non zero width paths # TODO: could there be a problem with float vs int here? mask = (table.width == 0) | table.width.isna() # print(f'subtracted={subtracted}\n\n') # display(table) # display(imask) # convert to polys - handle non zero width table1 = table[~mask] # if any are fillet, alter the path separately table1.loc[table1.fillet.notnull(), 'geometry'] = table1[table1.fillet.notnull()].apply( self.fillet_path, axis=1) if len(table1) > 0: table1.geometry = table1[['geometry', 'width']].apply(lambda x: x[ 0].buffer(distance=float(x[1]) / 2., cap_style=CAP_STYLE.flat, join_style=JOIN_STYLE.mitre, resolution=int(self.options['resolution'])), axis=1) kw = self.get_style('poly', subtracted=subtracted, extra=extra_kw) # render components self.render_poly(table1, ax, subtracted=subtracted, extra_kw=kw) # handle zero width table1 = table[mask] # best way to plot? # TODO: speed and vectorize? if len(table1) > 0: kw = self.get_style('path', subtracted=subtracted, extra=extra_kw) line_segments = LineCollection(table1.geometry) ax.add_collection(line_segments) # DEFAULT['renderer_mpl'] = Dict( # annot_conectors=Dict( # ofst=[0.025, 0.025], # annotate_kw=dict( # called by ax.annotate # color='r', # arrowprops=dict(color='r', shrink=0.1, width=0.05, headwidth=0.1) # ), # line_kw=dict(lw=2, c='r') # ), # ) # class QRendererMPL(QRendererGui): # """ # Renderer for matplotlib in a GUI environment. # TODO: How do we handle component selection, etc. # """ # name = 'mpl' # element_extensions = dict() # def render_shapely(self, obj, kw=None): # # TODO: simplify, specialize, and update this function # # right now, this is just calling the V0.1 old style # render(obj, ax=self.ax, kw= {} or kw) # def render_connectors(self): # ''' # Plots all connectors on the active axes. Draws the 1D line that # represents the "port" of a connector point. These are referenced for smart placement # of Metal components, such as when using functions like Metal_CPW_Connect. # TODO: add some filter for sense of what components are visible? # or on what chip the connectors are # ''' # for name, conn in self.design.connectors.items(): # line = LineString(conn.points) # self.render_shapely(line, kw=DEFAULT.annot_conectors.line_kw) # self.ax.annotate(name, xy=conn.middle[:2], xytext=conn.middle + # np.array(DEFAULT.annot_conectors.ofst), # **DEFAULT.annot_conectors.annotate_kw)
def add_gradient_line(ax: Axes, x: np.ndarray, y: np.ndarray, v: np.ndarray, cmap: str = 'gist_rainbow', linewidth: float = 2, vmin: Optional[float] = None, vmax: Optional[float] = None): """ Add a set of lines with a colormap based on the coordinates :param Axes ax: The axis to add a line to :param ndarray x: The n point x coordinates :param ndarray y: The n point y coordinates :param ndarray v: The n point color matrix to plot :param str cmap: The matplotlib colormap to use :param int linewidth: The line width to plot :param float vmin: The minimum value for the color map :param float vmax: The maximum value for the color map """ coords = np.stack([x, y], axis=1) v = np.squeeze(v) assert coords.ndim == 2 assert coords.shape[1] == 2 assert coords.shape[0] > 1 if v.ndim != 1: raise ValueError(f'Expected 1D colors but got shape {v.shape}') if v.shape[0] != coords.shape[0]: raise ValueError( f'Got coords with shape {coords.shape} but colors with shape {v.shape}' ) if vmin is None: vmin = np.min(v) if vmax is None: vmax = np.max(v) # Convert from vertex-centric to edge-centric coords = coords[:, np.newaxis, :] stack_coords = np.stack([coords[:-1, 0, :], coords[1:, 0, :]], axis=1) assert stack_coords.shape == (coords.shape[0] - 1, 2, 2) # Convert each segment to a color on the gradient cm = plt.get_cmap(cmap) cnorm = mplcolors.Normalize(vmin=vmin, vmax=vmax) scalar_map = mplcm.ScalarMappable(norm=cnorm, cmap=cm) index = (v[:-1] + v[1:]) / 2 coll = LineCollection(stack_coords, colors=scalar_map.to_rgba(index), linewidth=linewidth) ax.add_collection(coll) return ax
def plot(self, ax: axes.Axes) -> None: volumes = self._quotes.get("volume") if volumes is None: return mn, mx = ax.get_ylim() h = np.amax(self._quotes.loc[:, "high"]) l = np.amin(self._quotes.loc[:, "low"]) lh = np.amax(self._quotes.iloc[-30:].loc[:, "high"]) ll = np.amin(self._quotes.iloc[-30:].loc[:, "low"]) pos_top = False if abs(l - ll) > abs(h - lh): pos_top = False else: pos_top = True # max_height = (mx - mn) * self._chart_height_ratio nx, ny = ax.transData.inverted().transform_point( (0, int(1080 * self._chart_height_ratio)) ) ny = min(max(ny, mn), mx) max_height = ny - mn # volumes_max = volumes.max() volumes_max = volumes.quantile(self._quantile_clamp_ratio) volumes = volumes.clip(upper=volumes_max) volumes = ((volumes / volumes_max)) * max_height # volumes = ((volumes / volumes_max)) * (max_height - mn) # volumes += mn # interests = self._quotes.get("open interest") # if interests is not None: # interests = ((interests / interests.max())) * max_height length = len(self._quotes) bodies = np.ndarray(shape=length, dtype=object) xs = np.arange(length) for index, df in enumerate(self._quotes.itertuples()): p_open = df.open p_close = df.close color = self._color_unchanged if p_close > p_open: color = self._color_up elif p_close < p_open: color = self._color_down if not pos_top: body = patches.Rectangle( xy=(index - (self._body_width / 2.0), mn), width=self._body_width, # height=volumes.iloc[index] - mn, height=volumes.iloc[index], facecolor=color, edgecolor=color, alpha=self._alpha, ) if self._plot_average: ax.plot( xs, (mn + volumes).rolling(self._average_n).mean(), color=self._color_unchanged, alpha=self._line_alpha, linewidth=self._line_width, ) # ax.plot( # xs, # (mn + interests), # color="r", # alpha=self._alpha, # linewidth=self._line_width, # ) else: body = patches.Rectangle( xy=(index - (self._body_width / 2.0), mx), width=self._body_width, # height=volumes.iloc[index] - mn, height=-volumes.iloc[index], facecolor=color, edgecolor=color, alpha=self._alpha, ) if self._plot_average: ax.plot( xs, (mx - volumes).rolling(self._average_n).mean(), color=self._color_unchanged, alpha=self._line_alpha, linewidth=self._line_width, ) # ax.plot( # xs, # (mx - interests), # color="r", # alpha=self._alpha, # linewidth=self._line_width, # ) bodies[index] = body ax.add_collection( PatchCollection( bodies, match_original=True, zorder=3, ), )
def draw_graph_edges(figure: figure.Figure, axis: axes.Axes, graph: nx.Graph, attribute_name: Optional[str] = None, colorbar: bool = True): """draws graph edges from node to node, colored by weight Parameters ---------- figure: matplotlib.figure.Figure a matplotlib Figure axis: matplotlib.axes.Axes a matplotlib Axes, part of Figure graph: nx.Graph a networkx graph, assumed to have edges formed like graph.add_edge((0, 1), (0, 2), weight=1.234) attibute_name: str which edge attribute to plot. If None, will try to find the name Notes ----- modifes figure and axis in-place """ if attribute_name is None: names = find_graph_edge_attribute_names(graph) if len(names) != 1: raise ValueError("'attribute_name' was not specified, but when " "searching for one and only one name, found " f"{len(names)}: {names}. Specify " "'attribute_name'") attribute_name = names[0] # graph is (row, col), transpose to get (x, y) edges = nx.get_edge_attributes(graph, name=attribute_name) segments = [np.array([edge[0][::-1], edge[1][::-1]]) for edge in edges] weights = np.array(list(edges.values())) line_coll = LineCollection(segments, linestyle='solid', cmap="plasma", linewidths=0.3) line_coll.set_array(weights) vals = np.array(graph.nodes) mnvals = vals.min(axis=0) mxvals = vals.max(axis=0) ppvals = vals.ptp(axis=0) buffx = 0.02 * ppvals[1] buffy = 0.02 * ppvals[0] line_coll.set_linewidth(0.3 * 512 / ppvals[0]) axis.add_collection(line_coll) axis.set_xlim(mnvals[1] - buffx, mxvals[1] + buffx) axis.set_ylim(mnvals[0] - buffy, mxvals[0] + buffy) # invert yaxis for image-like orientation axis.invert_yaxis() axis.set_aspect("equal") if colorbar: divider = make_axes_locatable(axis) cax = divider.append_axes("right", size="5%", pad=0.05) figure.colorbar(line_coll, ax=axis, cax=cax) axis.set_title(attribute_name)
def plot(self, ax: axes.Axes) -> None: length = len(self._quotes) bodies = np.ndarray(shape=length, dtype=object) shadows = np.ndarray(shape=length, dtype=object) for index, df in enumerate(self._quotes.itertuples()): p_open = df.open p_high = df.high p_low = df.low p_close = df.close p_shadow_top = p_high p_shadow_bottom = p_low p_body_top: float p_body_bottom: float if p_open > p_close: p_body_top = p_open p_body_bottom = p_close else: p_body_top = p_close p_body_bottom = p_open assert p_body_top is not None assert p_body_bottom is not None if abs(p_open - p_close) < self._minimum_height: mid = (p_open + p_close) / 2.0 mid_height = self._minimum_height / 2.0 p_body_top = mid + mid_height p_body_bottom = mid - mid_height if abs(p_shadow_top - p_shadow_bottom) < self._minimum_height: mid = (p_shadow_top + p_shadow_bottom) / 2.0 mid_height = self._minimum_height / 2.0 p_shadow_top = mid + mid_height p_shadow_bottom = mid - mid_height color = self._color_unchanged if p_close > p_open: color = self._color_up elif p_close < p_open: color = self._color_down shadow = patches.Rectangle( xy=(index - (self._shadow_width / 2.0), p_shadow_bottom), width=self._shadow_width, height=p_shadow_top - p_shadow_bottom, facecolor=color, edgecolor=color, ) body = patches.Rectangle( xy=(index - (self._body_width / 2.0), p_body_bottom), width=self._body_width, height=p_body_top - p_body_bottom, facecolor=color, edgecolor=color, ) bodies[index] = body shadows[index] = shadow ax.add_collection( PatchCollection(bodies, match_original=True, zorder=self._zorder)) ax.add_collection( PatchCollection(shadows, match_original=True, zorder=self._zorder))