comparison env/lib/python3.9/site-packages/networkx/classes/graph.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 """Base class for undirected graphs.
2
3 The Graph class allows any hashable object as a node
4 and can associate key/value attribute pairs with each undirected edge.
5
6 Self-loops are allowed but multiple edges are not (see MultiGraph).
7
8 For directed graphs see DiGraph and MultiDiGraph.
9 """
10 from copy import deepcopy
11
12 import networkx as nx
13 from networkx.classes.coreviews import AdjacencyView
14 from networkx.classes.reportviews import NodeView, EdgeView, DegreeView
15 from networkx.exception import NetworkXError
16 import networkx.convert as convert
17
18
19 class Graph:
20 """
21 Base class for undirected graphs.
22
23 A Graph stores nodes and edges with optional data, or attributes.
24
25 Graphs hold undirected edges. Self loops are allowed but multiple
26 (parallel) edges are not.
27
28 Nodes can be arbitrary (hashable) Python objects with optional
29 key/value attributes. By convention `None` is not used as a node.
30
31 Edges are represented as links between nodes with optional
32 key/value attributes.
33
34 Parameters
35 ----------
36 incoming_graph_data : input graph (optional, default: None)
37 Data to initialize graph. If None (default) an empty
38 graph is created. The data can be any format that is supported
39 by the to_networkx_graph() function, currently including edge list,
40 dict of dicts, dict of lists, NetworkX graph, NumPy matrix
41 or 2d ndarray, SciPy sparse matrix, or PyGraphviz graph.
42
43 attr : keyword arguments, optional (default= no attributes)
44 Attributes to add to graph as key=value pairs.
45
46 See Also
47 --------
48 DiGraph
49 MultiGraph
50 MultiDiGraph
51 OrderedGraph
52
53 Examples
54 --------
55 Create an empty graph structure (a "null graph") with no nodes and
56 no edges.
57
58 >>> G = nx.Graph()
59
60 G can be grown in several ways.
61
62 **Nodes:**
63
64 Add one node at a time:
65
66 >>> G.add_node(1)
67
68 Add the nodes from any container (a list, dict, set or
69 even the lines from a file or the nodes from another graph).
70
71 >>> G.add_nodes_from([2, 3])
72 >>> G.add_nodes_from(range(100, 110))
73 >>> H = nx.path_graph(10)
74 >>> G.add_nodes_from(H)
75
76 In addition to strings and integers any hashable Python object
77 (except None) can represent a node, e.g. a customized node object,
78 or even another Graph.
79
80 >>> G.add_node(H)
81
82 **Edges:**
83
84 G can also be grown by adding edges.
85
86 Add one edge,
87
88 >>> G.add_edge(1, 2)
89
90 a list of edges,
91
92 >>> G.add_edges_from([(1, 2), (1, 3)])
93
94 or a collection of edges,
95
96 >>> G.add_edges_from(H.edges)
97
98 If some edges connect nodes not yet in the graph, the nodes
99 are added automatically. There are no errors when adding
100 nodes or edges that already exist.
101
102 **Attributes:**
103
104 Each graph, node, and edge can hold key/value attribute pairs
105 in an associated attribute dictionary (the keys must be hashable).
106 By default these are empty, but can be added or changed using
107 add_edge, add_node or direct manipulation of the attribute
108 dictionaries named graph, node and edge respectively.
109
110 >>> G = nx.Graph(day="Friday")
111 >>> G.graph
112 {'day': 'Friday'}
113
114 Add node attributes using add_node(), add_nodes_from() or G.nodes
115
116 >>> G.add_node(1, time="5pm")
117 >>> G.add_nodes_from([3], time="2pm")
118 >>> G.nodes[1]
119 {'time': '5pm'}
120 >>> G.nodes[1]["room"] = 714 # node must exist already to use G.nodes
121 >>> del G.nodes[1]["room"] # remove attribute
122 >>> list(G.nodes(data=True))
123 [(1, {'time': '5pm'}), (3, {'time': '2pm'})]
124
125 Add edge attributes using add_edge(), add_edges_from(), subscript
126 notation, or G.edges.
127
128 >>> G.add_edge(1, 2, weight=4.7)
129 >>> G.add_edges_from([(3, 4), (4, 5)], color="red")
130 >>> G.add_edges_from([(1, 2, {"color": "blue"}), (2, 3, {"weight": 8})])
131 >>> G[1][2]["weight"] = 4.7
132 >>> G.edges[1, 2]["weight"] = 4
133
134 Warning: we protect the graph data structure by making `G.edges` a
135 read-only dict-like structure. However, you can assign to attributes
136 in e.g. `G.edges[1, 2]`. Thus, use 2 sets of brackets to add/change
137 data attributes: `G.edges[1, 2]['weight'] = 4`
138 (For multigraphs: `MG.edges[u, v, key][name] = value`).
139
140 **Shortcuts:**
141
142 Many common graph features allow python syntax to speed reporting.
143
144 >>> 1 in G # check if node in graph
145 True
146 >>> [n for n in G if n < 3] # iterate through nodes
147 [1, 2]
148 >>> len(G) # number of nodes in graph
149 5
150
151 Often the best way to traverse all edges of a graph is via the neighbors.
152 The neighbors are reported as an adjacency-dict `G.adj` or `G.adjacency()`
153
154 >>> for n, nbrsdict in G.adjacency():
155 ... for nbr, eattr in nbrsdict.items():
156 ... if "weight" in eattr:
157 ... # Do something useful with the edges
158 ... pass
159
160 But the edges() method is often more convenient:
161
162 >>> for u, v, weight in G.edges.data("weight"):
163 ... if weight is not None:
164 ... # Do something useful with the edges
165 ... pass
166
167 **Reporting:**
168
169 Simple graph information is obtained using object-attributes and methods.
170 Reporting typically provides views instead of containers to reduce memory
171 usage. The views update as the graph is updated similarly to dict-views.
172 The objects `nodes`, `edges` and `adj` provide access to data attributes
173 via lookup (e.g. `nodes[n]`, `edges[u, v]`, `adj[u][v]`) and iteration
174 (e.g. `nodes.items()`, `nodes.data('color')`,
175 `nodes.data('color', default='blue')` and similarly for `edges`)
176 Views exist for `nodes`, `edges`, `neighbors()`/`adj` and `degree`.
177
178 For details on these and other miscellaneous methods, see below.
179
180 **Subclasses (Advanced):**
181
182 The Graph class uses a dict-of-dict-of-dict data structure.
183 The outer dict (node_dict) holds adjacency information keyed by node.
184 The next dict (adjlist_dict) represents the adjacency information and holds
185 edge data keyed by neighbor. The inner dict (edge_attr_dict) represents
186 the edge data and holds edge attribute values keyed by attribute names.
187
188 Each of these three dicts can be replaced in a subclass by a user defined
189 dict-like object. In general, the dict-like features should be
190 maintained but extra features can be added. To replace one of the
191 dicts create a new graph class by changing the class(!) variable
192 holding the factory for that dict-like structure. The variable names are
193 node_dict_factory, node_attr_dict_factory, adjlist_inner_dict_factory,
194 adjlist_outer_dict_factory, edge_attr_dict_factory and graph_attr_dict_factory.
195
196 node_dict_factory : function, (default: dict)
197 Factory function to be used to create the dict containing node
198 attributes, keyed by node id.
199 It should require no arguments and return a dict-like object
200
201 node_attr_dict_factory: function, (default: dict)
202 Factory function to be used to create the node attribute
203 dict which holds attribute values keyed by attribute name.
204 It should require no arguments and return a dict-like object
205
206 adjlist_outer_dict_factory : function, (default: dict)
207 Factory function to be used to create the outer-most dict
208 in the data structure that holds adjacency info keyed by node.
209 It should require no arguments and return a dict-like object.
210
211 adjlist_inner_dict_factory : function, (default: dict)
212 Factory function to be used to create the adjacency list
213 dict which holds edge data keyed by neighbor.
214 It should require no arguments and return a dict-like object
215
216 edge_attr_dict_factory : function, (default: dict)
217 Factory function to be used to create the edge attribute
218 dict which holds attribute values keyed by attribute name.
219 It should require no arguments and return a dict-like object.
220
221 graph_attr_dict_factory : function, (default: dict)
222 Factory function to be used to create the graph attribute
223 dict which holds attribute values keyed by attribute name.
224 It should require no arguments and return a dict-like object.
225
226 Typically, if your extension doesn't impact the data structure all
227 methods will inherit without issue except: `to_directed/to_undirected`.
228 By default these methods create a DiGraph/Graph class and you probably
229 want them to create your extension of a DiGraph/Graph. To facilitate
230 this we define two class variables that you can set in your subclass.
231
232 to_directed_class : callable, (default: DiGraph or MultiDiGraph)
233 Class to create a new graph structure in the `to_directed` method.
234 If `None`, a NetworkX class (DiGraph or MultiDiGraph) is used.
235
236 to_undirected_class : callable, (default: Graph or MultiGraph)
237 Class to create a new graph structure in the `to_undirected` method.
238 If `None`, a NetworkX class (Graph or MultiGraph) is used.
239
240 Examples
241 --------
242
243 Create a low memory graph class that effectively disallows edge
244 attributes by using a single attribute dict for all edges.
245 This reduces the memory used, but you lose edge attributes.
246
247 >>> class ThinGraph(nx.Graph):
248 ... all_edge_dict = {"weight": 1}
249 ...
250 ... def single_edge_dict(self):
251 ... return self.all_edge_dict
252 ...
253 ... edge_attr_dict_factory = single_edge_dict
254 >>> G = ThinGraph()
255 >>> G.add_edge(2, 1)
256 >>> G[2][1]
257 {'weight': 1}
258 >>> G.add_edge(2, 2)
259 >>> G[2][1] is G[2][2]
260 True
261
262 Please see :mod:`~networkx.classes.ordered` for more examples of
263 creating graph subclasses by overwriting the base class `dict` with
264 a dictionary-like object.
265 """
266
267 node_dict_factory = dict
268 node_attr_dict_factory = dict
269 adjlist_outer_dict_factory = dict
270 adjlist_inner_dict_factory = dict
271 edge_attr_dict_factory = dict
272 graph_attr_dict_factory = dict
273
274 def to_directed_class(self):
275 """Returns the class to use for empty directed copies.
276
277 If you subclass the base classes, use this to designate
278 what directed class to use for `to_directed()` copies.
279 """
280 return nx.DiGraph
281
282 def to_undirected_class(self):
283 """Returns the class to use for empty undirected copies.
284
285 If you subclass the base classes, use this to designate
286 what directed class to use for `to_directed()` copies.
287 """
288 return Graph
289
290 def __init__(self, incoming_graph_data=None, **attr):
291 """Initialize a graph with edges, name, or graph attributes.
292
293 Parameters
294 ----------
295 incoming_graph_data : input graph (optional, default: None)
296 Data to initialize graph. If None (default) an empty
297 graph is created. The data can be an edge list, or any
298 NetworkX graph object. If the corresponding optional Python
299 packages are installed the data can also be a NumPy matrix
300 or 2d ndarray, a SciPy sparse matrix, or a PyGraphviz graph.
301
302 attr : keyword arguments, optional (default= no attributes)
303 Attributes to add to graph as key=value pairs.
304
305 See Also
306 --------
307 convert
308
309 Examples
310 --------
311 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
312 >>> G = nx.Graph(name="my graph")
313 >>> e = [(1, 2), (2, 3), (3, 4)] # list of edges
314 >>> G = nx.Graph(e)
315
316 Arbitrary graph attribute pairs (key=value) may be assigned
317
318 >>> G = nx.Graph(e, day="Friday")
319 >>> G.graph
320 {'day': 'Friday'}
321
322 """
323 self.graph_attr_dict_factory = self.graph_attr_dict_factory
324 self.node_dict_factory = self.node_dict_factory
325 self.node_attr_dict_factory = self.node_attr_dict_factory
326 self.adjlist_outer_dict_factory = self.adjlist_outer_dict_factory
327 self.adjlist_inner_dict_factory = self.adjlist_inner_dict_factory
328 self.edge_attr_dict_factory = self.edge_attr_dict_factory
329
330 self.graph = self.graph_attr_dict_factory() # dictionary for graph attributes
331 self._node = self.node_dict_factory() # empty node attribute dict
332 self._adj = self.adjlist_outer_dict_factory() # empty adjacency dict
333 # attempt to load graph with data
334 if incoming_graph_data is not None:
335 convert.to_networkx_graph(incoming_graph_data, create_using=self)
336 # load graph attributes (must be after convert)
337 self.graph.update(attr)
338
339 @property
340 def adj(self):
341 """Graph adjacency object holding the neighbors of each node.
342
343 This object is a read-only dict-like structure with node keys
344 and neighbor-dict values. The neighbor-dict is keyed by neighbor
345 to the edge-data-dict. So `G.adj[3][2]['color'] = 'blue'` sets
346 the color of the edge `(3, 2)` to `"blue"`.
347
348 Iterating over G.adj behaves like a dict. Useful idioms include
349 `for nbr, datadict in G.adj[n].items():`.
350
351 The neighbor information is also provided by subscripting the graph.
352 So `for nbr, foovalue in G[node].data('foo', default=1):` works.
353
354 For directed graphs, `G.adj` holds outgoing (successor) info.
355 """
356 return AdjacencyView(self._adj)
357
358 @property
359 def name(self):
360 """String identifier of the graph.
361
362 This graph attribute appears in the attribute dict G.graph
363 keyed by the string `"name"`. as well as an attribute (technically
364 a property) `G.name`. This is entirely user controlled.
365 """
366 return self.graph.get("name", "")
367
368 @name.setter
369 def name(self, s):
370 self.graph["name"] = s
371
372 def __str__(self):
373 """Returns the graph name.
374
375 Returns
376 -------
377 name : string
378 The name of the graph.
379
380 Examples
381 --------
382 >>> G = nx.Graph(name="foo")
383 >>> str(G)
384 'foo'
385 """
386 return self.name
387
388 def __iter__(self):
389 """Iterate over the nodes. Use: 'for n in G'.
390
391 Returns
392 -------
393 niter : iterator
394 An iterator over all nodes in the graph.
395
396 Examples
397 --------
398 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
399 >>> [n for n in G]
400 [0, 1, 2, 3]
401 >>> list(G)
402 [0, 1, 2, 3]
403 """
404 return iter(self._node)
405
406 def __contains__(self, n):
407 """Returns True if n is a node, False otherwise. Use: 'n in G'.
408
409 Examples
410 --------
411 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
412 >>> 1 in G
413 True
414 """
415 try:
416 return n in self._node
417 except TypeError:
418 return False
419
420 def __len__(self):
421 """Returns the number of nodes in the graph. Use: 'len(G)'.
422
423 Returns
424 -------
425 nnodes : int
426 The number of nodes in the graph.
427
428 See Also
429 --------
430 number_of_nodes, order which are identical
431
432 Examples
433 --------
434 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
435 >>> len(G)
436 4
437
438 """
439 return len(self._node)
440
441 def __getitem__(self, n):
442 """Returns a dict of neighbors of node n. Use: 'G[n]'.
443
444 Parameters
445 ----------
446 n : node
447 A node in the graph.
448
449 Returns
450 -------
451 adj_dict : dictionary
452 The adjacency dictionary for nodes connected to n.
453
454 Notes
455 -----
456 G[n] is the same as G.adj[n] and similar to G.neighbors(n)
457 (which is an iterator over G.adj[n])
458
459 Examples
460 --------
461 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
462 >>> G[0]
463 AtlasView({1: {}})
464 """
465 return self.adj[n]
466
467 def add_node(self, node_for_adding, **attr):
468 """Add a single node `node_for_adding` and update node attributes.
469
470 Parameters
471 ----------
472 node_for_adding : node
473 A node can be any hashable Python object except None.
474 attr : keyword arguments, optional
475 Set or change node attributes using key=value.
476
477 See Also
478 --------
479 add_nodes_from
480
481 Examples
482 --------
483 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
484 >>> G.add_node(1)
485 >>> G.add_node("Hello")
486 >>> K3 = nx.Graph([(0, 1), (1, 2), (2, 0)])
487 >>> G.add_node(K3)
488 >>> G.number_of_nodes()
489 3
490
491 Use keywords set/change node attributes:
492
493 >>> G.add_node(1, size=10)
494 >>> G.add_node(3, weight=0.4, UTM=("13S", 382871, 3972649))
495
496 Notes
497 -----
498 A hashable object is one that can be used as a key in a Python
499 dictionary. This includes strings, numbers, tuples of strings
500 and numbers, etc.
501
502 On many platforms hashable items also include mutables such as
503 NetworkX Graphs, though one should be careful that the hash
504 doesn't change on mutables.
505 """
506 if node_for_adding not in self._node:
507 self._adj[node_for_adding] = self.adjlist_inner_dict_factory()
508 attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory()
509 attr_dict.update(attr)
510 else: # update attr even if node already exists
511 self._node[node_for_adding].update(attr)
512
513 def add_nodes_from(self, nodes_for_adding, **attr):
514 """Add multiple nodes.
515
516 Parameters
517 ----------
518 nodes_for_adding : iterable container
519 A container of nodes (list, dict, set, etc.).
520 OR
521 A container of (node, attribute dict) tuples.
522 Node attributes are updated using the attribute dict.
523 attr : keyword arguments, optional (default= no attributes)
524 Update attributes for all nodes in nodes.
525 Node attributes specified in nodes as a tuple take
526 precedence over attributes specified via keyword arguments.
527
528 See Also
529 --------
530 add_node
531
532 Examples
533 --------
534 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
535 >>> G.add_nodes_from("Hello")
536 >>> K3 = nx.Graph([(0, 1), (1, 2), (2, 0)])
537 >>> G.add_nodes_from(K3)
538 >>> sorted(G.nodes(), key=str)
539 [0, 1, 2, 'H', 'e', 'l', 'o']
540
541 Use keywords to update specific node attributes for every node.
542
543 >>> G.add_nodes_from([1, 2], size=10)
544 >>> G.add_nodes_from([3, 4], weight=0.4)
545
546 Use (node, attrdict) tuples to update attributes for specific nodes.
547
548 >>> G.add_nodes_from([(1, dict(size=11)), (2, {"color": "blue"})])
549 >>> G.nodes[1]["size"]
550 11
551 >>> H = nx.Graph()
552 >>> H.add_nodes_from(G.nodes(data=True))
553 >>> H.nodes[1]["size"]
554 11
555
556 """
557 for n in nodes_for_adding:
558 # keep all this inside try/except because
559 # CPython throws TypeError on n not in self._node,
560 # while pre-2.7.5 ironpython throws on self._adj[n]
561 try:
562 if n not in self._node:
563 self._adj[n] = self.adjlist_inner_dict_factory()
564 attr_dict = self._node[n] = self.node_attr_dict_factory()
565 attr_dict.update(attr)
566 else:
567 self._node[n].update(attr)
568 except TypeError:
569 nn, ndict = n
570 if nn not in self._node:
571 self._adj[nn] = self.adjlist_inner_dict_factory()
572 newdict = attr.copy()
573 newdict.update(ndict)
574 attr_dict = self._node[nn] = self.node_attr_dict_factory()
575 attr_dict.update(newdict)
576 else:
577 olddict = self._node[nn]
578 olddict.update(attr)
579 olddict.update(ndict)
580
581 def remove_node(self, n):
582 """Remove node n.
583
584 Removes the node n and all adjacent edges.
585 Attempting to remove a non-existent node will raise an exception.
586
587 Parameters
588 ----------
589 n : node
590 A node in the graph
591
592 Raises
593 -------
594 NetworkXError
595 If n is not in the graph.
596
597 See Also
598 --------
599 remove_nodes_from
600
601 Examples
602 --------
603 >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc
604 >>> list(G.edges)
605 [(0, 1), (1, 2)]
606 >>> G.remove_node(1)
607 >>> list(G.edges)
608 []
609
610 """
611 adj = self._adj
612 try:
613 nbrs = list(adj[n]) # list handles self-loops (allows mutation)
614 del self._node[n]
615 except KeyError as e: # NetworkXError if n not in self
616 raise NetworkXError(f"The node {n} is not in the graph.") from e
617 for u in nbrs:
618 del adj[u][n] # remove all edges n-u in graph
619 del adj[n] # now remove node
620
621 def remove_nodes_from(self, nodes):
622 """Remove multiple nodes.
623
624 Parameters
625 ----------
626 nodes : iterable container
627 A container of nodes (list, dict, set, etc.). If a node
628 in the container is not in the graph it is silently
629 ignored.
630
631 See Also
632 --------
633 remove_node
634
635 Examples
636 --------
637 >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc
638 >>> e = list(G.nodes)
639 >>> e
640 [0, 1, 2]
641 >>> G.remove_nodes_from(e)
642 >>> list(G.nodes)
643 []
644
645 """
646 adj = self._adj
647 for n in nodes:
648 try:
649 del self._node[n]
650 for u in list(adj[n]): # list handles self-loops
651 del adj[u][n] # (allows mutation of dict in loop)
652 del adj[n]
653 except KeyError:
654 pass
655
656 @property
657 def nodes(self):
658 """A NodeView of the Graph as G.nodes or G.nodes().
659
660 Can be used as `G.nodes` for data lookup and for set-like operations.
661 Can also be used as `G.nodes(data='color', default=None)` to return a
662 NodeDataView which reports specific node data but no set operations.
663 It presents a dict-like interface as well with `G.nodes.items()`
664 iterating over `(node, nodedata)` 2-tuples and `G.nodes[3]['foo']`
665 providing the value of the `foo` attribute for node `3`. In addition,
666 a view `G.nodes.data('foo')` provides a dict-like interface to the
667 `foo` attribute of each node. `G.nodes.data('foo', default=1)`
668 provides a default for nodes that do not have attribute `foo`.
669
670 Parameters
671 ----------
672 data : string or bool, optional (default=False)
673 The node attribute returned in 2-tuple (n, ddict[data]).
674 If True, return entire node attribute dict as (n, ddict).
675 If False, return just the nodes n.
676
677 default : value, optional (default=None)
678 Value used for nodes that don't have the requested attribute.
679 Only relevant if data is not True or False.
680
681 Returns
682 -------
683 NodeView
684 Allows set-like operations over the nodes as well as node
685 attribute dict lookup and calling to get a NodeDataView.
686 A NodeDataView iterates over `(n, data)` and has no set operations.
687 A NodeView iterates over `n` and includes set operations.
688
689 When called, if data is False, an iterator over nodes.
690 Otherwise an iterator of 2-tuples (node, attribute value)
691 where the attribute is specified in `data`.
692 If data is True then the attribute becomes the
693 entire data dictionary.
694
695 Notes
696 -----
697 If your node data is not needed, it is simpler and equivalent
698 to use the expression ``for n in G``, or ``list(G)``.
699
700 Examples
701 --------
702 There are two simple ways of getting a list of all nodes in the graph:
703
704 >>> G = nx.path_graph(3)
705 >>> list(G.nodes)
706 [0, 1, 2]
707 >>> list(G)
708 [0, 1, 2]
709
710 To get the node data along with the nodes:
711
712 >>> G.add_node(1, time="5pm")
713 >>> G.nodes[0]["foo"] = "bar"
714 >>> list(G.nodes(data=True))
715 [(0, {'foo': 'bar'}), (1, {'time': '5pm'}), (2, {})]
716 >>> list(G.nodes.data())
717 [(0, {'foo': 'bar'}), (1, {'time': '5pm'}), (2, {})]
718
719 >>> list(G.nodes(data="foo"))
720 [(0, 'bar'), (1, None), (2, None)]
721 >>> list(G.nodes.data("foo"))
722 [(0, 'bar'), (1, None), (2, None)]
723
724 >>> list(G.nodes(data="time"))
725 [(0, None), (1, '5pm'), (2, None)]
726 >>> list(G.nodes.data("time"))
727 [(0, None), (1, '5pm'), (2, None)]
728
729 >>> list(G.nodes(data="time", default="Not Available"))
730 [(0, 'Not Available'), (1, '5pm'), (2, 'Not Available')]
731 >>> list(G.nodes.data("time", default="Not Available"))
732 [(0, 'Not Available'), (1, '5pm'), (2, 'Not Available')]
733
734 If some of your nodes have an attribute and the rest are assumed
735 to have a default attribute value you can create a dictionary
736 from node/attribute pairs using the `default` keyword argument
737 to guarantee the value is never None::
738
739 >>> G = nx.Graph()
740 >>> G.add_node(0)
741 >>> G.add_node(1, weight=2)
742 >>> G.add_node(2, weight=3)
743 >>> dict(G.nodes(data="weight", default=1))
744 {0: 1, 1: 2, 2: 3}
745
746 """
747 nodes = NodeView(self)
748 # Lazy View creation: overload the (class) property on the instance
749 # Then future G.nodes use the existing View
750 # setattr doesn't work because attribute already exists
751 self.__dict__["nodes"] = nodes
752 return nodes
753
754 def number_of_nodes(self):
755 """Returns the number of nodes in the graph.
756
757 Returns
758 -------
759 nnodes : int
760 The number of nodes in the graph.
761
762 See Also
763 --------
764 order, __len__ which are identical
765
766 Examples
767 --------
768 >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc
769 >>> G.number_of_nodes()
770 3
771 """
772 return len(self._node)
773
774 def order(self):
775 """Returns the number of nodes in the graph.
776
777 Returns
778 -------
779 nnodes : int
780 The number of nodes in the graph.
781
782 See Also
783 --------
784 number_of_nodes, __len__ which are identical
785
786 Examples
787 --------
788 >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc
789 >>> G.order()
790 3
791 """
792 return len(self._node)
793
794 def has_node(self, n):
795 """Returns True if the graph contains the node n.
796
797 Identical to `n in G`
798
799 Parameters
800 ----------
801 n : node
802
803 Examples
804 --------
805 >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc
806 >>> G.has_node(0)
807 True
808
809 It is more readable and simpler to use
810
811 >>> 0 in G
812 True
813
814 """
815 try:
816 return n in self._node
817 except TypeError:
818 return False
819
820 def add_edge(self, u_of_edge, v_of_edge, **attr):
821 """Add an edge between u and v.
822
823 The nodes u and v will be automatically added if they are
824 not already in the graph.
825
826 Edge attributes can be specified with keywords or by directly
827 accessing the edge's attribute dictionary. See examples below.
828
829 Parameters
830 ----------
831 u, v : nodes
832 Nodes can be, for example, strings or numbers.
833 Nodes must be hashable (and not None) Python objects.
834 attr : keyword arguments, optional
835 Edge data (or labels or objects) can be assigned using
836 keyword arguments.
837
838 See Also
839 --------
840 add_edges_from : add a collection of edges
841
842 Notes
843 -----
844 Adding an edge that already exists updates the edge data.
845
846 Many NetworkX algorithms designed for weighted graphs use
847 an edge attribute (by default `weight`) to hold a numerical value.
848
849 Examples
850 --------
851 The following all add the edge e=(1, 2) to graph G:
852
853 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
854 >>> e = (1, 2)
855 >>> G.add_edge(1, 2) # explicit two-node form
856 >>> G.add_edge(*e) # single edge as tuple of two nodes
857 >>> G.add_edges_from([(1, 2)]) # add edges from iterable container
858
859 Associate data to edges using keywords:
860
861 >>> G.add_edge(1, 2, weight=3)
862 >>> G.add_edge(1, 3, weight=7, capacity=15, length=342.7)
863
864 For non-string attribute keys, use subscript notation.
865
866 >>> G.add_edge(1, 2)
867 >>> G[1][2].update({0: 5})
868 >>> G.edges[1, 2].update({0: 5})
869 """
870 u, v = u_of_edge, v_of_edge
871 # add nodes
872 if u not in self._node:
873 self._adj[u] = self.adjlist_inner_dict_factory()
874 self._node[u] = self.node_attr_dict_factory()
875 if v not in self._node:
876 self._adj[v] = self.adjlist_inner_dict_factory()
877 self._node[v] = self.node_attr_dict_factory()
878 # add the edge
879 datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
880 datadict.update(attr)
881 self._adj[u][v] = datadict
882 self._adj[v][u] = datadict
883
884 def add_edges_from(self, ebunch_to_add, **attr):
885 """Add all the edges in ebunch_to_add.
886
887 Parameters
888 ----------
889 ebunch_to_add : container of edges
890 Each edge given in the container will be added to the
891 graph. The edges must be given as as 2-tuples (u, v) or
892 3-tuples (u, v, d) where d is a dictionary containing edge data.
893 attr : keyword arguments, optional
894 Edge data (or labels or objects) can be assigned using
895 keyword arguments.
896
897 See Also
898 --------
899 add_edge : add a single edge
900 add_weighted_edges_from : convenient way to add weighted edges
901
902 Notes
903 -----
904 Adding the same edge twice has no effect but any edge data
905 will be updated when each duplicate edge is added.
906
907 Edge attributes specified in an ebunch take precedence over
908 attributes specified via keyword arguments.
909
910 Examples
911 --------
912 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
913 >>> G.add_edges_from([(0, 1), (1, 2)]) # using a list of edge tuples
914 >>> e = zip(range(0, 3), range(1, 4))
915 >>> G.add_edges_from(e) # Add the path graph 0-1-2-3
916
917 Associate data to edges
918
919 >>> G.add_edges_from([(1, 2), (2, 3)], weight=3)
920 >>> G.add_edges_from([(3, 4), (1, 4)], label="WN2898")
921 """
922 for e in ebunch_to_add:
923 ne = len(e)
924 if ne == 3:
925 u, v, dd = e
926 elif ne == 2:
927 u, v = e
928 dd = {} # doesn't need edge_attr_dict_factory
929 else:
930 raise NetworkXError(f"Edge tuple {e} must be a 2-tuple or 3-tuple.")
931 if u not in self._node:
932 self._adj[u] = self.adjlist_inner_dict_factory()
933 self._node[u] = self.node_attr_dict_factory()
934 if v not in self._node:
935 self._adj[v] = self.adjlist_inner_dict_factory()
936 self._node[v] = self.node_attr_dict_factory()
937 datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
938 datadict.update(attr)
939 datadict.update(dd)
940 self._adj[u][v] = datadict
941 self._adj[v][u] = datadict
942
943 def add_weighted_edges_from(self, ebunch_to_add, weight="weight", **attr):
944 """Add weighted edges in `ebunch_to_add` with specified weight attr
945
946 Parameters
947 ----------
948 ebunch_to_add : container of edges
949 Each edge given in the list or container will be added
950 to the graph. The edges must be given as 3-tuples (u, v, w)
951 where w is a number.
952 weight : string, optional (default= 'weight')
953 The attribute name for the edge weights to be added.
954 attr : keyword arguments, optional (default= no attributes)
955 Edge attributes to add/update for all edges.
956
957 See Also
958 --------
959 add_edge : add a single edge
960 add_edges_from : add multiple edges
961
962 Notes
963 -----
964 Adding the same edge twice for Graph/DiGraph simply updates
965 the edge data. For MultiGraph/MultiDiGraph, duplicate edges
966 are stored.
967
968 Examples
969 --------
970 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
971 >>> G.add_weighted_edges_from([(0, 1, 3.0), (1, 2, 7.5)])
972 """
973 self.add_edges_from(((u, v, {weight: d}) for u, v, d in ebunch_to_add), **attr)
974
975 def remove_edge(self, u, v):
976 """Remove the edge between u and v.
977
978 Parameters
979 ----------
980 u, v : nodes
981 Remove the edge between nodes u and v.
982
983 Raises
984 ------
985 NetworkXError
986 If there is not an edge between u and v.
987
988 See Also
989 --------
990 remove_edges_from : remove a collection of edges
991
992 Examples
993 --------
994 >>> G = nx.path_graph(4) # or DiGraph, etc
995 >>> G.remove_edge(0, 1)
996 >>> e = (1, 2)
997 >>> G.remove_edge(*e) # unpacks e from an edge tuple
998 >>> e = (2, 3, {"weight": 7}) # an edge with attribute data
999 >>> G.remove_edge(*e[:2]) # select first part of edge tuple
1000 """
1001 try:
1002 del self._adj[u][v]
1003 if u != v: # self-loop needs only one entry removed
1004 del self._adj[v][u]
1005 except KeyError as e:
1006 raise NetworkXError(f"The edge {u}-{v} is not in the graph") from e
1007
1008 def remove_edges_from(self, ebunch):
1009 """Remove all edges specified in ebunch.
1010
1011 Parameters
1012 ----------
1013 ebunch: list or container of edge tuples
1014 Each edge given in the list or container will be removed
1015 from the graph. The edges can be:
1016
1017 - 2-tuples (u, v) edge between u and v.
1018 - 3-tuples (u, v, k) where k is ignored.
1019
1020 See Also
1021 --------
1022 remove_edge : remove a single edge
1023
1024 Notes
1025 -----
1026 Will fail silently if an edge in ebunch is not in the graph.
1027
1028 Examples
1029 --------
1030 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1031 >>> ebunch = [(1, 2), (2, 3)]
1032 >>> G.remove_edges_from(ebunch)
1033 """
1034 adj = self._adj
1035 for e in ebunch:
1036 u, v = e[:2] # ignore edge data if present
1037 if u in adj and v in adj[u]:
1038 del adj[u][v]
1039 if u != v: # self loop needs only one entry removed
1040 del adj[v][u]
1041
1042 def update(self, edges=None, nodes=None):
1043 """Update the graph using nodes/edges/graphs as input.
1044
1045 Like dict.update, this method takes a graph as input, adding the
1046 graph's nodes and edges to this graph. It can also take two inputs:
1047 edges and nodes. Finally it can take either edges or nodes.
1048 To specify only nodes the keyword `nodes` must be used.
1049
1050 The collections of edges and nodes are treated similarly to
1051 the add_edges_from/add_nodes_from methods. When iterated, they
1052 should yield 2-tuples (u, v) or 3-tuples (u, v, datadict).
1053
1054 Parameters
1055 ----------
1056 edges : Graph object, collection of edges, or None
1057 The first parameter can be a graph or some edges. If it has
1058 attributes `nodes` and `edges`, then it is taken to be a
1059 Graph-like object and those attributes are used as collections
1060 of nodes and edges to be added to the graph.
1061 If the first parameter does not have those attributes, it is
1062 treated as a collection of edges and added to the graph.
1063 If the first argument is None, no edges are added.
1064 nodes : collection of nodes, or None
1065 The second parameter is treated as a collection of nodes
1066 to be added to the graph unless it is None.
1067 If `edges is None` and `nodes is None` an exception is raised.
1068 If the first parameter is a Graph, then `nodes` is ignored.
1069
1070 Examples
1071 --------
1072 >>> G = nx.path_graph(5)
1073 >>> G.update(nx.complete_graph(range(4, 10)))
1074 >>> from itertools import combinations
1075 >>> edges = (
1076 ... (u, v, {"power": u * v})
1077 ... for u, v in combinations(range(10, 20), 2)
1078 ... if u * v < 225
1079 ... )
1080 >>> nodes = [1000] # for singleton, use a container
1081 >>> G.update(edges, nodes)
1082
1083 Notes
1084 -----
1085 It you want to update the graph using an adjacency structure
1086 it is straightforward to obtain the edges/nodes from adjacency.
1087 The following examples provide common cases, your adjacency may
1088 be slightly different and require tweaks of these examples.
1089
1090 >>> # dict-of-set/list/tuple
1091 >>> adj = {1: {2, 3}, 2: {1, 3}, 3: {1, 2}}
1092 >>> e = [(u, v) for u, nbrs in adj.items() for v in nbrs]
1093 >>> G.update(edges=e, nodes=adj)
1094
1095 >>> DG = nx.DiGraph()
1096 >>> # dict-of-dict-of-attribute
1097 >>> adj = {1: {2: 1.3, 3: 0.7}, 2: {1: 1.4}, 3: {1: 0.7}}
1098 >>> e = [
1099 ... (u, v, {"weight": d})
1100 ... for u, nbrs in adj.items()
1101 ... for v, d in nbrs.items()
1102 ... ]
1103 >>> DG.update(edges=e, nodes=adj)
1104
1105 >>> # dict-of-dict-of-dict
1106 >>> adj = {1: {2: {"weight": 1.3}, 3: {"color": 0.7, "weight": 1.2}}}
1107 >>> e = [
1108 ... (u, v, {"weight": d})
1109 ... for u, nbrs in adj.items()
1110 ... for v, d in nbrs.items()
1111 ... ]
1112 >>> DG.update(edges=e, nodes=adj)
1113
1114 >>> # predecessor adjacency (dict-of-set)
1115 >>> pred = {1: {2, 3}, 2: {3}, 3: {3}}
1116 >>> e = [(v, u) for u, nbrs in pred.items() for v in nbrs]
1117
1118 >>> # MultiGraph dict-of-dict-of-dict-of-attribute
1119 >>> MDG = nx.MultiDiGraph()
1120 >>> adj = {
1121 ... 1: {2: {0: {"weight": 1.3}, 1: {"weight": 1.2}}},
1122 ... 3: {2: {0: {"weight": 0.7}}},
1123 ... }
1124 >>> e = [
1125 ... (u, v, ekey, d)
1126 ... for u, nbrs in adj.items()
1127 ... for v, keydict in nbrs.items()
1128 ... for ekey, d in keydict.items()
1129 ... ]
1130 >>> MDG.update(edges=e)
1131
1132 See Also
1133 --------
1134 add_edges_from: add multiple edges to a graph
1135 add_nodes_from: add multiple nodes to a graph
1136 """
1137 if edges is not None:
1138 if nodes is not None:
1139 self.add_nodes_from(nodes)
1140 self.add_edges_from(edges)
1141 else:
1142 # check if edges is a Graph object
1143 try:
1144 graph_nodes = edges.nodes
1145 graph_edges = edges.edges
1146 except AttributeError:
1147 # edge not Graph-like
1148 self.add_edges_from(edges)
1149 else: # edges is Graph-like
1150 self.add_nodes_from(graph_nodes.data())
1151 self.add_edges_from(graph_edges.data())
1152 self.graph.update(edges.graph)
1153 elif nodes is not None:
1154 self.add_nodes_from(nodes)
1155 else:
1156 raise NetworkXError("update needs nodes or edges input")
1157
1158 def has_edge(self, u, v):
1159 """Returns True if the edge (u, v) is in the graph.
1160
1161 This is the same as `v in G[u]` without KeyError exceptions.
1162
1163 Parameters
1164 ----------
1165 u, v : nodes
1166 Nodes can be, for example, strings or numbers.
1167 Nodes must be hashable (and not None) Python objects.
1168
1169 Returns
1170 -------
1171 edge_ind : bool
1172 True if edge is in the graph, False otherwise.
1173
1174 Examples
1175 --------
1176 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1177 >>> G.has_edge(0, 1) # using two nodes
1178 True
1179 >>> e = (0, 1)
1180 >>> G.has_edge(*e) # e is a 2-tuple (u, v)
1181 True
1182 >>> e = (0, 1, {"weight": 7})
1183 >>> G.has_edge(*e[:2]) # e is a 3-tuple (u, v, data_dictionary)
1184 True
1185
1186 The following syntax are equivalent:
1187
1188 >>> G.has_edge(0, 1)
1189 True
1190 >>> 1 in G[0] # though this gives KeyError if 0 not in G
1191 True
1192
1193 """
1194 try:
1195 return v in self._adj[u]
1196 except KeyError:
1197 return False
1198
1199 def neighbors(self, n):
1200 """Returns an iterator over all neighbors of node n.
1201
1202 This is identical to `iter(G[n])`
1203
1204 Parameters
1205 ----------
1206 n : node
1207 A node in the graph
1208
1209 Returns
1210 -------
1211 neighbors : iterator
1212 An iterator over all neighbors of node n
1213
1214 Raises
1215 ------
1216 NetworkXError
1217 If the node n is not in the graph.
1218
1219 Examples
1220 --------
1221 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1222 >>> [n for n in G.neighbors(0)]
1223 [1]
1224
1225 Notes
1226 -----
1227 Alternate ways to access the neighbors are ``G.adj[n]`` or ``G[n]``:
1228
1229 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
1230 >>> G.add_edge("a", "b", weight=7)
1231 >>> G["a"]
1232 AtlasView({'b': {'weight': 7}})
1233 >>> G = nx.path_graph(4)
1234 >>> [n for n in G[0]]
1235 [1]
1236 """
1237 try:
1238 return iter(self._adj[n])
1239 except KeyError as e:
1240 raise NetworkXError(f"The node {n} is not in the graph.") from e
1241
1242 @property
1243 def edges(self):
1244 """An EdgeView of the Graph as G.edges or G.edges().
1245
1246 edges(self, nbunch=None, data=False, default=None)
1247
1248 The EdgeView provides set-like operations on the edge-tuples
1249 as well as edge attribute lookup. When called, it also provides
1250 an EdgeDataView object which allows control of access to edge
1251 attributes (but does not provide set-like operations).
1252 Hence, `G.edges[u, v]['color']` provides the value of the color
1253 attribute for edge `(u, v)` while
1254 `for (u, v, c) in G.edges.data('color', default='red'):`
1255 iterates through all the edges yielding the color attribute
1256 with default `'red'` if no color attribute exists.
1257
1258 Parameters
1259 ----------
1260 nbunch : single node, container, or all nodes (default= all nodes)
1261 The view will only report edges incident to these nodes.
1262 data : string or bool, optional (default=False)
1263 The edge attribute returned in 3-tuple (u, v, ddict[data]).
1264 If True, return edge attribute dict in 3-tuple (u, v, ddict).
1265 If False, return 2-tuple (u, v).
1266 default : value, optional (default=None)
1267 Value used for edges that don't have the requested attribute.
1268 Only relevant if data is not True or False.
1269
1270 Returns
1271 -------
1272 edges : EdgeView
1273 A view of edge attributes, usually it iterates over (u, v)
1274 or (u, v, d) tuples of edges, but can also be used for
1275 attribute lookup as `edges[u, v]['foo']`.
1276
1277 Notes
1278 -----
1279 Nodes in nbunch that are not in the graph will be (quietly) ignored.
1280 For directed graphs this returns the out-edges.
1281
1282 Examples
1283 --------
1284 >>> G = nx.path_graph(3) # or MultiGraph, etc
1285 >>> G.add_edge(2, 3, weight=5)
1286 >>> [e for e in G.edges]
1287 [(0, 1), (1, 2), (2, 3)]
1288 >>> G.edges.data() # default data is {} (empty dict)
1289 EdgeDataView([(0, 1, {}), (1, 2, {}), (2, 3, {'weight': 5})])
1290 >>> G.edges.data("weight", default=1)
1291 EdgeDataView([(0, 1, 1), (1, 2, 1), (2, 3, 5)])
1292 >>> G.edges([0, 3]) # only edges incident to these nodes
1293 EdgeDataView([(0, 1), (3, 2)])
1294 >>> G.edges(0) # only edges incident to a single node (use G.adj[0]?)
1295 EdgeDataView([(0, 1)])
1296 """
1297 return EdgeView(self)
1298
1299 def get_edge_data(self, u, v, default=None):
1300 """Returns the attribute dictionary associated with edge (u, v).
1301
1302 This is identical to `G[u][v]` except the default is returned
1303 instead of an exception if the edge doesn't exist.
1304
1305 Parameters
1306 ----------
1307 u, v : nodes
1308 default: any Python object (default=None)
1309 Value to return if the edge (u, v) is not found.
1310
1311 Returns
1312 -------
1313 edge_dict : dictionary
1314 The edge attribute dictionary.
1315
1316 Examples
1317 --------
1318 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1319 >>> G[0][1]
1320 {}
1321
1322 Warning: Assigning to `G[u][v]` is not permitted.
1323 But it is safe to assign attributes `G[u][v]['foo']`
1324
1325 >>> G[0][1]["weight"] = 7
1326 >>> G[0][1]["weight"]
1327 7
1328 >>> G[1][0]["weight"]
1329 7
1330
1331 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1332 >>> G.get_edge_data(0, 1) # default edge data is {}
1333 {}
1334 >>> e = (0, 1)
1335 >>> G.get_edge_data(*e) # tuple form
1336 {}
1337 >>> G.get_edge_data("a", "b", default=0) # edge not in graph, return 0
1338 0
1339 """
1340 try:
1341 return self._adj[u][v]
1342 except KeyError:
1343 return default
1344
1345 def adjacency(self):
1346 """Returns an iterator over (node, adjacency dict) tuples for all nodes.
1347
1348 For directed graphs, only outgoing neighbors/adjacencies are included.
1349
1350 Returns
1351 -------
1352 adj_iter : iterator
1353 An iterator over (node, adjacency dictionary) for all nodes in
1354 the graph.
1355
1356 Examples
1357 --------
1358 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1359 >>> [(n, nbrdict) for n, nbrdict in G.adjacency()]
1360 [(0, {1: {}}), (1, {0: {}, 2: {}}), (2, {1: {}, 3: {}}), (3, {2: {}})]
1361
1362 """
1363 return iter(self._adj.items())
1364
1365 @property
1366 def degree(self):
1367 """A DegreeView for the Graph as G.degree or G.degree().
1368
1369 The node degree is the number of edges adjacent to the node.
1370 The weighted node degree is the sum of the edge weights for
1371 edges incident to that node.
1372
1373 This object provides an iterator for (node, degree) as well as
1374 lookup for the degree for a single node.
1375
1376 Parameters
1377 ----------
1378 nbunch : single node, container, or all nodes (default= all nodes)
1379 The view will only report edges incident to these nodes.
1380
1381 weight : string or None, optional (default=None)
1382 The name of an edge attribute that holds the numerical value used
1383 as a weight. If None, then each edge has weight 1.
1384 The degree is the sum of the edge weights adjacent to the node.
1385
1386 Returns
1387 -------
1388 If a single node is requested
1389 deg : int
1390 Degree of the node
1391
1392 OR if multiple nodes are requested
1393 nd_view : A DegreeView object capable of iterating (node, degree) pairs
1394
1395 Examples
1396 --------
1397 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1398 >>> G.degree[0] # node 0 has degree 1
1399 1
1400 >>> list(G.degree([0, 1, 2]))
1401 [(0, 1), (1, 2), (2, 2)]
1402 """
1403 return DegreeView(self)
1404
1405 def clear(self):
1406 """Remove all nodes and edges from the graph.
1407
1408 This also removes the name, and all graph, node, and edge attributes.
1409
1410 Examples
1411 --------
1412 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1413 >>> G.clear()
1414 >>> list(G.nodes)
1415 []
1416 >>> list(G.edges)
1417 []
1418
1419 """
1420 self._adj.clear()
1421 self._node.clear()
1422 self.graph.clear()
1423
1424 def clear_edges(self):
1425 """Remove all edges from the graph without altering nodes.
1426
1427 Examples
1428 --------
1429 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1430 >>> G.clear_edges()
1431 >>> list(G.nodes)
1432 [0, 1, 2, 3]
1433 >>> list(G.edges)
1434 []
1435 """
1436 for neighbours_dict in self._adj.values():
1437 neighbours_dict.clear()
1438
1439 def is_multigraph(self):
1440 """Returns True if graph is a multigraph, False otherwise."""
1441 return False
1442
1443 def is_directed(self):
1444 """Returns True if graph is directed, False otherwise."""
1445 return False
1446
1447 def copy(self, as_view=False):
1448 """Returns a copy of the graph.
1449
1450 The copy method by default returns an independent shallow copy
1451 of the graph and attributes. That is, if an attribute is a
1452 container, that container is shared by the original an the copy.
1453 Use Python's `copy.deepcopy` for new containers.
1454
1455 If `as_view` is True then a view is returned instead of a copy.
1456
1457 Notes
1458 -----
1459 All copies reproduce the graph structure, but data attributes
1460 may be handled in different ways. There are four types of copies
1461 of a graph that people might want.
1462
1463 Deepcopy -- A "deepcopy" copies the graph structure as well as
1464 all data attributes and any objects they might contain.
1465 The entire graph object is new so that changes in the copy
1466 do not affect the original object. (see Python's copy.deepcopy)
1467
1468 Data Reference (Shallow) -- For a shallow copy the graph structure
1469 is copied but the edge, node and graph attribute dicts are
1470 references to those in the original graph. This saves
1471 time and memory but could cause confusion if you change an attribute
1472 in one graph and it changes the attribute in the other.
1473 NetworkX does not provide this level of shallow copy.
1474
1475 Independent Shallow -- This copy creates new independent attribute
1476 dicts and then does a shallow copy of the attributes. That is, any
1477 attributes that are containers are shared between the new graph
1478 and the original. This is exactly what `dict.copy()` provides.
1479 You can obtain this style copy using:
1480
1481 >>> G = nx.path_graph(5)
1482 >>> H = G.copy()
1483 >>> H = G.copy(as_view=False)
1484 >>> H = nx.Graph(G)
1485 >>> H = G.__class__(G)
1486
1487 Fresh Data -- For fresh data, the graph structure is copied while
1488 new empty data attribute dicts are created. The resulting graph
1489 is independent of the original and it has no edge, node or graph
1490 attributes. Fresh copies are not enabled. Instead use:
1491
1492 >>> H = G.__class__()
1493 >>> H.add_nodes_from(G)
1494 >>> H.add_edges_from(G.edges)
1495
1496 View -- Inspired by dict-views, graph-views act like read-only
1497 versions of the original graph, providing a copy of the original
1498 structure without requiring any memory for copying the information.
1499
1500 See the Python copy module for more information on shallow
1501 and deep copies, https://docs.python.org/3/library/copy.html.
1502
1503 Parameters
1504 ----------
1505 as_view : bool, optional (default=False)
1506 If True, the returned graph-view provides a read-only view
1507 of the original graph without actually copying any data.
1508
1509 Returns
1510 -------
1511 G : Graph
1512 A copy of the graph.
1513
1514 See Also
1515 --------
1516 to_directed: return a directed copy of the graph.
1517
1518 Examples
1519 --------
1520 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1521 >>> H = G.copy()
1522
1523 """
1524 if as_view is True:
1525 return nx.graphviews.generic_graph_view(self)
1526 G = self.__class__()
1527 G.graph.update(self.graph)
1528 G.add_nodes_from((n, d.copy()) for n, d in self._node.items())
1529 G.add_edges_from(
1530 (u, v, datadict.copy())
1531 for u, nbrs in self._adj.items()
1532 for v, datadict in nbrs.items()
1533 )
1534 return G
1535
1536 def to_directed(self, as_view=False):
1537 """Returns a directed representation of the graph.
1538
1539 Returns
1540 -------
1541 G : DiGraph
1542 A directed graph with the same name, same nodes, and with
1543 each edge (u, v, data) replaced by two directed edges
1544 (u, v, data) and (v, u, data).
1545
1546 Notes
1547 -----
1548 This returns a "deepcopy" of the edge, node, and
1549 graph attributes which attempts to completely copy
1550 all of the data and references.
1551
1552 This is in contrast to the similar D=DiGraph(G) which returns a
1553 shallow copy of the data.
1554
1555 See the Python copy module for more information on shallow
1556 and deep copies, https://docs.python.org/3/library/copy.html.
1557
1558 Warning: If you have subclassed Graph to use dict-like objects
1559 in the data structure, those changes do not transfer to the
1560 DiGraph created by this method.
1561
1562 Examples
1563 --------
1564 >>> G = nx.Graph() # or MultiGraph, etc
1565 >>> G.add_edge(0, 1)
1566 >>> H = G.to_directed()
1567 >>> list(H.edges)
1568 [(0, 1), (1, 0)]
1569
1570 If already directed, return a (deep) copy
1571
1572 >>> G = nx.DiGraph() # or MultiDiGraph, etc
1573 >>> G.add_edge(0, 1)
1574 >>> H = G.to_directed()
1575 >>> list(H.edges)
1576 [(0, 1)]
1577 """
1578 graph_class = self.to_directed_class()
1579 if as_view is True:
1580 return nx.graphviews.generic_graph_view(self, graph_class)
1581 # deepcopy when not a view
1582 G = graph_class()
1583 G.graph.update(deepcopy(self.graph))
1584 G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
1585 G.add_edges_from(
1586 (u, v, deepcopy(data))
1587 for u, nbrs in self._adj.items()
1588 for v, data in nbrs.items()
1589 )
1590 return G
1591
1592 def to_undirected(self, as_view=False):
1593 """Returns an undirected copy of the graph.
1594
1595 Parameters
1596 ----------
1597 as_view : bool (optional, default=False)
1598 If True return a view of the original undirected graph.
1599
1600 Returns
1601 -------
1602 G : Graph/MultiGraph
1603 A deepcopy of the graph.
1604
1605 See Also
1606 --------
1607 Graph, copy, add_edge, add_edges_from
1608
1609 Notes
1610 -----
1611 This returns a "deepcopy" of the edge, node, and
1612 graph attributes which attempts to completely copy
1613 all of the data and references.
1614
1615 This is in contrast to the similar `G = nx.DiGraph(D)` which returns a
1616 shallow copy of the data.
1617
1618 See the Python copy module for more information on shallow
1619 and deep copies, https://docs.python.org/3/library/copy.html.
1620
1621 Warning: If you have subclassed DiGraph to use dict-like objects
1622 in the data structure, those changes do not transfer to the
1623 Graph created by this method.
1624
1625 Examples
1626 --------
1627 >>> G = nx.path_graph(2) # or MultiGraph, etc
1628 >>> H = G.to_directed()
1629 >>> list(H.edges)
1630 [(0, 1), (1, 0)]
1631 >>> G2 = H.to_undirected()
1632 >>> list(G2.edges)
1633 [(0, 1)]
1634 """
1635 graph_class = self.to_undirected_class()
1636 if as_view is True:
1637 return nx.graphviews.generic_graph_view(self, graph_class)
1638 # deepcopy when not a view
1639 G = graph_class()
1640 G.graph.update(deepcopy(self.graph))
1641 G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
1642 G.add_edges_from(
1643 (u, v, deepcopy(d))
1644 for u, nbrs in self._adj.items()
1645 for v, d in nbrs.items()
1646 )
1647 return G
1648
1649 def subgraph(self, nodes):
1650 """Returns a SubGraph view of the subgraph induced on `nodes`.
1651
1652 The induced subgraph of the graph contains the nodes in `nodes`
1653 and the edges between those nodes.
1654
1655 Parameters
1656 ----------
1657 nodes : list, iterable
1658 A container of nodes which will be iterated through once.
1659
1660 Returns
1661 -------
1662 G : SubGraph View
1663 A subgraph view of the graph. The graph structure cannot be
1664 changed but node/edge attributes can and are shared with the
1665 original graph.
1666
1667 Notes
1668 -----
1669 The graph, edge and node attributes are shared with the original graph.
1670 Changes to the graph structure is ruled out by the view, but changes
1671 to attributes are reflected in the original graph.
1672
1673 To create a subgraph with its own copy of the edge/node attributes use:
1674 G.subgraph(nodes).copy()
1675
1676 For an inplace reduction of a graph to a subgraph you can remove nodes:
1677 G.remove_nodes_from([n for n in G if n not in set(nodes)])
1678
1679 Subgraph views are sometimes NOT what you want. In most cases where
1680 you want to do more than simply look at the induced edges, it makes
1681 more sense to just create the subgraph as its own graph with code like:
1682
1683 ::
1684
1685 # Create a subgraph SG based on a (possibly multigraph) G
1686 SG = G.__class__()
1687 SG.add_nodes_from((n, G.nodes[n]) for n in largest_wcc)
1688 if SG.is_multigraph():
1689 SG.add_edges_from((n, nbr, key, d)
1690 for n, nbrs in G.adj.items() if n in largest_wcc
1691 for nbr, keydict in nbrs.items() if nbr in largest_wcc
1692 for key, d in keydict.items())
1693 else:
1694 SG.add_edges_from((n, nbr, d)
1695 for n, nbrs in G.adj.items() if n in largest_wcc
1696 for nbr, d in nbrs.items() if nbr in largest_wcc)
1697 SG.graph.update(G.graph)
1698
1699 Examples
1700 --------
1701 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1702 >>> H = G.subgraph([0, 1, 2])
1703 >>> list(H.edges)
1704 [(0, 1), (1, 2)]
1705 """
1706 induced_nodes = nx.filters.show_nodes(self.nbunch_iter(nodes))
1707 # if already a subgraph, don't make a chain
1708 subgraph = nx.graphviews.subgraph_view
1709 if hasattr(self, "_NODE_OK"):
1710 return subgraph(self._graph, induced_nodes, self._EDGE_OK)
1711 return subgraph(self, induced_nodes)
1712
1713 def edge_subgraph(self, edges):
1714 """Returns the subgraph induced by the specified edges.
1715
1716 The induced subgraph contains each edge in `edges` and each
1717 node incident to any one of those edges.
1718
1719 Parameters
1720 ----------
1721 edges : iterable
1722 An iterable of edges in this graph.
1723
1724 Returns
1725 -------
1726 G : Graph
1727 An edge-induced subgraph of this graph with the same edge
1728 attributes.
1729
1730 Notes
1731 -----
1732 The graph, edge, and node attributes in the returned subgraph
1733 view are references to the corresponding attributes in the original
1734 graph. The view is read-only.
1735
1736 To create a full graph version of the subgraph with its own copy
1737 of the edge or node attributes, use::
1738
1739 >>> G.edge_subgraph(edges).copy() # doctest: +SKIP
1740
1741 Examples
1742 --------
1743 >>> G = nx.path_graph(5)
1744 >>> H = G.edge_subgraph([(0, 1), (3, 4)])
1745 >>> list(H.nodes)
1746 [0, 1, 3, 4]
1747 >>> list(H.edges)
1748 [(0, 1), (3, 4)]
1749
1750 """
1751 return nx.edge_subgraph(self, edges)
1752
1753 def size(self, weight=None):
1754 """Returns the number of edges or total of all edge weights.
1755
1756 Parameters
1757 ----------
1758 weight : string or None, optional (default=None)
1759 The edge attribute that holds the numerical value used
1760 as a weight. If None, then each edge has weight 1.
1761
1762 Returns
1763 -------
1764 size : numeric
1765 The number of edges or
1766 (if weight keyword is provided) the total weight sum.
1767
1768 If weight is None, returns an int. Otherwise a float
1769 (or more general numeric if the weights are more general).
1770
1771 See Also
1772 --------
1773 number_of_edges
1774
1775 Examples
1776 --------
1777 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1778 >>> G.size()
1779 3
1780
1781 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
1782 >>> G.add_edge("a", "b", weight=2)
1783 >>> G.add_edge("b", "c", weight=4)
1784 >>> G.size()
1785 2
1786 >>> G.size(weight="weight")
1787 6.0
1788 """
1789 s = sum(d for v, d in self.degree(weight=weight))
1790 # If `weight` is None, the sum of the degrees is guaranteed to be
1791 # even, so we can perform integer division and hence return an
1792 # integer. Otherwise, the sum of the weighted degrees is not
1793 # guaranteed to be an integer, so we perform "real" division.
1794 return s // 2 if weight is None else s / 2
1795
1796 def number_of_edges(self, u=None, v=None):
1797 """Returns the number of edges between two nodes.
1798
1799 Parameters
1800 ----------
1801 u, v : nodes, optional (default=all edges)
1802 If u and v are specified, return the number of edges between
1803 u and v. Otherwise return the total number of all edges.
1804
1805 Returns
1806 -------
1807 nedges : int
1808 The number of edges in the graph. If nodes `u` and `v` are
1809 specified return the number of edges between those nodes. If
1810 the graph is directed, this only returns the number of edges
1811 from `u` to `v`.
1812
1813 See Also
1814 --------
1815 size
1816
1817 Examples
1818 --------
1819 For undirected graphs, this method counts the total number of
1820 edges in the graph:
1821
1822 >>> G = nx.path_graph(4)
1823 >>> G.number_of_edges()
1824 3
1825
1826 If you specify two nodes, this counts the total number of edges
1827 joining the two nodes:
1828
1829 >>> G.number_of_edges(0, 1)
1830 1
1831
1832 For directed graphs, this method can count the total number of
1833 directed edges from `u` to `v`:
1834
1835 >>> G = nx.DiGraph()
1836 >>> G.add_edge(0, 1)
1837 >>> G.add_edge(1, 0)
1838 >>> G.number_of_edges(0, 1)
1839 1
1840
1841 """
1842 if u is None:
1843 return int(self.size())
1844 if v in self._adj[u]:
1845 return 1
1846 return 0
1847
1848 def nbunch_iter(self, nbunch=None):
1849 """Returns an iterator over nodes contained in nbunch that are
1850 also in the graph.
1851
1852 The nodes in nbunch are checked for membership in the graph
1853 and if not are silently ignored.
1854
1855 Parameters
1856 ----------
1857 nbunch : single node, container, or all nodes (default= all nodes)
1858 The view will only report edges incident to these nodes.
1859
1860 Returns
1861 -------
1862 niter : iterator
1863 An iterator over nodes in nbunch that are also in the graph.
1864 If nbunch is None, iterate over all nodes in the graph.
1865
1866 Raises
1867 ------
1868 NetworkXError
1869 If nbunch is not a node or or sequence of nodes.
1870 If a node in nbunch is not hashable.
1871
1872 See Also
1873 --------
1874 Graph.__iter__
1875
1876 Notes
1877 -----
1878 When nbunch is an iterator, the returned iterator yields values
1879 directly from nbunch, becoming exhausted when nbunch is exhausted.
1880
1881 To test whether nbunch is a single node, one can use
1882 "if nbunch in self:", even after processing with this routine.
1883
1884 If nbunch is not a node or a (possibly empty) sequence/iterator
1885 or None, a :exc:`NetworkXError` is raised. Also, if any object in
1886 nbunch is not hashable, a :exc:`NetworkXError` is raised.
1887 """
1888 if nbunch is None: # include all nodes via iterator
1889 bunch = iter(self._adj)
1890 elif nbunch in self: # if nbunch is a single node
1891 bunch = iter([nbunch])
1892 else: # if nbunch is a sequence of nodes
1893
1894 def bunch_iter(nlist, adj):
1895 try:
1896 for n in nlist:
1897 if n in adj:
1898 yield n
1899 except TypeError as e:
1900 message = e.args[0]
1901 # capture error for non-sequence/iterator nbunch.
1902 if "iter" in message:
1903 msg = "nbunch is not a node or a sequence of nodes."
1904 raise NetworkXError(msg) from e
1905 # capture error for unhashable node.
1906 elif "hashable" in message:
1907 msg = f"Node {n} in sequence nbunch is not a valid node."
1908 raise NetworkXError(msg) from e
1909 else:
1910 raise
1911
1912 bunch = bunch_iter(nbunch, self._adj)
1913 return bunch