nx_pylab.py 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552
  1. """
  2. **********
  3. Matplotlib
  4. **********
  5. Draw networks with matplotlib.
  6. Examples
  7. --------
  8. >>> G = nx.complete_graph(5)
  9. >>> nx.draw(G)
  10. See Also
  11. --------
  12. - :doc:`matplotlib <matplotlib:index>`
  13. - :func:`matplotlib.pyplot.scatter`
  14. - :obj:`matplotlib.patches.FancyArrowPatch`
  15. """
  16. from numbers import Number
  17. import networkx as nx
  18. from networkx.drawing.layout import (
  19. circular_layout,
  20. kamada_kawai_layout,
  21. planar_layout,
  22. random_layout,
  23. shell_layout,
  24. spectral_layout,
  25. spring_layout,
  26. )
  27. __all__ = [
  28. "draw",
  29. "draw_networkx",
  30. "draw_networkx_nodes",
  31. "draw_networkx_edges",
  32. "draw_networkx_labels",
  33. "draw_networkx_edge_labels",
  34. "draw_circular",
  35. "draw_kamada_kawai",
  36. "draw_random",
  37. "draw_spectral",
  38. "draw_spring",
  39. "draw_planar",
  40. "draw_shell",
  41. ]
  42. def draw(G, pos=None, ax=None, **kwds):
  43. """Draw the graph G with Matplotlib.
  44. Draw the graph as a simple representation with no node
  45. labels or edge labels and using the full Matplotlib figure area
  46. and no axis labels by default. See draw_networkx() for more
  47. full-featured drawing that allows title, axis labels etc.
  48. Parameters
  49. ----------
  50. G : graph
  51. A networkx graph
  52. pos : dictionary, optional
  53. A dictionary with nodes as keys and positions as values.
  54. If not specified a spring layout positioning will be computed.
  55. See :py:mod:`networkx.drawing.layout` for functions that
  56. compute node positions.
  57. ax : Matplotlib Axes object, optional
  58. Draw the graph in specified Matplotlib axes.
  59. kwds : optional keywords
  60. See networkx.draw_networkx() for a description of optional keywords.
  61. Examples
  62. --------
  63. >>> G = nx.dodecahedral_graph()
  64. >>> nx.draw(G)
  65. >>> nx.draw(G, pos=nx.spring_layout(G)) # use spring layout
  66. See Also
  67. --------
  68. draw_networkx
  69. draw_networkx_nodes
  70. draw_networkx_edges
  71. draw_networkx_labels
  72. draw_networkx_edge_labels
  73. Notes
  74. -----
  75. This function has the same name as pylab.draw and pyplot.draw
  76. so beware when using `from networkx import *`
  77. since you might overwrite the pylab.draw function.
  78. With pyplot use
  79. >>> import matplotlib.pyplot as plt
  80. >>> G = nx.dodecahedral_graph()
  81. >>> nx.draw(G) # networkx draw()
  82. >>> plt.draw() # pyplot draw()
  83. Also see the NetworkX drawing examples at
  84. https://networkx.org/documentation/latest/auto_examples/index.html
  85. """
  86. import matplotlib.pyplot as plt
  87. if ax is None:
  88. cf = plt.gcf()
  89. else:
  90. cf = ax.get_figure()
  91. cf.set_facecolor("w")
  92. if ax is None:
  93. if cf.axes:
  94. ax = cf.gca()
  95. else:
  96. ax = cf.add_axes((0, 0, 1, 1))
  97. if "with_labels" not in kwds:
  98. kwds["with_labels"] = "labels" in kwds
  99. draw_networkx(G, pos=pos, ax=ax, **kwds)
  100. ax.set_axis_off()
  101. plt.draw_if_interactive()
  102. return
  103. def draw_networkx(G, pos=None, arrows=None, with_labels=True, **kwds):
  104. r"""Draw the graph G using Matplotlib.
  105. Draw the graph with Matplotlib with options for node positions,
  106. labeling, titles, and many other drawing features.
  107. See draw() for simple drawing without labels or axes.
  108. Parameters
  109. ----------
  110. G : graph
  111. A networkx graph
  112. pos : dictionary, optional
  113. A dictionary with nodes as keys and positions as values.
  114. If not specified a spring layout positioning will be computed.
  115. See :py:mod:`networkx.drawing.layout` for functions that
  116. compute node positions.
  117. arrows : bool or None, optional (default=None)
  118. If `None`, directed graphs draw arrowheads with
  119. `~matplotlib.patches.FancyArrowPatch`, while undirected graphs draw edges
  120. via `~matplotlib.collections.LineCollection` for speed.
  121. If `True`, draw arrowheads with FancyArrowPatches (bendable and stylish).
  122. If `False`, draw edges using LineCollection (linear and fast).
  123. For directed graphs, if True draw arrowheads.
  124. Note: Arrows will be the same color as edges.
  125. arrowstyle : str (default='-\|>' for directed graphs)
  126. For directed graphs, choose the style of the arrowsheads.
  127. For undirected graphs default to '-'
  128. See `matplotlib.patches.ArrowStyle` for more options.
  129. arrowsize : int or list (default=10)
  130. For directed graphs, choose the size of the arrow head's length and
  131. width. A list of values can be passed in to assign a different size for arrow head's length and width.
  132. See `matplotlib.patches.FancyArrowPatch` for attribute `mutation_scale`
  133. for more info.
  134. with_labels : bool (default=True)
  135. Set to True to draw labels on the nodes.
  136. ax : Matplotlib Axes object, optional
  137. Draw the graph in the specified Matplotlib axes.
  138. nodelist : list (default=list(G))
  139. Draw only specified nodes
  140. edgelist : list (default=list(G.edges()))
  141. Draw only specified edges
  142. node_size : scalar or array (default=300)
  143. Size of nodes. If an array is specified it must be the
  144. same length as nodelist.
  145. node_color : color or array of colors (default='#1f78b4')
  146. Node color. Can be a single color or a sequence of colors with the same
  147. length as nodelist. Color can be string or rgb (or rgba) tuple of
  148. floats from 0-1. If numeric values are specified they will be
  149. mapped to colors using the cmap and vmin,vmax parameters. See
  150. matplotlib.scatter for more details.
  151. node_shape : string (default='o')
  152. The shape of the node. Specification is as matplotlib.scatter
  153. marker, one of 'so^>v<dph8'.
  154. alpha : float or None (default=None)
  155. The node and edge transparency
  156. cmap : Matplotlib colormap, optional
  157. Colormap for mapping intensities of nodes
  158. vmin,vmax : float, optional
  159. Minimum and maximum for node colormap scaling
  160. linewidths : scalar or sequence (default=1.0)
  161. Line width of symbol border
  162. width : float or array of floats (default=1.0)
  163. Line width of edges
  164. edge_color : color or array of colors (default='k')
  165. Edge color. Can be a single color or a sequence of colors with the same
  166. length as edgelist. Color can be string or rgb (or rgba) tuple of
  167. floats from 0-1. If numeric values are specified they will be
  168. mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters.
  169. edge_cmap : Matplotlib colormap, optional
  170. Colormap for mapping intensities of edges
  171. edge_vmin,edge_vmax : floats, optional
  172. Minimum and maximum for edge colormap scaling
  173. style : string (default=solid line)
  174. Edge line style e.g.: '-', '--', '-.', ':'
  175. or words like 'solid' or 'dashed'.
  176. (See `matplotlib.patches.FancyArrowPatch`: `linestyle`)
  177. labels : dictionary (default=None)
  178. Node labels in a dictionary of text labels keyed by node
  179. font_size : int (default=12 for nodes, 10 for edges)
  180. Font size for text labels
  181. font_color : string (default='k' black)
  182. Font color string
  183. font_weight : string (default='normal')
  184. Font weight
  185. font_family : string (default='sans-serif')
  186. Font family
  187. label : string, optional
  188. Label for graph legend
  189. kwds : optional keywords
  190. See networkx.draw_networkx_nodes(), networkx.draw_networkx_edges(), and
  191. networkx.draw_networkx_labels() for a description of optional keywords.
  192. Notes
  193. -----
  194. For directed graphs, arrows are drawn at the head end. Arrows can be
  195. turned off with keyword arrows=False.
  196. Examples
  197. --------
  198. >>> G = nx.dodecahedral_graph()
  199. >>> nx.draw(G)
  200. >>> nx.draw(G, pos=nx.spring_layout(G)) # use spring layout
  201. >>> import matplotlib.pyplot as plt
  202. >>> limits = plt.axis("off") # turn off axis
  203. Also see the NetworkX drawing examples at
  204. https://networkx.org/documentation/latest/auto_examples/index.html
  205. See Also
  206. --------
  207. draw
  208. draw_networkx_nodes
  209. draw_networkx_edges
  210. draw_networkx_labels
  211. draw_networkx_edge_labels
  212. """
  213. from inspect import signature
  214. import matplotlib.pyplot as plt
  215. # Get all valid keywords by inspecting the signatures of draw_networkx_nodes,
  216. # draw_networkx_edges, draw_networkx_labels
  217. valid_node_kwds = signature(draw_networkx_nodes).parameters.keys()
  218. valid_edge_kwds = signature(draw_networkx_edges).parameters.keys()
  219. valid_label_kwds = signature(draw_networkx_labels).parameters.keys()
  220. # Create a set with all valid keywords across the three functions and
  221. # remove the arguments of this function (draw_networkx)
  222. valid_kwds = (valid_node_kwds | valid_edge_kwds | valid_label_kwds) - {
  223. "G",
  224. "pos",
  225. "arrows",
  226. "with_labels",
  227. }
  228. if any(k not in valid_kwds for k in kwds):
  229. invalid_args = ", ".join([k for k in kwds if k not in valid_kwds])
  230. raise ValueError(f"Received invalid argument(s): {invalid_args}")
  231. node_kwds = {k: v for k, v in kwds.items() if k in valid_node_kwds}
  232. edge_kwds = {k: v for k, v in kwds.items() if k in valid_edge_kwds}
  233. label_kwds = {k: v for k, v in kwds.items() if k in valid_label_kwds}
  234. if pos is None:
  235. pos = nx.drawing.spring_layout(G) # default to spring layout
  236. draw_networkx_nodes(G, pos, **node_kwds)
  237. draw_networkx_edges(G, pos, arrows=arrows, **edge_kwds)
  238. if with_labels:
  239. draw_networkx_labels(G, pos, **label_kwds)
  240. plt.draw_if_interactive()
  241. def draw_networkx_nodes(
  242. G,
  243. pos,
  244. nodelist=None,
  245. node_size=300,
  246. node_color="#1f78b4",
  247. node_shape="o",
  248. alpha=None,
  249. cmap=None,
  250. vmin=None,
  251. vmax=None,
  252. ax=None,
  253. linewidths=None,
  254. edgecolors=None,
  255. label=None,
  256. margins=None,
  257. ):
  258. """Draw the nodes of the graph G.
  259. This draws only the nodes of the graph G.
  260. Parameters
  261. ----------
  262. G : graph
  263. A networkx graph
  264. pos : dictionary
  265. A dictionary with nodes as keys and positions as values.
  266. Positions should be sequences of length 2.
  267. ax : Matplotlib Axes object, optional
  268. Draw the graph in the specified Matplotlib axes.
  269. nodelist : list (default list(G))
  270. Draw only specified nodes
  271. node_size : scalar or array (default=300)
  272. Size of nodes. If an array it must be the same length as nodelist.
  273. node_color : color or array of colors (default='#1f78b4')
  274. Node color. Can be a single color or a sequence of colors with the same
  275. length as nodelist. Color can be string or rgb (or rgba) tuple of
  276. floats from 0-1. If numeric values are specified they will be
  277. mapped to colors using the cmap and vmin,vmax parameters. See
  278. matplotlib.scatter for more details.
  279. node_shape : string (default='o')
  280. The shape of the node. Specification is as matplotlib.scatter
  281. marker, one of 'so^>v<dph8'.
  282. alpha : float or array of floats (default=None)
  283. The node transparency. This can be a single alpha value,
  284. in which case it will be applied to all the nodes of color. Otherwise,
  285. if it is an array, the elements of alpha will be applied to the colors
  286. in order (cycling through alpha multiple times if necessary).
  287. cmap : Matplotlib colormap (default=None)
  288. Colormap for mapping intensities of nodes
  289. vmin,vmax : floats or None (default=None)
  290. Minimum and maximum for node colormap scaling
  291. linewidths : [None | scalar | sequence] (default=1.0)
  292. Line width of symbol border
  293. edgecolors : [None | scalar | sequence] (default = node_color)
  294. Colors of node borders
  295. label : [None | string]
  296. Label for legend
  297. margins : float or 2-tuple, optional
  298. Sets the padding for axis autoscaling. Increase margin to prevent
  299. clipping for nodes that are near the edges of an image. Values should
  300. be in the range ``[0, 1]``. See :meth:`matplotlib.axes.Axes.margins`
  301. for details. The default is `None`, which uses the Matplotlib default.
  302. Returns
  303. -------
  304. matplotlib.collections.PathCollection
  305. `PathCollection` of the nodes.
  306. Examples
  307. --------
  308. >>> G = nx.dodecahedral_graph()
  309. >>> nodes = nx.draw_networkx_nodes(G, pos=nx.spring_layout(G))
  310. Also see the NetworkX drawing examples at
  311. https://networkx.org/documentation/latest/auto_examples/index.html
  312. See Also
  313. --------
  314. draw
  315. draw_networkx
  316. draw_networkx_edges
  317. draw_networkx_labels
  318. draw_networkx_edge_labels
  319. """
  320. from collections.abc import Iterable
  321. import matplotlib as mpl
  322. import matplotlib.collections # call as mpl.collections
  323. import matplotlib.pyplot as plt
  324. import numpy as np
  325. if ax is None:
  326. ax = plt.gca()
  327. if nodelist is None:
  328. nodelist = list(G)
  329. if len(nodelist) == 0: # empty nodelist, no drawing
  330. return mpl.collections.PathCollection(None)
  331. try:
  332. xy = np.asarray([pos[v] for v in nodelist])
  333. except KeyError as err:
  334. raise nx.NetworkXError(f"Node {err} has no position.") from err
  335. if isinstance(alpha, Iterable):
  336. node_color = apply_alpha(node_color, alpha, nodelist, cmap, vmin, vmax)
  337. alpha = None
  338. node_collection = ax.scatter(
  339. xy[:, 0],
  340. xy[:, 1],
  341. s=node_size,
  342. c=node_color,
  343. marker=node_shape,
  344. cmap=cmap,
  345. vmin=vmin,
  346. vmax=vmax,
  347. alpha=alpha,
  348. linewidths=linewidths,
  349. edgecolors=edgecolors,
  350. label=label,
  351. )
  352. ax.tick_params(
  353. axis="both",
  354. which="both",
  355. bottom=False,
  356. left=False,
  357. labelbottom=False,
  358. labelleft=False,
  359. )
  360. if margins is not None:
  361. if isinstance(margins, Iterable):
  362. ax.margins(*margins)
  363. else:
  364. ax.margins(margins)
  365. node_collection.set_zorder(2)
  366. return node_collection
  367. def draw_networkx_edges(
  368. G,
  369. pos,
  370. edgelist=None,
  371. width=1.0,
  372. edge_color="k",
  373. style="solid",
  374. alpha=None,
  375. arrowstyle=None,
  376. arrowsize=10,
  377. edge_cmap=None,
  378. edge_vmin=None,
  379. edge_vmax=None,
  380. ax=None,
  381. arrows=None,
  382. label=None,
  383. node_size=300,
  384. nodelist=None,
  385. node_shape="o",
  386. connectionstyle="arc3",
  387. min_source_margin=0,
  388. min_target_margin=0,
  389. ):
  390. r"""Draw the edges of the graph G.
  391. This draws only the edges of the graph G.
  392. Parameters
  393. ----------
  394. G : graph
  395. A networkx graph
  396. pos : dictionary
  397. A dictionary with nodes as keys and positions as values.
  398. Positions should be sequences of length 2.
  399. edgelist : collection of edge tuples (default=G.edges())
  400. Draw only specified edges
  401. width : float or array of floats (default=1.0)
  402. Line width of edges
  403. edge_color : color or array of colors (default='k')
  404. Edge color. Can be a single color or a sequence of colors with the same
  405. length as edgelist. Color can be string or rgb (or rgba) tuple of
  406. floats from 0-1. If numeric values are specified they will be
  407. mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters.
  408. style : string or array of strings (default='solid')
  409. Edge line style e.g.: '-', '--', '-.', ':'
  410. or words like 'solid' or 'dashed'.
  411. Can be a single style or a sequence of styles with the same
  412. length as the edge list.
  413. If less styles than edges are given the styles will cycle.
  414. If more styles than edges are given the styles will be used sequentially
  415. and not be exhausted.
  416. Also, `(offset, onoffseq)` tuples can be used as style instead of a strings.
  417. (See `matplotlib.patches.FancyArrowPatch`: `linestyle`)
  418. alpha : float or array of floats (default=None)
  419. The edge transparency. This can be a single alpha value,
  420. in which case it will be applied to all specified edges. Otherwise,
  421. if it is an array, the elements of alpha will be applied to the colors
  422. in order (cycling through alpha multiple times if necessary).
  423. edge_cmap : Matplotlib colormap, optional
  424. Colormap for mapping intensities of edges
  425. edge_vmin,edge_vmax : floats, optional
  426. Minimum and maximum for edge colormap scaling
  427. ax : Matplotlib Axes object, optional
  428. Draw the graph in the specified Matplotlib axes.
  429. arrows : bool or None, optional (default=None)
  430. If `None`, directed graphs draw arrowheads with
  431. `~matplotlib.patches.FancyArrowPatch`, while undirected graphs draw edges
  432. via `~matplotlib.collections.LineCollection` for speed.
  433. If `True`, draw arrowheads with FancyArrowPatches (bendable and stylish).
  434. If `False`, draw edges using LineCollection (linear and fast).
  435. Note: Arrowheads will be the same color as edges.
  436. arrowstyle : str (default='-\|>' for directed graphs)
  437. For directed graphs and `arrows==True` defaults to '-\|>',
  438. For undirected graphs default to '-'.
  439. See `matplotlib.patches.ArrowStyle` for more options.
  440. arrowsize : int (default=10)
  441. For directed graphs, choose the size of the arrow head's length and
  442. width. See `matplotlib.patches.FancyArrowPatch` for attribute
  443. `mutation_scale` for more info.
  444. connectionstyle : string (default="arc3")
  445. Pass the connectionstyle parameter to create curved arc of rounding
  446. radius rad. For example, connectionstyle='arc3,rad=0.2'.
  447. See `matplotlib.patches.ConnectionStyle` and
  448. `matplotlib.patches.FancyArrowPatch` for more info.
  449. node_size : scalar or array (default=300)
  450. Size of nodes. Though the nodes are not drawn with this function, the
  451. node size is used in determining edge positioning.
  452. nodelist : list, optional (default=G.nodes())
  453. This provides the node order for the `node_size` array (if it is an array).
  454. node_shape : string (default='o')
  455. The marker used for nodes, used in determining edge positioning.
  456. Specification is as a `matplotlib.markers` marker, e.g. one of 'so^>v<dph8'.
  457. label : None or string
  458. Label for legend
  459. min_source_margin : int (default=0)
  460. The minimum margin (gap) at the beginning of the edge at the source.
  461. min_target_margin : int (default=0)
  462. The minimum margin (gap) at the end of the edge at the target.
  463. Returns
  464. -------
  465. matplotlib.collections.LineCollection or a list of matplotlib.patches.FancyArrowPatch
  466. If ``arrows=True``, a list of FancyArrowPatches is returned.
  467. If ``arrows=False``, a LineCollection is returned.
  468. If ``arrows=None`` (the default), then a LineCollection is returned if
  469. `G` is undirected, otherwise returns a list of FancyArrowPatches.
  470. Notes
  471. -----
  472. For directed graphs, arrows are drawn at the head end. Arrows can be
  473. turned off with keyword arrows=False or by passing an arrowstyle without
  474. an arrow on the end.
  475. Be sure to include `node_size` as a keyword argument; arrows are
  476. drawn considering the size of nodes.
  477. Self-loops are always drawn with `~matplotlib.patches.FancyArrowPatch`
  478. regardless of the value of `arrows` or whether `G` is directed.
  479. When ``arrows=False`` or ``arrows=None`` and `G` is undirected, the
  480. FancyArrowPatches corresponding to the self-loops are not explicitly
  481. returned. They should instead be accessed via the ``Axes.patches``
  482. attribute (see examples).
  483. Examples
  484. --------
  485. >>> G = nx.dodecahedral_graph()
  486. >>> edges = nx.draw_networkx_edges(G, pos=nx.spring_layout(G))
  487. >>> G = nx.DiGraph()
  488. >>> G.add_edges_from([(1, 2), (1, 3), (2, 3)])
  489. >>> arcs = nx.draw_networkx_edges(G, pos=nx.spring_layout(G))
  490. >>> alphas = [0.3, 0.4, 0.5]
  491. >>> for i, arc in enumerate(arcs): # change alpha values of arcs
  492. ... arc.set_alpha(alphas[i])
  493. The FancyArrowPatches corresponding to self-loops are not always
  494. returned, but can always be accessed via the ``patches`` attribute of the
  495. `matplotlib.Axes` object.
  496. >>> import matplotlib.pyplot as plt
  497. >>> fig, ax = plt.subplots()
  498. >>> G = nx.Graph([(0, 1), (0, 0)]) # Self-loop at node 0
  499. >>> edge_collection = nx.draw_networkx_edges(G, pos=nx.circular_layout(G), ax=ax)
  500. >>> self_loop_fap = ax.patches[0]
  501. Also see the NetworkX drawing examples at
  502. https://networkx.org/documentation/latest/auto_examples/index.html
  503. See Also
  504. --------
  505. draw
  506. draw_networkx
  507. draw_networkx_nodes
  508. draw_networkx_labels
  509. draw_networkx_edge_labels
  510. """
  511. import matplotlib as mpl
  512. import matplotlib.collections # call as mpl.collections
  513. import matplotlib.colors # call as mpl.colors
  514. import matplotlib.patches # call as mpl.patches
  515. import matplotlib.path # call as mpl.path
  516. import matplotlib.pyplot as plt
  517. import numpy as np
  518. # The default behavior is to use LineCollection to draw edges for
  519. # undirected graphs (for performance reasons) and use FancyArrowPatches
  520. # for directed graphs.
  521. # The `arrows` keyword can be used to override the default behavior
  522. use_linecollection = not G.is_directed()
  523. if arrows in (True, False):
  524. use_linecollection = not arrows
  525. # Some kwargs only apply to FancyArrowPatches. Warn users when they use
  526. # non-default values for these kwargs when LineCollection is being used
  527. # instead of silently ignoring the specified option
  528. if use_linecollection and any(
  529. [
  530. arrowstyle is not None,
  531. arrowsize != 10,
  532. connectionstyle != "arc3",
  533. min_source_margin != 0,
  534. min_target_margin != 0,
  535. ]
  536. ):
  537. import warnings
  538. msg = (
  539. "\n\nThe {0} keyword argument is not applicable when drawing edges\n"
  540. "with LineCollection.\n\n"
  541. "To make this warning go away, either specify `arrows=True` to\n"
  542. "force FancyArrowPatches or use the default value for {0}.\n"
  543. "Note that using FancyArrowPatches may be slow for large graphs.\n"
  544. )
  545. if arrowstyle is not None:
  546. msg = msg.format("arrowstyle")
  547. if arrowsize != 10:
  548. msg = msg.format("arrowsize")
  549. if connectionstyle != "arc3":
  550. msg = msg.format("connectionstyle")
  551. if min_source_margin != 0:
  552. msg = msg.format("min_source_margin")
  553. if min_target_margin != 0:
  554. msg = msg.format("min_target_margin")
  555. warnings.warn(msg, category=UserWarning, stacklevel=2)
  556. if arrowstyle == None:
  557. if G.is_directed():
  558. arrowstyle = "-|>"
  559. else:
  560. arrowstyle = "-"
  561. if ax is None:
  562. ax = plt.gca()
  563. if edgelist is None:
  564. edgelist = list(G.edges())
  565. if len(edgelist) == 0: # no edges!
  566. return []
  567. if nodelist is None:
  568. nodelist = list(G.nodes())
  569. # FancyArrowPatch handles color=None different from LineCollection
  570. if edge_color is None:
  571. edge_color = "k"
  572. edgelist_tuple = list(map(tuple, edgelist))
  573. # set edge positions
  574. edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist])
  575. # Check if edge_color is an array of floats and map to edge_cmap.
  576. # This is the only case handled differently from matplotlib
  577. if (
  578. np.iterable(edge_color)
  579. and (len(edge_color) == len(edge_pos))
  580. and np.alltrue([isinstance(c, Number) for c in edge_color])
  581. ):
  582. if edge_cmap is not None:
  583. assert isinstance(edge_cmap, mpl.colors.Colormap)
  584. else:
  585. edge_cmap = plt.get_cmap()
  586. if edge_vmin is None:
  587. edge_vmin = min(edge_color)
  588. if edge_vmax is None:
  589. edge_vmax = max(edge_color)
  590. color_normal = mpl.colors.Normalize(vmin=edge_vmin, vmax=edge_vmax)
  591. edge_color = [edge_cmap(color_normal(e)) for e in edge_color]
  592. def _draw_networkx_edges_line_collection():
  593. edge_collection = mpl.collections.LineCollection(
  594. edge_pos,
  595. colors=edge_color,
  596. linewidths=width,
  597. antialiaseds=(1,),
  598. linestyle=style,
  599. alpha=alpha,
  600. )
  601. edge_collection.set_cmap(edge_cmap)
  602. edge_collection.set_clim(edge_vmin, edge_vmax)
  603. edge_collection.set_zorder(1) # edges go behind nodes
  604. edge_collection.set_label(label)
  605. ax.add_collection(edge_collection)
  606. return edge_collection
  607. def _draw_networkx_edges_fancy_arrow_patch():
  608. # Note: Waiting for someone to implement arrow to intersection with
  609. # marker. Meanwhile, this works well for polygons with more than 4
  610. # sides and circle.
  611. def to_marker_edge(marker_size, marker):
  612. if marker in "s^>v<d": # `large` markers need extra space
  613. return np.sqrt(2 * marker_size) / 2
  614. else:
  615. return np.sqrt(marker_size) / 2
  616. # Draw arrows with `matplotlib.patches.FancyarrowPatch`
  617. arrow_collection = []
  618. if isinstance(arrowsize, list):
  619. if len(arrowsize) != len(edge_pos):
  620. raise ValueError("arrowsize should have the same length as edgelist")
  621. else:
  622. mutation_scale = arrowsize # scale factor of arrow head
  623. base_connection_style = mpl.patches.ConnectionStyle(connectionstyle)
  624. # Fallback for self-loop scale. Left outside of _connectionstyle so it is
  625. # only computed once
  626. max_nodesize = np.array(node_size).max()
  627. def _connectionstyle(posA, posB, *args, **kwargs):
  628. # check if we need to do a self-loop
  629. if np.all(posA == posB):
  630. # Self-loops are scaled by view extent, except in cases the extent
  631. # is 0, e.g. for a single node. In this case, fall back to scaling
  632. # by the maximum node size
  633. selfloop_ht = 0.005 * max_nodesize if h == 0 else h
  634. # this is called with _screen space_ values so convert back
  635. # to data space
  636. data_loc = ax.transData.inverted().transform(posA)
  637. v_shift = 0.1 * selfloop_ht
  638. h_shift = v_shift * 0.5
  639. # put the top of the loop first so arrow is not hidden by node
  640. path = [
  641. # 1
  642. data_loc + np.asarray([0, v_shift]),
  643. # 4 4 4
  644. data_loc + np.asarray([h_shift, v_shift]),
  645. data_loc + np.asarray([h_shift, 0]),
  646. data_loc,
  647. # 4 4 4
  648. data_loc + np.asarray([-h_shift, 0]),
  649. data_loc + np.asarray([-h_shift, v_shift]),
  650. data_loc + np.asarray([0, v_shift]),
  651. ]
  652. ret = mpl.path.Path(ax.transData.transform(path), [1, 4, 4, 4, 4, 4, 4])
  653. # if not, fall back to the user specified behavior
  654. else:
  655. ret = base_connection_style(posA, posB, *args, **kwargs)
  656. return ret
  657. # FancyArrowPatch doesn't handle color strings
  658. arrow_colors = mpl.colors.colorConverter.to_rgba_array(edge_color, alpha)
  659. for i, (src, dst) in zip(fancy_edges_indices, edge_pos):
  660. x1, y1 = src
  661. x2, y2 = dst
  662. shrink_source = 0 # space from source to tail
  663. shrink_target = 0 # space from head to target
  664. if isinstance(arrowsize, list):
  665. # Scale each factor of each arrow based on arrowsize list
  666. mutation_scale = arrowsize[i]
  667. if np.iterable(node_size): # many node sizes
  668. source, target = edgelist[i][:2]
  669. source_node_size = node_size[nodelist.index(source)]
  670. target_node_size = node_size[nodelist.index(target)]
  671. shrink_source = to_marker_edge(source_node_size, node_shape)
  672. shrink_target = to_marker_edge(target_node_size, node_shape)
  673. else:
  674. shrink_source = shrink_target = to_marker_edge(node_size, node_shape)
  675. if shrink_source < min_source_margin:
  676. shrink_source = min_source_margin
  677. if shrink_target < min_target_margin:
  678. shrink_target = min_target_margin
  679. if len(arrow_colors) > i:
  680. arrow_color = arrow_colors[i]
  681. elif len(arrow_colors) == 1:
  682. arrow_color = arrow_colors[0]
  683. else: # Cycle through colors
  684. arrow_color = arrow_colors[i % len(arrow_colors)]
  685. if np.iterable(width):
  686. if len(width) > i:
  687. line_width = width[i]
  688. else:
  689. line_width = width[i % len(width)]
  690. else:
  691. line_width = width
  692. if (
  693. np.iterable(style)
  694. and not isinstance(style, str)
  695. and not isinstance(style, tuple)
  696. ):
  697. if len(style) > i:
  698. linestyle = style[i]
  699. else: # Cycle through styles
  700. linestyle = style[i % len(style)]
  701. else:
  702. linestyle = style
  703. arrow = mpl.patches.FancyArrowPatch(
  704. (x1, y1),
  705. (x2, y2),
  706. arrowstyle=arrowstyle,
  707. shrinkA=shrink_source,
  708. shrinkB=shrink_target,
  709. mutation_scale=mutation_scale,
  710. color=arrow_color,
  711. linewidth=line_width,
  712. connectionstyle=_connectionstyle,
  713. linestyle=linestyle,
  714. zorder=1,
  715. ) # arrows go behind nodes
  716. arrow_collection.append(arrow)
  717. ax.add_patch(arrow)
  718. return arrow_collection
  719. # compute initial view
  720. minx = np.amin(np.ravel(edge_pos[:, :, 0]))
  721. maxx = np.amax(np.ravel(edge_pos[:, :, 0]))
  722. miny = np.amin(np.ravel(edge_pos[:, :, 1]))
  723. maxy = np.amax(np.ravel(edge_pos[:, :, 1]))
  724. w = maxx - minx
  725. h = maxy - miny
  726. # Draw the edges
  727. if use_linecollection:
  728. edge_viz_obj = _draw_networkx_edges_line_collection()
  729. # Make sure selfloop edges are also drawn
  730. selfloops_to_draw = [loop for loop in nx.selfloop_edges(G) if loop in edgelist]
  731. if selfloops_to_draw:
  732. fancy_edges_indices = [
  733. edgelist_tuple.index(loop) for loop in selfloops_to_draw
  734. ]
  735. edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in selfloops_to_draw])
  736. arrowstyle = "-"
  737. _draw_networkx_edges_fancy_arrow_patch()
  738. else:
  739. fancy_edges_indices = range(len(edgelist))
  740. edge_viz_obj = _draw_networkx_edges_fancy_arrow_patch()
  741. # update view after drawing
  742. padx, pady = 0.05 * w, 0.05 * h
  743. corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady)
  744. ax.update_datalim(corners)
  745. ax.autoscale_view()
  746. ax.tick_params(
  747. axis="both",
  748. which="both",
  749. bottom=False,
  750. left=False,
  751. labelbottom=False,
  752. labelleft=False,
  753. )
  754. return edge_viz_obj
  755. def draw_networkx_labels(
  756. G,
  757. pos,
  758. labels=None,
  759. font_size=12,
  760. font_color="k",
  761. font_family="sans-serif",
  762. font_weight="normal",
  763. alpha=None,
  764. bbox=None,
  765. horizontalalignment="center",
  766. verticalalignment="center",
  767. ax=None,
  768. clip_on=True,
  769. ):
  770. """Draw node labels on the graph G.
  771. Parameters
  772. ----------
  773. G : graph
  774. A networkx graph
  775. pos : dictionary
  776. A dictionary with nodes as keys and positions as values.
  777. Positions should be sequences of length 2.
  778. labels : dictionary (default={n: n for n in G})
  779. Node labels in a dictionary of text labels keyed by node.
  780. Node-keys in labels should appear as keys in `pos`.
  781. If needed use: `{n:lab for n,lab in labels.items() if n in pos}`
  782. font_size : int (default=12)
  783. Font size for text labels
  784. font_color : string (default='k' black)
  785. Font color string
  786. font_weight : string (default='normal')
  787. Font weight
  788. font_family : string (default='sans-serif')
  789. Font family
  790. alpha : float or None (default=None)
  791. The text transparency
  792. bbox : Matplotlib bbox, (default is Matplotlib's ax.text default)
  793. Specify text box properties (e.g. shape, color etc.) for node labels.
  794. horizontalalignment : string (default='center')
  795. Horizontal alignment {'center', 'right', 'left'}
  796. verticalalignment : string (default='center')
  797. Vertical alignment {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
  798. ax : Matplotlib Axes object, optional
  799. Draw the graph in the specified Matplotlib axes.
  800. clip_on : bool (default=True)
  801. Turn on clipping of node labels at axis boundaries
  802. Returns
  803. -------
  804. dict
  805. `dict` of labels keyed on the nodes
  806. Examples
  807. --------
  808. >>> G = nx.dodecahedral_graph()
  809. >>> labels = nx.draw_networkx_labels(G, pos=nx.spring_layout(G))
  810. Also see the NetworkX drawing examples at
  811. https://networkx.org/documentation/latest/auto_examples/index.html
  812. See Also
  813. --------
  814. draw
  815. draw_networkx
  816. draw_networkx_nodes
  817. draw_networkx_edges
  818. draw_networkx_edge_labels
  819. """
  820. import matplotlib.pyplot as plt
  821. if ax is None:
  822. ax = plt.gca()
  823. if labels is None:
  824. labels = {n: n for n in G.nodes()}
  825. text_items = {} # there is no text collection so we'll fake one
  826. for n, label in labels.items():
  827. (x, y) = pos[n]
  828. if not isinstance(label, str):
  829. label = str(label) # this makes "1" and 1 labeled the same
  830. t = ax.text(
  831. x,
  832. y,
  833. label,
  834. size=font_size,
  835. color=font_color,
  836. family=font_family,
  837. weight=font_weight,
  838. alpha=alpha,
  839. horizontalalignment=horizontalalignment,
  840. verticalalignment=verticalalignment,
  841. transform=ax.transData,
  842. bbox=bbox,
  843. clip_on=clip_on,
  844. )
  845. text_items[n] = t
  846. ax.tick_params(
  847. axis="both",
  848. which="both",
  849. bottom=False,
  850. left=False,
  851. labelbottom=False,
  852. labelleft=False,
  853. )
  854. return text_items
  855. def draw_networkx_edge_labels(
  856. G,
  857. pos,
  858. edge_labels=None,
  859. label_pos=0.5,
  860. font_size=10,
  861. font_color="k",
  862. font_family="sans-serif",
  863. font_weight="normal",
  864. alpha=None,
  865. bbox=None,
  866. horizontalalignment="center",
  867. verticalalignment="center",
  868. ax=None,
  869. rotate=True,
  870. clip_on=True,
  871. ):
  872. """Draw edge labels.
  873. Parameters
  874. ----------
  875. G : graph
  876. A networkx graph
  877. pos : dictionary
  878. A dictionary with nodes as keys and positions as values.
  879. Positions should be sequences of length 2.
  880. edge_labels : dictionary (default=None)
  881. Edge labels in a dictionary of labels keyed by edge two-tuple.
  882. Only labels for the keys in the dictionary are drawn.
  883. label_pos : float (default=0.5)
  884. Position of edge label along edge (0=head, 0.5=center, 1=tail)
  885. font_size : int (default=10)
  886. Font size for text labels
  887. font_color : string (default='k' black)
  888. Font color string
  889. font_weight : string (default='normal')
  890. Font weight
  891. font_family : string (default='sans-serif')
  892. Font family
  893. alpha : float or None (default=None)
  894. The text transparency
  895. bbox : Matplotlib bbox, optional
  896. Specify text box properties (e.g. shape, color etc.) for edge labels.
  897. Default is {boxstyle='round', ec=(1.0, 1.0, 1.0), fc=(1.0, 1.0, 1.0)}.
  898. horizontalalignment : string (default='center')
  899. Horizontal alignment {'center', 'right', 'left'}
  900. verticalalignment : string (default='center')
  901. Vertical alignment {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
  902. ax : Matplotlib Axes object, optional
  903. Draw the graph in the specified Matplotlib axes.
  904. rotate : bool (default=True)
  905. Rotate edge labels to lie parallel to edges
  906. clip_on : bool (default=True)
  907. Turn on clipping of edge labels at axis boundaries
  908. Returns
  909. -------
  910. dict
  911. `dict` of labels keyed by edge
  912. Examples
  913. --------
  914. >>> G = nx.dodecahedral_graph()
  915. >>> edge_labels = nx.draw_networkx_edge_labels(G, pos=nx.spring_layout(G))
  916. Also see the NetworkX drawing examples at
  917. https://networkx.org/documentation/latest/auto_examples/index.html
  918. See Also
  919. --------
  920. draw
  921. draw_networkx
  922. draw_networkx_nodes
  923. draw_networkx_edges
  924. draw_networkx_labels
  925. """
  926. import matplotlib.pyplot as plt
  927. import numpy as np
  928. if ax is None:
  929. ax = plt.gca()
  930. if edge_labels is None:
  931. labels = {(u, v): d for u, v, d in G.edges(data=True)}
  932. else:
  933. labels = edge_labels
  934. # Informative exception for multiedges
  935. try:
  936. (u, v) = next(iter(labels)) # ensures no edge key provided
  937. except ValueError as err:
  938. raise nx.NetworkXError(
  939. "draw_networkx_edge_labels does not support multiedges."
  940. ) from err
  941. except StopIteration:
  942. pass
  943. text_items = {}
  944. for (n1, n2), label in labels.items():
  945. (x1, y1) = pos[n1]
  946. (x2, y2) = pos[n2]
  947. (x, y) = (
  948. x1 * label_pos + x2 * (1.0 - label_pos),
  949. y1 * label_pos + y2 * (1.0 - label_pos),
  950. )
  951. if rotate:
  952. # in degrees
  953. angle = np.arctan2(y2 - y1, x2 - x1) / (2.0 * np.pi) * 360
  954. # make label orientation "right-side-up"
  955. if angle > 90:
  956. angle -= 180
  957. if angle < -90:
  958. angle += 180
  959. # transform data coordinate angle to screen coordinate angle
  960. xy = np.array((x, y))
  961. trans_angle = ax.transData.transform_angles(
  962. np.array((angle,)), xy.reshape((1, 2))
  963. )[0]
  964. else:
  965. trans_angle = 0.0
  966. # use default box of white with white border
  967. if bbox is None:
  968. bbox = {"boxstyle": "round", "ec": (1.0, 1.0, 1.0), "fc": (1.0, 1.0, 1.0)}
  969. if not isinstance(label, str):
  970. label = str(label) # this makes "1" and 1 labeled the same
  971. t = ax.text(
  972. x,
  973. y,
  974. label,
  975. size=font_size,
  976. color=font_color,
  977. family=font_family,
  978. weight=font_weight,
  979. alpha=alpha,
  980. horizontalalignment=horizontalalignment,
  981. verticalalignment=verticalalignment,
  982. rotation=trans_angle,
  983. transform=ax.transData,
  984. bbox=bbox,
  985. zorder=1,
  986. clip_on=clip_on,
  987. )
  988. text_items[(n1, n2)] = t
  989. ax.tick_params(
  990. axis="both",
  991. which="both",
  992. bottom=False,
  993. left=False,
  994. labelbottom=False,
  995. labelleft=False,
  996. )
  997. return text_items
  998. def draw_circular(G, **kwargs):
  999. """Draw the graph `G` with a circular layout.
  1000. This is a convenience function equivalent to::
  1001. nx.draw(G, pos=nx.circular_layout(G), **kwargs)
  1002. Parameters
  1003. ----------
  1004. G : graph
  1005. A networkx graph
  1006. kwargs : optional keywords
  1007. See `draw_networkx` for a description of optional keywords.
  1008. Notes
  1009. -----
  1010. The layout is computed each time this function is called. For
  1011. repeated drawing it is much more efficient to call
  1012. `~networkx.drawing.layout.circular_layout` directly and reuse the result::
  1013. >>> G = nx.complete_graph(5)
  1014. >>> pos = nx.circular_layout(G)
  1015. >>> nx.draw(G, pos=pos) # Draw the original graph
  1016. >>> # Draw a subgraph, reusing the same node positions
  1017. >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
  1018. See Also
  1019. --------
  1020. :func:`~networkx.drawing.layout.circular_layout`
  1021. """
  1022. draw(G, circular_layout(G), **kwargs)
  1023. def draw_kamada_kawai(G, **kwargs):
  1024. """Draw the graph `G` with a Kamada-Kawai force-directed layout.
  1025. This is a convenience function equivalent to::
  1026. nx.draw(G, pos=nx.kamada_kawai_layout(G), **kwargs)
  1027. Parameters
  1028. ----------
  1029. G : graph
  1030. A networkx graph
  1031. kwargs : optional keywords
  1032. See `draw_networkx` for a description of optional keywords.
  1033. Notes
  1034. -----
  1035. The layout is computed each time this function is called.
  1036. For repeated drawing it is much more efficient to call
  1037. `~networkx.drawing.layout.kamada_kawai_layout` directly and reuse the
  1038. result::
  1039. >>> G = nx.complete_graph(5)
  1040. >>> pos = nx.kamada_kawai_layout(G)
  1041. >>> nx.draw(G, pos=pos) # Draw the original graph
  1042. >>> # Draw a subgraph, reusing the same node positions
  1043. >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
  1044. See Also
  1045. --------
  1046. :func:`~networkx.drawing.layout.kamada_kawai_layout`
  1047. """
  1048. draw(G, kamada_kawai_layout(G), **kwargs)
  1049. def draw_random(G, **kwargs):
  1050. """Draw the graph `G` with a random layout.
  1051. This is a convenience function equivalent to::
  1052. nx.draw(G, pos=nx.random_layout(G), **kwargs)
  1053. Parameters
  1054. ----------
  1055. G : graph
  1056. A networkx graph
  1057. kwargs : optional keywords
  1058. See `draw_networkx` for a description of optional keywords.
  1059. Notes
  1060. -----
  1061. The layout is computed each time this function is called.
  1062. For repeated drawing it is much more efficient to call
  1063. `~networkx.drawing.layout.random_layout` directly and reuse the result::
  1064. >>> G = nx.complete_graph(5)
  1065. >>> pos = nx.random_layout(G)
  1066. >>> nx.draw(G, pos=pos) # Draw the original graph
  1067. >>> # Draw a subgraph, reusing the same node positions
  1068. >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
  1069. See Also
  1070. --------
  1071. :func:`~networkx.drawing.layout.random_layout`
  1072. """
  1073. draw(G, random_layout(G), **kwargs)
  1074. def draw_spectral(G, **kwargs):
  1075. """Draw the graph `G` with a spectral 2D layout.
  1076. This is a convenience function equivalent to::
  1077. nx.draw(G, pos=nx.spectral_layout(G), **kwargs)
  1078. For more information about how node positions are determined, see
  1079. `~networkx.drawing.layout.spectral_layout`.
  1080. Parameters
  1081. ----------
  1082. G : graph
  1083. A networkx graph
  1084. kwargs : optional keywords
  1085. See `draw_networkx` for a description of optional keywords.
  1086. Notes
  1087. -----
  1088. The layout is computed each time this function is called.
  1089. For repeated drawing it is much more efficient to call
  1090. `~networkx.drawing.layout.spectral_layout` directly and reuse the result::
  1091. >>> G = nx.complete_graph(5)
  1092. >>> pos = nx.spectral_layout(G)
  1093. >>> nx.draw(G, pos=pos) # Draw the original graph
  1094. >>> # Draw a subgraph, reusing the same node positions
  1095. >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
  1096. See Also
  1097. --------
  1098. :func:`~networkx.drawing.layout.spectral_layout`
  1099. """
  1100. draw(G, spectral_layout(G), **kwargs)
  1101. def draw_spring(G, **kwargs):
  1102. """Draw the graph `G` with a spring layout.
  1103. This is a convenience function equivalent to::
  1104. nx.draw(G, pos=nx.spring_layout(G), **kwargs)
  1105. Parameters
  1106. ----------
  1107. G : graph
  1108. A networkx graph
  1109. kwargs : optional keywords
  1110. See `draw_networkx` for a description of optional keywords.
  1111. Notes
  1112. -----
  1113. `~networkx.drawing.layout.spring_layout` is also the default layout for
  1114. `draw`, so this function is equivalent to `draw`.
  1115. The layout is computed each time this function is called.
  1116. For repeated drawing it is much more efficient to call
  1117. `~networkx.drawing.layout.spring_layout` directly and reuse the result::
  1118. >>> G = nx.complete_graph(5)
  1119. >>> pos = nx.spring_layout(G)
  1120. >>> nx.draw(G, pos=pos) # Draw the original graph
  1121. >>> # Draw a subgraph, reusing the same node positions
  1122. >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
  1123. See Also
  1124. --------
  1125. draw
  1126. :func:`~networkx.drawing.layout.spring_layout`
  1127. """
  1128. draw(G, spring_layout(G), **kwargs)
  1129. def draw_shell(G, nlist=None, **kwargs):
  1130. """Draw networkx graph `G` with shell layout.
  1131. This is a convenience function equivalent to::
  1132. nx.draw(G, pos=nx.shell_layout(G, nlist=nlist), **kwargs)
  1133. Parameters
  1134. ----------
  1135. G : graph
  1136. A networkx graph
  1137. nlist : list of list of nodes, optional
  1138. A list containing lists of nodes representing the shells.
  1139. Default is `None`, meaning all nodes are in a single shell.
  1140. See `~networkx.drawing.layout.shell_layout` for details.
  1141. kwargs : optional keywords
  1142. See `draw_networkx` for a description of optional keywords.
  1143. Notes
  1144. -----
  1145. The layout is computed each time this function is called.
  1146. For repeated drawing it is much more efficient to call
  1147. `~networkx.drawing.layout.shell_layout` directly and reuse the result::
  1148. >>> G = nx.complete_graph(5)
  1149. >>> pos = nx.shell_layout(G)
  1150. >>> nx.draw(G, pos=pos) # Draw the original graph
  1151. >>> # Draw a subgraph, reusing the same node positions
  1152. >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
  1153. See Also
  1154. --------
  1155. :func:`~networkx.drawing.layout.shell_layout`
  1156. """
  1157. draw(G, shell_layout(G, nlist=nlist), **kwargs)
  1158. def draw_planar(G, **kwargs):
  1159. """Draw a planar networkx graph `G` with planar layout.
  1160. This is a convenience function equivalent to::
  1161. nx.draw(G, pos=nx.planar_layout(G), **kwargs)
  1162. Parameters
  1163. ----------
  1164. G : graph
  1165. A planar networkx graph
  1166. kwargs : optional keywords
  1167. See `draw_networkx` for a description of optional keywords.
  1168. Raises
  1169. ------
  1170. NetworkXException
  1171. When `G` is not planar
  1172. Notes
  1173. -----
  1174. The layout is computed each time this function is called.
  1175. For repeated drawing it is much more efficient to call
  1176. `~networkx.drawing.layout.planar_layout` directly and reuse the result::
  1177. >>> G = nx.path_graph(5)
  1178. >>> pos = nx.planar_layout(G)
  1179. >>> nx.draw(G, pos=pos) # Draw the original graph
  1180. >>> # Draw a subgraph, reusing the same node positions
  1181. >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
  1182. See Also
  1183. --------
  1184. :func:`~networkx.drawing.layout.planar_layout`
  1185. """
  1186. draw(G, planar_layout(G), **kwargs)
  1187. def apply_alpha(colors, alpha, elem_list, cmap=None, vmin=None, vmax=None):
  1188. """Apply an alpha (or list of alphas) to the colors provided.
  1189. Parameters
  1190. ----------
  1191. colors : color string or array of floats (default='r')
  1192. Color of element. Can be a single color format string,
  1193. or a sequence of colors with the same length as nodelist.
  1194. If numeric values are specified they will be mapped to
  1195. colors using the cmap and vmin,vmax parameters. See
  1196. matplotlib.scatter for more details.
  1197. alpha : float or array of floats
  1198. Alpha values for elements. This can be a single alpha value, in
  1199. which case it will be applied to all the elements of color. Otherwise,
  1200. if it is an array, the elements of alpha will be applied to the colors
  1201. in order (cycling through alpha multiple times if necessary).
  1202. elem_list : array of networkx objects
  1203. The list of elements which are being colored. These could be nodes,
  1204. edges or labels.
  1205. cmap : matplotlib colormap
  1206. Color map for use if colors is a list of floats corresponding to points
  1207. on a color mapping.
  1208. vmin, vmax : float
  1209. Minimum and maximum values for normalizing colors if a colormap is used
  1210. Returns
  1211. -------
  1212. rgba_colors : numpy ndarray
  1213. Array containing RGBA format values for each of the node colours.
  1214. """
  1215. from itertools import cycle, islice
  1216. import matplotlib as mpl
  1217. import matplotlib.cm # call as mpl.cm
  1218. import matplotlib.colors # call as mpl.colors
  1219. import numpy as np
  1220. # If we have been provided with a list of numbers as long as elem_list,
  1221. # apply the color mapping.
  1222. if len(colors) == len(elem_list) and isinstance(colors[0], Number):
  1223. mapper = mpl.cm.ScalarMappable(cmap=cmap)
  1224. mapper.set_clim(vmin, vmax)
  1225. rgba_colors = mapper.to_rgba(colors)
  1226. # Otherwise, convert colors to matplotlib's RGB using the colorConverter
  1227. # object. These are converted to numpy ndarrays to be consistent with the
  1228. # to_rgba method of ScalarMappable.
  1229. else:
  1230. try:
  1231. rgba_colors = np.array([mpl.colors.colorConverter.to_rgba(colors)])
  1232. except ValueError:
  1233. rgba_colors = np.array(
  1234. [mpl.colors.colorConverter.to_rgba(color) for color in colors]
  1235. )
  1236. # Set the final column of the rgba_colors to have the relevant alpha values
  1237. try:
  1238. # If alpha is longer than the number of colors, resize to the number of
  1239. # elements. Also, if rgba_colors.size (the number of elements of
  1240. # rgba_colors) is the same as the number of elements, resize the array,
  1241. # to avoid it being interpreted as a colormap by scatter()
  1242. if len(alpha) > len(rgba_colors) or rgba_colors.size == len(elem_list):
  1243. rgba_colors = np.resize(rgba_colors, (len(elem_list), 4))
  1244. rgba_colors[1:, 0] = rgba_colors[0, 0]
  1245. rgba_colors[1:, 1] = rgba_colors[0, 1]
  1246. rgba_colors[1:, 2] = rgba_colors[0, 2]
  1247. rgba_colors[:, 3] = list(islice(cycle(alpha), len(rgba_colors)))
  1248. except TypeError:
  1249. rgba_colors[:, -1] = alpha
  1250. return rgba_colors