Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/networkx/drawing/nx_agraph.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
| author | shellac |
|---|---|
| date | Mon, 22 Mar 2021 18:12:50 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:4f3585e2f14b |
|---|---|
| 1 """ | |
| 2 *************** | |
| 3 Graphviz AGraph | |
| 4 *************** | |
| 5 | |
| 6 Interface to pygraphviz AGraph class. | |
| 7 | |
| 8 Examples | |
| 9 -------- | |
| 10 >>> G = nx.complete_graph(5) | |
| 11 >>> A = nx.nx_agraph.to_agraph(G) | |
| 12 >>> H = nx.nx_agraph.from_agraph(A) | |
| 13 | |
| 14 See Also | |
| 15 -------- | |
| 16 Pygraphviz: http://pygraphviz.github.io/ | |
| 17 """ | |
| 18 import os | |
| 19 import tempfile | |
| 20 import networkx as nx | |
| 21 | |
| 22 __all__ = [ | |
| 23 "from_agraph", | |
| 24 "to_agraph", | |
| 25 "write_dot", | |
| 26 "read_dot", | |
| 27 "graphviz_layout", | |
| 28 "pygraphviz_layout", | |
| 29 "view_pygraphviz", | |
| 30 ] | |
| 31 | |
| 32 | |
| 33 def from_agraph(A, create_using=None): | |
| 34 """Returns a NetworkX Graph or DiGraph from a PyGraphviz graph. | |
| 35 | |
| 36 Parameters | |
| 37 ---------- | |
| 38 A : PyGraphviz AGraph | |
| 39 A graph created with PyGraphviz | |
| 40 | |
| 41 create_using : NetworkX graph constructor, optional (default=None) | |
| 42 Graph type to create. If graph instance, then cleared before populated. | |
| 43 If `None`, then the appropriate Graph type is inferred from `A`. | |
| 44 | |
| 45 Examples | |
| 46 -------- | |
| 47 >>> K5 = nx.complete_graph(5) | |
| 48 >>> A = nx.nx_agraph.to_agraph(K5) | |
| 49 >>> G = nx.nx_agraph.from_agraph(A) | |
| 50 | |
| 51 Notes | |
| 52 ----- | |
| 53 The Graph G will have a dictionary G.graph_attr containing | |
| 54 the default graphviz attributes for graphs, nodes and edges. | |
| 55 | |
| 56 Default node attributes will be in the dictionary G.node_attr | |
| 57 which is keyed by node. | |
| 58 | |
| 59 Edge attributes will be returned as edge data in G. With | |
| 60 edge_attr=False the edge data will be the Graphviz edge weight | |
| 61 attribute or the value 1 if no edge weight attribute is found. | |
| 62 | |
| 63 """ | |
| 64 if create_using is None: | |
| 65 if A.is_directed(): | |
| 66 if A.is_strict(): | |
| 67 create_using = nx.DiGraph | |
| 68 else: | |
| 69 create_using = nx.MultiDiGraph | |
| 70 else: | |
| 71 if A.is_strict(): | |
| 72 create_using = nx.Graph | |
| 73 else: | |
| 74 create_using = nx.MultiGraph | |
| 75 | |
| 76 # assign defaults | |
| 77 N = nx.empty_graph(0, create_using) | |
| 78 if A.name is not None: | |
| 79 N.name = A.name | |
| 80 | |
| 81 # add graph attributes | |
| 82 N.graph.update(A.graph_attr) | |
| 83 | |
| 84 # add nodes, attributes to N.node_attr | |
| 85 for n in A.nodes(): | |
| 86 str_attr = {str(k): v for k, v in n.attr.items()} | |
| 87 N.add_node(str(n), **str_attr) | |
| 88 | |
| 89 # add edges, assign edge data as dictionary of attributes | |
| 90 for e in A.edges(): | |
| 91 u, v = str(e[0]), str(e[1]) | |
| 92 attr = dict(e.attr) | |
| 93 str_attr = {str(k): v for k, v in attr.items()} | |
| 94 if not N.is_multigraph(): | |
| 95 if e.name is not None: | |
| 96 str_attr["key"] = e.name | |
| 97 N.add_edge(u, v, **str_attr) | |
| 98 else: | |
| 99 N.add_edge(u, v, key=e.name, **str_attr) | |
| 100 | |
| 101 # add default attributes for graph, nodes, and edges | |
| 102 # hang them on N.graph_attr | |
| 103 N.graph["graph"] = dict(A.graph_attr) | |
| 104 N.graph["node"] = dict(A.node_attr) | |
| 105 N.graph["edge"] = dict(A.edge_attr) | |
| 106 return N | |
| 107 | |
| 108 | |
| 109 def to_agraph(N): | |
| 110 """Returns a pygraphviz graph from a NetworkX graph N. | |
| 111 | |
| 112 Parameters | |
| 113 ---------- | |
| 114 N : NetworkX graph | |
| 115 A graph created with NetworkX | |
| 116 | |
| 117 Examples | |
| 118 -------- | |
| 119 >>> K5 = nx.complete_graph(5) | |
| 120 >>> A = nx.nx_agraph.to_agraph(K5) | |
| 121 | |
| 122 Notes | |
| 123 ----- | |
| 124 If N has an dict N.graph_attr an attempt will be made first | |
| 125 to copy properties attached to the graph (see from_agraph) | |
| 126 and then updated with the calling arguments if any. | |
| 127 | |
| 128 """ | |
| 129 try: | |
| 130 import pygraphviz | |
| 131 except ImportError as e: | |
| 132 raise ImportError("requires pygraphviz " "http://pygraphviz.github.io/") from e | |
| 133 directed = N.is_directed() | |
| 134 strict = nx.number_of_selfloops(N) == 0 and not N.is_multigraph() | |
| 135 A = pygraphviz.AGraph(name=N.name, strict=strict, directed=directed) | |
| 136 | |
| 137 # default graph attributes | |
| 138 A.graph_attr.update(N.graph.get("graph", {})) | |
| 139 A.node_attr.update(N.graph.get("node", {})) | |
| 140 A.edge_attr.update(N.graph.get("edge", {})) | |
| 141 | |
| 142 A.graph_attr.update( | |
| 143 (k, v) for k, v in N.graph.items() if k not in ("graph", "node", "edge") | |
| 144 ) | |
| 145 | |
| 146 # add nodes | |
| 147 for n, nodedata in N.nodes(data=True): | |
| 148 A.add_node(n) | |
| 149 # Add node data | |
| 150 a = A.get_node(n) | |
| 151 a.attr.update({k: str(v) for k, v in nodedata.items()}) | |
| 152 | |
| 153 # loop over edges | |
| 154 if N.is_multigraph(): | |
| 155 for u, v, key, edgedata in N.edges(data=True, keys=True): | |
| 156 str_edgedata = {k: str(v) for k, v in edgedata.items() if k != "key"} | |
| 157 A.add_edge(u, v, key=str(key)) | |
| 158 # Add edge data | |
| 159 a = A.get_edge(u, v) | |
| 160 a.attr.update(str_edgedata) | |
| 161 | |
| 162 else: | |
| 163 for u, v, edgedata in N.edges(data=True): | |
| 164 str_edgedata = {k: str(v) for k, v in edgedata.items()} | |
| 165 A.add_edge(u, v) | |
| 166 # Add edge data | |
| 167 a = A.get_edge(u, v) | |
| 168 a.attr.update(str_edgedata) | |
| 169 | |
| 170 return A | |
| 171 | |
| 172 | |
| 173 def write_dot(G, path): | |
| 174 """Write NetworkX graph G to Graphviz dot format on path. | |
| 175 | |
| 176 Parameters | |
| 177 ---------- | |
| 178 G : graph | |
| 179 A networkx graph | |
| 180 path : filename | |
| 181 Filename or file handle to write | |
| 182 """ | |
| 183 A = to_agraph(G) | |
| 184 A.write(path) | |
| 185 A.clear() | |
| 186 return | |
| 187 | |
| 188 | |
| 189 def read_dot(path): | |
| 190 """Returns a NetworkX graph from a dot file on path. | |
| 191 | |
| 192 Parameters | |
| 193 ---------- | |
| 194 path : file or string | |
| 195 File name or file handle to read. | |
| 196 """ | |
| 197 try: | |
| 198 import pygraphviz | |
| 199 except ImportError as e: | |
| 200 raise ImportError( | |
| 201 "read_dot() requires pygraphviz " "http://pygraphviz.github.io/" | |
| 202 ) from e | |
| 203 A = pygraphviz.AGraph(file=path) | |
| 204 gr = from_agraph(A) | |
| 205 A.clear() | |
| 206 return gr | |
| 207 | |
| 208 | |
| 209 def graphviz_layout(G, prog="neato", root=None, args=""): | |
| 210 """Create node positions for G using Graphviz. | |
| 211 | |
| 212 Parameters | |
| 213 ---------- | |
| 214 G : NetworkX graph | |
| 215 A graph created with NetworkX | |
| 216 prog : string | |
| 217 Name of Graphviz layout program | |
| 218 root : string, optional | |
| 219 Root node for twopi layout | |
| 220 args : string, optional | |
| 221 Extra arguments to Graphviz layout program | |
| 222 | |
| 223 Returns | |
| 224 ------- | |
| 225 Dictionary of x, y, positions keyed by node. | |
| 226 | |
| 227 Examples | |
| 228 -------- | |
| 229 >>> G = nx.petersen_graph() | |
| 230 >>> pos = nx.nx_agraph.graphviz_layout(G) | |
| 231 >>> pos = nx.nx_agraph.graphviz_layout(G, prog="dot") | |
| 232 | |
| 233 Notes | |
| 234 ----- | |
| 235 This is a wrapper for pygraphviz_layout. | |
| 236 """ | |
| 237 return pygraphviz_layout(G, prog=prog, root=root, args=args) | |
| 238 | |
| 239 | |
| 240 def pygraphviz_layout(G, prog="neato", root=None, args=""): | |
| 241 """Create node positions for G using Graphviz. | |
| 242 | |
| 243 Parameters | |
| 244 ---------- | |
| 245 G : NetworkX graph | |
| 246 A graph created with NetworkX | |
| 247 prog : string | |
| 248 Name of Graphviz layout program | |
| 249 root : string, optional | |
| 250 Root node for twopi layout | |
| 251 args : string, optional | |
| 252 Extra arguments to Graphviz layout program | |
| 253 | |
| 254 Returns | |
| 255 ------- | |
| 256 node_pos : dict | |
| 257 Dictionary of x, y, positions keyed by node. | |
| 258 | |
| 259 Examples | |
| 260 -------- | |
| 261 >>> G = nx.petersen_graph() | |
| 262 >>> pos = nx.nx_agraph.graphviz_layout(G) | |
| 263 >>> pos = nx.nx_agraph.graphviz_layout(G, prog="dot") | |
| 264 | |
| 265 Notes | |
| 266 ----- | |
| 267 If you use complex node objects, they may have the same string | |
| 268 representation and GraphViz could treat them as the same node. | |
| 269 The layout may assign both nodes a single location. See Issue #1568 | |
| 270 If this occurs in your case, consider relabeling the nodes just | |
| 271 for the layout computation using something similar to:: | |
| 272 | |
| 273 >>> H = nx.convert_node_labels_to_integers(G, label_attribute="node_label") | |
| 274 >>> H_layout = nx.nx_agraph.pygraphviz_layout(G, prog="dot") | |
| 275 >>> G_layout = {H.nodes[n]["node_label"]: p for n, p in H_layout.items()} | |
| 276 | |
| 277 """ | |
| 278 try: | |
| 279 import pygraphviz | |
| 280 except ImportError as e: | |
| 281 raise ImportError("requires pygraphviz " "http://pygraphviz.github.io/") from e | |
| 282 if root is not None: | |
| 283 args += f"-Groot={root}" | |
| 284 A = to_agraph(G) | |
| 285 A.layout(prog=prog, args=args) | |
| 286 node_pos = {} | |
| 287 for n in G: | |
| 288 node = pygraphviz.Node(A, n) | |
| 289 try: | |
| 290 xs = node.attr["pos"].split(",") | |
| 291 node_pos[n] = tuple(float(x) for x in xs) | |
| 292 except: | |
| 293 print("no position for node", n) | |
| 294 node_pos[n] = (0.0, 0.0) | |
| 295 return node_pos | |
| 296 | |
| 297 | |
| 298 @nx.utils.open_file(5, "w+b") | |
| 299 def view_pygraphviz( | |
| 300 G, edgelabel=None, prog="dot", args="", suffix="", path=None, show=True | |
| 301 ): | |
| 302 """Views the graph G using the specified layout algorithm. | |
| 303 | |
| 304 Parameters | |
| 305 ---------- | |
| 306 G : NetworkX graph | |
| 307 The machine to draw. | |
| 308 edgelabel : str, callable, None | |
| 309 If a string, then it specifes the edge attribute to be displayed | |
| 310 on the edge labels. If a callable, then it is called for each | |
| 311 edge and it should return the string to be displayed on the edges. | |
| 312 The function signature of `edgelabel` should be edgelabel(data), | |
| 313 where `data` is the edge attribute dictionary. | |
| 314 prog : string | |
| 315 Name of Graphviz layout program. | |
| 316 args : str | |
| 317 Additional arguments to pass to the Graphviz layout program. | |
| 318 suffix : str | |
| 319 If `filename` is None, we save to a temporary file. The value of | |
| 320 `suffix` will appear at the tail end of the temporary filename. | |
| 321 path : str, None | |
| 322 The filename used to save the image. If None, save to a temporary | |
| 323 file. File formats are the same as those from pygraphviz.agraph.draw. | |
| 324 show : bool, default = True | |
| 325 Whether to display the graph with `networkx.utils.default_opener`, | |
| 326 default is `True`. If `False`, the rendered graph is still available | |
| 327 at `path`. | |
| 328 | |
| 329 Returns | |
| 330 ------- | |
| 331 path : str | |
| 332 The filename of the generated image. | |
| 333 A : PyGraphviz graph | |
| 334 The PyGraphviz graph instance used to generate the image. | |
| 335 | |
| 336 Notes | |
| 337 ----- | |
| 338 If this function is called in succession too quickly, sometimes the | |
| 339 image is not displayed. So you might consider time.sleep(.5) between | |
| 340 calls if you experience problems. | |
| 341 | |
| 342 """ | |
| 343 if not len(G): | |
| 344 raise nx.NetworkXException("An empty graph cannot be drawn.") | |
| 345 | |
| 346 # If we are providing default values for graphviz, these must be set | |
| 347 # before any nodes or edges are added to the PyGraphviz graph object. | |
| 348 # The reason for this is that default values only affect incoming objects. | |
| 349 # If you change the default values after the objects have been added, | |
| 350 # then they inherit no value and are set only if explicitly set. | |
| 351 | |
| 352 # to_agraph() uses these values. | |
| 353 attrs = ["edge", "node", "graph"] | |
| 354 for attr in attrs: | |
| 355 if attr not in G.graph: | |
| 356 G.graph[attr] = {} | |
| 357 | |
| 358 # These are the default values. | |
| 359 edge_attrs = {"fontsize": "10"} | |
| 360 node_attrs = { | |
| 361 "style": "filled", | |
| 362 "fillcolor": "#0000FF40", | |
| 363 "height": "0.75", | |
| 364 "width": "0.75", | |
| 365 "shape": "circle", | |
| 366 } | |
| 367 graph_attrs = {} | |
| 368 | |
| 369 def update_attrs(which, attrs): | |
| 370 # Update graph attributes. Return list of those which were added. | |
| 371 added = [] | |
| 372 for k, v in attrs.items(): | |
| 373 if k not in G.graph[which]: | |
| 374 G.graph[which][k] = v | |
| 375 added.append(k) | |
| 376 | |
| 377 def clean_attrs(which, added): | |
| 378 # Remove added attributes | |
| 379 for attr in added: | |
| 380 del G.graph[which][attr] | |
| 381 if not G.graph[which]: | |
| 382 del G.graph[which] | |
| 383 | |
| 384 # Update all default values | |
| 385 update_attrs("edge", edge_attrs) | |
| 386 update_attrs("node", node_attrs) | |
| 387 update_attrs("graph", graph_attrs) | |
| 388 | |
| 389 # Convert to agraph, so we inherit default values | |
| 390 A = to_agraph(G) | |
| 391 | |
| 392 # Remove the default values we added to the original graph. | |
| 393 clean_attrs("edge", edge_attrs) | |
| 394 clean_attrs("node", node_attrs) | |
| 395 clean_attrs("graph", graph_attrs) | |
| 396 | |
| 397 # If the user passed in an edgelabel, we update the labels for all edges. | |
| 398 if edgelabel is not None: | |
| 399 if not hasattr(edgelabel, "__call__"): | |
| 400 | |
| 401 def func(data): | |
| 402 return "".join([" ", str(data[edgelabel]), " "]) | |
| 403 | |
| 404 else: | |
| 405 func = edgelabel | |
| 406 | |
| 407 # update all the edge labels | |
| 408 if G.is_multigraph(): | |
| 409 for u, v, key, data in G.edges(keys=True, data=True): | |
| 410 # PyGraphviz doesn't convert the key to a string. See #339 | |
| 411 edge = A.get_edge(u, v, str(key)) | |
| 412 edge.attr["label"] = str(func(data)) | |
| 413 else: | |
| 414 for u, v, data in G.edges(data=True): | |
| 415 edge = A.get_edge(u, v) | |
| 416 edge.attr["label"] = str(func(data)) | |
| 417 | |
| 418 if path is None: | |
| 419 ext = "png" | |
| 420 if suffix: | |
| 421 suffix = f"_{suffix}.{ext}" | |
| 422 else: | |
| 423 suffix = f".{ext}" | |
| 424 path = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) | |
| 425 else: | |
| 426 # Assume the decorator worked and it is a file-object. | |
| 427 pass | |
| 428 | |
| 429 # Write graph to file | |
| 430 A.draw(path=path, format=None, prog=prog, args=args) | |
| 431 path.close() | |
| 432 | |
| 433 # Show graph in a new window (depends on platform configuration) | |
| 434 if show: | |
| 435 nx.utils.default_opener(path.name) | |
| 436 | |
| 437 return path.name, A | |
| 438 | |
| 439 | |
| 440 def display_pygraphviz(graph, path, format=None, prog=None, args=""): | |
| 441 """Internal function to display a graph in OS dependent manner. | |
| 442 | |
| 443 Parameters | |
| 444 ---------- | |
| 445 graph : PyGraphviz graph | |
| 446 A PyGraphviz AGraph instance. | |
| 447 path : file object | |
| 448 An already opened file object that will be closed. | |
| 449 format : str, None | |
| 450 An attempt is made to guess the output format based on the extension | |
| 451 of the filename. If that fails, the value of `format` is used. | |
| 452 prog : string | |
| 453 Name of Graphviz layout program. | |
| 454 args : str | |
| 455 Additional arguments to pass to the Graphviz layout program. | |
| 456 | |
| 457 Notes | |
| 458 ----- | |
| 459 If this function is called in succession too quickly, sometimes the | |
| 460 image is not displayed. So you might consider time.sleep(.5) between | |
| 461 calls if you experience problems. | |
| 462 | |
| 463 """ | |
| 464 import warnings | |
| 465 | |
| 466 warnings.warn( | |
| 467 "display_pygraphviz is deprecated and will be removed in NetworkX 3.0. " | |
| 468 "To view a graph G using pygraphviz, use nx.nx_agraph.view_pygraphviz(G). " | |
| 469 "To view a graph from file, consider nx.utils.default_opener(filename).", | |
| 470 DeprecationWarning, | |
| 471 ) | |
| 472 if format is None: | |
| 473 filename = path.name | |
| 474 format = os.path.splitext(filename)[1].lower()[1:] | |
| 475 if not format: | |
| 476 # Let the draw() function use its default | |
| 477 format = None | |
| 478 | |
| 479 # Save to a file and display in the default viewer. | |
| 480 # We must close the file before viewing it. | |
| 481 graph.draw(path, format, prog, args) | |
| 482 path.close() | |
| 483 nx.utils.default_opener(filename) |
