""" ********** Matplotlib ********** Draw networks with matplotlib. Examples -------- >>> G = nx.complete_graph(5) >>> nx.draw(G) See Also -------- - :doc:`matplotlib ` - :func:`matplotlib.pyplot.scatter` - :obj:`matplotlib.patches.FancyArrowPatch` """ from numbers import Number import networkx as nx from networkx.drawing.layout import ( circular_layout, kamada_kawai_layout, planar_layout, random_layout, shell_layout, spectral_layout, spring_layout, ) __all__ = [ "draw", "draw_networkx", "draw_networkx_nodes", "draw_networkx_edges", "draw_networkx_labels", "draw_networkx_edge_labels", "draw_circular", "draw_kamada_kawai", "draw_random", "draw_spectral", "draw_spring", "draw_planar", "draw_shell", ] def draw(G, pos=None, ax=None, **kwds): """Draw the graph G with Matplotlib. Draw the graph as a simple representation with no node labels or edge labels and using the full Matplotlib figure area and no axis labels by default. See draw_networkx() for more full-featured drawing that allows title, axis labels etc. Parameters ---------- G : graph A networkx graph pos : dictionary, optional A dictionary with nodes as keys and positions as values. If not specified a spring layout positioning will be computed. See :py:mod:`networkx.drawing.layout` for functions that compute node positions. ax : Matplotlib Axes object, optional Draw the graph in specified Matplotlib axes. kwds : optional keywords See networkx.draw_networkx() for a description of optional keywords. Examples -------- >>> G = nx.dodecahedral_graph() >>> nx.draw(G) >>> nx.draw(G, pos=nx.spring_layout(G)) # use spring layout See Also -------- draw_networkx draw_networkx_nodes draw_networkx_edges draw_networkx_labels draw_networkx_edge_labels Notes ----- This function has the same name as pylab.draw and pyplot.draw so beware when using `from networkx import *` since you might overwrite the pylab.draw function. With pyplot use >>> import matplotlib.pyplot as plt >>> G = nx.dodecahedral_graph() >>> nx.draw(G) # networkx draw() >>> plt.draw() # pyplot draw() Also see the NetworkX drawing examples at https://networkx.org/documentation/latest/auto_examples/index.html """ import matplotlib.pyplot as plt if ax is None: cf = plt.gcf() else: cf = ax.get_figure() cf.set_facecolor("w") if ax is None: if cf.axes: ax = cf.gca() else: ax = cf.add_axes((0, 0, 1, 1)) if "with_labels" not in kwds: kwds["with_labels"] = "labels" in kwds draw_networkx(G, pos=pos, ax=ax, **kwds) ax.set_axis_off() plt.draw_if_interactive() return def draw_networkx(G, pos=None, arrows=None, with_labels=True, **kwds): r"""Draw the graph G using Matplotlib. Draw the graph with Matplotlib with options for node positions, labeling, titles, and many other drawing features. See draw() for simple drawing without labels or axes. Parameters ---------- G : graph A networkx graph pos : dictionary, optional A dictionary with nodes as keys and positions as values. If not specified a spring layout positioning will be computed. See :py:mod:`networkx.drawing.layout` for functions that compute node positions. arrows : bool or None, optional (default=None) If `None`, directed graphs draw arrowheads with `~matplotlib.patches.FancyArrowPatch`, while undirected graphs draw edges via `~matplotlib.collections.LineCollection` for speed. If `True`, draw arrowheads with FancyArrowPatches (bendable and stylish). If `False`, draw edges using LineCollection (linear and fast). For directed graphs, if True draw arrowheads. Note: Arrows will be the same color as edges. arrowstyle : str (default='-\|>' for directed graphs) For directed graphs, choose the style of the arrowsheads. For undirected graphs default to '-' See `matplotlib.patches.ArrowStyle` for more options. arrowsize : int or list (default=10) For directed graphs, choose the size of the arrow head's length and width. A list of values can be passed in to assign a different size for arrow head's length and width. See `matplotlib.patches.FancyArrowPatch` for attribute `mutation_scale` for more info. with_labels : bool (default=True) Set to True to draw labels on the nodes. ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. nodelist : list (default=list(G)) Draw only specified nodes edgelist : list (default=list(G.edges())) Draw only specified edges node_size : scalar or array (default=300) Size of nodes. If an array is specified it must be the same length as nodelist. node_color : color or array of colors (default='#1f78b4') Node color. Can be a single color or a sequence of colors with the same length as nodelist. Color can be string or rgb (or rgba) tuple of floats from 0-1. If numeric values are specified they will be mapped to colors using the cmap and vmin,vmax parameters. See matplotlib.scatter for more details. node_shape : string (default='o') The shape of the node. Specification is as matplotlib.scatter marker, one of 'so^>v>> G = nx.dodecahedral_graph() >>> nx.draw(G) >>> nx.draw(G, pos=nx.spring_layout(G)) # use spring layout >>> import matplotlib.pyplot as plt >>> limits = plt.axis("off") # turn off axis Also see the NetworkX drawing examples at https://networkx.org/documentation/latest/auto_examples/index.html See Also -------- draw draw_networkx_nodes draw_networkx_edges draw_networkx_labels draw_networkx_edge_labels """ from inspect import signature import matplotlib.pyplot as plt # Get all valid keywords by inspecting the signatures of draw_networkx_nodes, # draw_networkx_edges, draw_networkx_labels valid_node_kwds = signature(draw_networkx_nodes).parameters.keys() valid_edge_kwds = signature(draw_networkx_edges).parameters.keys() valid_label_kwds = signature(draw_networkx_labels).parameters.keys() # Create a set with all valid keywords across the three functions and # remove the arguments of this function (draw_networkx) valid_kwds = (valid_node_kwds | valid_edge_kwds | valid_label_kwds) - { "G", "pos", "arrows", "with_labels", } if any(k not in valid_kwds for k in kwds): invalid_args = ", ".join([k for k in kwds if k not in valid_kwds]) raise ValueError(f"Received invalid argument(s): {invalid_args}") node_kwds = {k: v for k, v in kwds.items() if k in valid_node_kwds} edge_kwds = {k: v for k, v in kwds.items() if k in valid_edge_kwds} label_kwds = {k: v for k, v in kwds.items() if k in valid_label_kwds} if pos is None: pos = nx.drawing.spring_layout(G) # default to spring layout draw_networkx_nodes(G, pos, **node_kwds) draw_networkx_edges(G, pos, arrows=arrows, **edge_kwds) if with_labels: draw_networkx_labels(G, pos, **label_kwds) plt.draw_if_interactive() def draw_networkx_nodes( G, pos, nodelist=None, node_size=300, node_color="#1f78b4", node_shape="o", alpha=None, cmap=None, vmin=None, vmax=None, ax=None, linewidths=None, edgecolors=None, label=None, margins=None, ): """Draw the nodes of the graph G. This draws only the nodes of the graph G. Parameters ---------- G : graph A networkx graph pos : dictionary A dictionary with nodes as keys and positions as values. Positions should be sequences of length 2. ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. nodelist : list (default list(G)) Draw only specified nodes node_size : scalar or array (default=300) Size of nodes. If an array it must be the same length as nodelist. node_color : color or array of colors (default='#1f78b4') Node color. Can be a single color or a sequence of colors with the same length as nodelist. Color can be string or rgb (or rgba) tuple of floats from 0-1. If numeric values are specified they will be mapped to colors using the cmap and vmin,vmax parameters. See matplotlib.scatter for more details. node_shape : string (default='o') The shape of the node. Specification is as matplotlib.scatter marker, one of 'so^>v>> G = nx.dodecahedral_graph() >>> nodes = nx.draw_networkx_nodes(G, pos=nx.spring_layout(G)) Also see the NetworkX drawing examples at https://networkx.org/documentation/latest/auto_examples/index.html See Also -------- draw draw_networkx draw_networkx_edges draw_networkx_labels draw_networkx_edge_labels """ from collections.abc import Iterable import matplotlib as mpl import matplotlib.collections # call as mpl.collections import matplotlib.pyplot as plt import numpy as np if ax is None: ax = plt.gca() if nodelist is None: nodelist = list(G) if len(nodelist) == 0: # empty nodelist, no drawing return mpl.collections.PathCollection(None) try: xy = np.asarray([pos[v] for v in nodelist]) except KeyError as err: raise nx.NetworkXError(f"Node {err} has no position.") from err if isinstance(alpha, Iterable): node_color = apply_alpha(node_color, alpha, nodelist, cmap, vmin, vmax) alpha = None node_collection = ax.scatter( xy[:, 0], xy[:, 1], s=node_size, c=node_color, marker=node_shape, cmap=cmap, vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, edgecolors=edgecolors, label=label, ) ax.tick_params( axis="both", which="both", bottom=False, left=False, labelbottom=False, labelleft=False, ) if margins is not None: if isinstance(margins, Iterable): ax.margins(*margins) else: ax.margins(margins) node_collection.set_zorder(2) return node_collection def draw_networkx_edges( G, pos, edgelist=None, width=1.0, edge_color="k", style="solid", alpha=None, arrowstyle=None, arrowsize=10, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=None, label=None, node_size=300, nodelist=None, node_shape="o", connectionstyle="arc3", min_source_margin=0, min_target_margin=0, ): r"""Draw the edges of the graph G. This draws only the edges of the graph G. Parameters ---------- G : graph A networkx graph pos : dictionary A dictionary with nodes as keys and positions as values. Positions should be sequences of length 2. edgelist : collection of edge tuples (default=G.edges()) Draw only specified edges width : float or array of floats (default=1.0) Line width of edges edge_color : color or array of colors (default='k') Edge color. Can be a single color or a sequence of colors with the same length as edgelist. Color can be string or rgb (or rgba) tuple of floats from 0-1. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters. style : string or array of strings (default='solid') Edge line style e.g.: '-', '--', '-.', ':' or words like 'solid' or 'dashed'. Can be a single style or a sequence of styles with the same length as the edge list. If less styles than edges are given the styles will cycle. If more styles than edges are given the styles will be used sequentially and not be exhausted. Also, `(offset, onoffseq)` tuples can be used as style instead of a strings. (See `matplotlib.patches.FancyArrowPatch`: `linestyle`) alpha : float or array of floats (default=None) The edge transparency. This can be a single alpha value, in which case it will be applied to all specified edges. Otherwise, if it is an array, the elements of alpha will be applied to the colors in order (cycling through alpha multiple times if necessary). edge_cmap : Matplotlib colormap, optional Colormap for mapping intensities of edges edge_vmin,edge_vmax : floats, optional Minimum and maximum for edge colormap scaling ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. arrows : bool or None, optional (default=None) If `None`, directed graphs draw arrowheads with `~matplotlib.patches.FancyArrowPatch`, while undirected graphs draw edges via `~matplotlib.collections.LineCollection` for speed. If `True`, draw arrowheads with FancyArrowPatches (bendable and stylish). If `False`, draw edges using LineCollection (linear and fast). Note: Arrowheads will be the same color as edges. arrowstyle : str (default='-\|>' for directed graphs) For directed graphs and `arrows==True` defaults to '-\|>', For undirected graphs default to '-'. See `matplotlib.patches.ArrowStyle` for more options. arrowsize : int (default=10) For directed graphs, choose the size of the arrow head's length and width. See `matplotlib.patches.FancyArrowPatch` for attribute `mutation_scale` for more info. connectionstyle : string (default="arc3") Pass the connectionstyle parameter to create curved arc of rounding radius rad. For example, connectionstyle='arc3,rad=0.2'. See `matplotlib.patches.ConnectionStyle` and `matplotlib.patches.FancyArrowPatch` for more info. node_size : scalar or array (default=300) Size of nodes. Though the nodes are not drawn with this function, the node size is used in determining edge positioning. nodelist : list, optional (default=G.nodes()) This provides the node order for the `node_size` array (if it is an array). node_shape : string (default='o') The marker used for nodes, used in determining edge positioning. Specification is as a `matplotlib.markers` marker, e.g. one of 'so^>v>> G = nx.dodecahedral_graph() >>> edges = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> G = nx.DiGraph() >>> G.add_edges_from([(1, 2), (1, 3), (2, 3)]) >>> arcs = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> alphas = [0.3, 0.4, 0.5] >>> for i, arc in enumerate(arcs): # change alpha values of arcs ... arc.set_alpha(alphas[i]) The FancyArrowPatches corresponding to self-loops are not always returned, but can always be accessed via the ``patches`` attribute of the `matplotlib.Axes` object. >>> import matplotlib.pyplot as plt >>> fig, ax = plt.subplots() >>> G = nx.Graph([(0, 1), (0, 0)]) # Self-loop at node 0 >>> edge_collection = nx.draw_networkx_edges(G, pos=nx.circular_layout(G), ax=ax) >>> self_loop_fap = ax.patches[0] Also see the NetworkX drawing examples at https://networkx.org/documentation/latest/auto_examples/index.html See Also -------- draw draw_networkx draw_networkx_nodes draw_networkx_labels draw_networkx_edge_labels """ import matplotlib as mpl import matplotlib.collections # call as mpl.collections import matplotlib.colors # call as mpl.colors import matplotlib.patches # call as mpl.patches import matplotlib.path # call as mpl.path import matplotlib.pyplot as plt import numpy as np # The default behavior is to use LineCollection to draw edges for # undirected graphs (for performance reasons) and use FancyArrowPatches # for directed graphs. # The `arrows` keyword can be used to override the default behavior use_linecollection = not G.is_directed() if arrows in (True, False): use_linecollection = not arrows # Some kwargs only apply to FancyArrowPatches. Warn users when they use # non-default values for these kwargs when LineCollection is being used # instead of silently ignoring the specified option if use_linecollection and any( [ arrowstyle is not None, arrowsize != 10, connectionstyle != "arc3", min_source_margin != 0, min_target_margin != 0, ] ): import warnings msg = ( "\n\nThe {0} keyword argument is not applicable when drawing edges\n" "with LineCollection.\n\n" "To make this warning go away, either specify `arrows=True` to\n" "force FancyArrowPatches or use the default value for {0}.\n" "Note that using FancyArrowPatches may be slow for large graphs.\n" ) if arrowstyle is not None: msg = msg.format("arrowstyle") if arrowsize != 10: msg = msg.format("arrowsize") if connectionstyle != "arc3": msg = msg.format("connectionstyle") if min_source_margin != 0: msg = msg.format("min_source_margin") if min_target_margin != 0: msg = msg.format("min_target_margin") warnings.warn(msg, category=UserWarning, stacklevel=2) if arrowstyle == None: if G.is_directed(): arrowstyle = "-|>" else: arrowstyle = "-" if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if len(edgelist) == 0: # no edges! return [] if nodelist is None: nodelist = list(G.nodes()) # FancyArrowPatch handles color=None different from LineCollection if edge_color is None: edge_color = "k" edgelist_tuple = list(map(tuple, edgelist)) # set edge positions edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) # Check if edge_color is an array of floats and map to edge_cmap. # This is the only case handled differently from matplotlib if ( np.iterable(edge_color) and (len(edge_color) == len(edge_pos)) and np.all([isinstance(c, Number) for c in edge_color]) ): if edge_cmap is not None: assert isinstance(edge_cmap, mpl.colors.Colormap) else: edge_cmap = plt.get_cmap() if edge_vmin is None: edge_vmin = min(edge_color) if edge_vmax is None: edge_vmax = max(edge_color) color_normal = mpl.colors.Normalize(vmin=edge_vmin, vmax=edge_vmax) edge_color = [edge_cmap(color_normal(e)) for e in edge_color] def _draw_networkx_edges_line_collection(): edge_collection = mpl.collections.LineCollection( edge_pos, colors=edge_color, linewidths=width, antialiaseds=(1,), linestyle=style, alpha=alpha, ) edge_collection.set_cmap(edge_cmap) edge_collection.set_clim(edge_vmin, edge_vmax) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) return edge_collection def _draw_networkx_edges_fancy_arrow_patch(): # Note: Waiting for someone to implement arrow to intersection with # marker. Meanwhile, this works well for polygons with more than 4 # sides and circle. def to_marker_edge(marker_size, marker): if marker in "s^>v