Mercurial > repos > shellac > sam_consensus_v3
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 |