Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/networkx/classes/reportviews.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 # Copyright (C) 2004-2019 by | |
2 # Aric Hagberg <hagberg@lanl.gov> | |
3 # Dan Schult <dschult@colgate.edu> | |
4 # Pieter Swart <swart@lanl.gov> | |
5 # All rights reserved. | |
6 # BSD license. | |
7 # | |
8 # Authors: Aric Hagberg (hagberg@lanl.gov), | |
9 # Pieter Swart (swart@lanl.gov), | |
10 # Dan Schult(dschult@colgate.edu) | |
11 """ | |
12 View Classes provide node, edge and degree "views" of a graph. | |
13 | |
14 Views for nodes, edges and degree are provided for all base graph classes. | |
15 A view means a read-only object that is quick to create, automatically | |
16 updated when the graph changes, and provides basic access like `n in V`, | |
17 `for n in V`, `V[n]` and sometimes set operations. | |
18 | |
19 The views are read-only iterable containers that are updated as the | |
20 graph is updated. As with dicts, the graph should not be updated | |
21 while iterating through the view. Views can be iterated multiple times. | |
22 | |
23 Edge and Node views also allow data attribute lookup. | |
24 The resulting attribute dict is writable as `G.edges[3, 4]['color']='red'` | |
25 Degree views allow lookup of degree values for single nodes. | |
26 Weighted degree is supported with the `weight` argument. | |
27 | |
28 NodeView | |
29 ======== | |
30 | |
31 `V = G.nodes` (or `V = G.nodes()`) allows `len(V)`, `n in V`, set | |
32 operations e.g. "G.nodes & H.nodes", and `dd = G.nodes[n]`, where | |
33 `dd` is the node data dict. Iteration is over the nodes by default. | |
34 | |
35 NodeDataView | |
36 ============ | |
37 | |
38 To iterate over (node, data) pairs, use arguments to `G.nodes()` | |
39 to create a DataView e.g. `DV = G.nodes(data='color', default='red')`. | |
40 The DataView iterates as `for n, color in DV` and allows | |
41 `(n, 'red') in DV`. Using `DV = G.nodes(data=True)`, the DataViews | |
42 use the full datadict in writeable form also allowing contain testing as | |
43 `(n, {'color': 'red'}) in VD`. DataViews allow set operations when | |
44 data attributes are hashable. | |
45 | |
46 DegreeView | |
47 ========== | |
48 | |
49 `V = G.degree` allows iteration over (node, degree) pairs as well | |
50 as lookup: `deg=V[n]`. There are many flavors of DegreeView | |
51 for In/Out/Directed/Multi. For Directed Graphs, `G.degree` | |
52 counts both in and out going edges. `G.out_degree` and | |
53 `G.in_degree` count only specific directions. | |
54 Weighted degree using edge data attributes is provide via | |
55 `V = G.degree(weight='attr_name')` where any string with the | |
56 attribute name can be used. `weight=None` is the default. | |
57 No set operations are implemented for degrees, use NodeView. | |
58 | |
59 The argument `nbunch` restricts iteration to nodes in nbunch. | |
60 The DegreeView can still lookup any node even if nbunch is specified. | |
61 | |
62 EdgeView | |
63 ======== | |
64 | |
65 `V = G.edges` or `V = G.edges()` allows iteration over edges as well as | |
66 `e in V`, set operations and edge data lookup `dd = G.edges[2, 3]`. | |
67 Iteration is over 2-tuples `(u, v)` for Graph/DiGraph. For multigraphs | |
68 edges 3-tuples `(u, v, key)` are the default but 2-tuples can be obtained | |
69 via `V = G.edges(keys=False)`. | |
70 | |
71 Set operations for directed graphs treat the edges as a set of 2-tuples. | |
72 For undirected graphs, 2-tuples are not a unique representation of edges. | |
73 So long as the set being compared to contains unique representations | |
74 of its edges, the set operations will act as expected. If the other | |
75 set contains both `(0, 1)` and `(1, 0)` however, the result of set | |
76 operations may contain both representations of the same edge. | |
77 | |
78 EdgeDataView | |
79 ============ | |
80 | |
81 Edge data can be reported using an EdgeDataView typically created | |
82 by calling an EdgeView: `DV = G.edges(data='weight', default=1)`. | |
83 The EdgeDataView allows iteration over edge tuples, membership checking | |
84 but no set operations. | |
85 | |
86 Iteration depends on `data` and `default` and for multigraph `keys` | |
87 If `data is False` (the default) then iterate over 2-tuples `(u, v)`. | |
88 If `data is True` iterate over 3-tuples `(u, v, datadict)`. | |
89 Otherwise iterate over `(u, v, datadict.get(data, default))`. | |
90 For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key` | |
91 to create 3-tuples and 4-tuples. | |
92 | |
93 The argument `nbunch` restricts edges to those incident to nodes in nbunch. | |
94 """ | |
95 from collections.abc import Mapping, Set, Iterable | |
96 import networkx as nx | |
97 | |
98 __all__ = ['NodeView', 'NodeDataView', | |
99 'EdgeView', 'OutEdgeView', 'InEdgeView', | |
100 'EdgeDataView', 'OutEdgeDataView', 'InEdgeDataView', | |
101 'MultiEdgeView', 'OutMultiEdgeView', 'InMultiEdgeView', | |
102 'MultiEdgeDataView', 'OutMultiEdgeDataView', 'InMultiEdgeDataView', | |
103 'DegreeView', 'DiDegreeView', 'InDegreeView', 'OutDegreeView', | |
104 'MultiDegreeView', 'DiMultiDegreeView', | |
105 'InMultiDegreeView', 'OutMultiDegreeView'] | |
106 | |
107 | |
108 # NodeViews | |
109 class NodeView(Mapping, Set): | |
110 """A NodeView class to act as G.nodes for a NetworkX Graph | |
111 | |
112 Set operations act on the nodes without considering data. | |
113 Iteration is over nodes. Node data can be looked up like a dict. | |
114 Use NodeDataView to iterate over node data or to specify a data | |
115 attribute for lookup. NodeDataView is created by calling the NodeView. | |
116 | |
117 Parameters | |
118 ---------- | |
119 graph : NetworkX graph-like class | |
120 | |
121 Examples | |
122 -------- | |
123 >>> G = nx.path_graph(3) | |
124 >>> NV = G.nodes() | |
125 >>> 2 in NV | |
126 True | |
127 >>> for n in NV: print(n) | |
128 0 | |
129 1 | |
130 2 | |
131 >>> assert(NV & {1, 2, 3} == {1, 2}) | |
132 | |
133 >>> G.add_node(2, color='blue') | |
134 >>> NV[2] | |
135 {'color': 'blue'} | |
136 >>> G.add_node(8, color='red') | |
137 >>> NDV = G.nodes(data=True) | |
138 >>> (2, NV[2]) in NDV | |
139 True | |
140 >>> for n, dd in NDV: print((n, dd.get('color', 'aqua'))) | |
141 (0, 'aqua') | |
142 (1, 'aqua') | |
143 (2, 'blue') | |
144 (8, 'red') | |
145 >>> NDV[2] == NV[2] | |
146 True | |
147 | |
148 >>> NVdata = G.nodes(data='color', default='aqua') | |
149 >>> (2, NVdata[2]) in NVdata | |
150 True | |
151 >>> for n, dd in NVdata: print((n, dd)) | |
152 (0, 'aqua') | |
153 (1, 'aqua') | |
154 (2, 'blue') | |
155 (8, 'red') | |
156 >>> NVdata[2] == NV[2] # NVdata gets 'color', NV gets datadict | |
157 False | |
158 """ | |
159 __slots__ = '_nodes', | |
160 | |
161 def __getstate__(self): | |
162 return {'_nodes': self._nodes} | |
163 | |
164 def __setstate__(self, state): | |
165 self._nodes = state['_nodes'] | |
166 | |
167 def __init__(self, graph): | |
168 self._nodes = graph._node | |
169 | |
170 # Mapping methods | |
171 def __len__(self): | |
172 return len(self._nodes) | |
173 | |
174 def __iter__(self): | |
175 return iter(self._nodes) | |
176 | |
177 def __getitem__(self, n): | |
178 return self._nodes[n] | |
179 | |
180 # Set methods | |
181 def __contains__(self, n): | |
182 return n in self._nodes | |
183 | |
184 @classmethod | |
185 def _from_iterable(cls, it): | |
186 return set(it) | |
187 | |
188 # DataView method | |
189 def __call__(self, data=False, default=None): | |
190 if data is False: | |
191 return self | |
192 return NodeDataView(self._nodes, data, default) | |
193 | |
194 def data(self, data=True, default=None): | |
195 if data is False: | |
196 return self | |
197 return NodeDataView(self._nodes, data, default) | |
198 | |
199 def __str__(self): | |
200 return str(list(self)) | |
201 | |
202 def __repr__(self): | |
203 return '%s(%r)' % (self.__class__.__name__, tuple(self)) | |
204 | |
205 | |
206 class NodeDataView(Set): | |
207 """A DataView class for nodes of a NetworkX Graph | |
208 | |
209 The main use for this class is to iterate through node-data pairs. | |
210 The data can be the entire data-dictionary for each node, or it | |
211 can be a specific attribute (with default) for each node. | |
212 Set operations are enabled with NodeDataView, but don't work in | |
213 cases where the data is not hashable. Use with caution. | |
214 Typically, set operations on nodes use NodeView, not NodeDataView. | |
215 That is, they use `G.nodes` instead of `G.nodes(data='foo')`. | |
216 | |
217 Parameters | |
218 ========== | |
219 graph : NetworkX graph-like class | |
220 data : bool or string (default=False) | |
221 default : object (default=None) | |
222 """ | |
223 __slots__ = ('_nodes', '_data', '_default') | |
224 | |
225 def __getstate__(self): | |
226 return {'_nodes': self._nodes, | |
227 '_data': self._data, | |
228 '_default': self._default} | |
229 | |
230 def __setstate__(self, state): | |
231 self._nodes = state['_nodes'] | |
232 self._data = state['_data'] | |
233 self._default = state['_default'] | |
234 | |
235 def __init__(self, nodedict, data=False, default=None): | |
236 self._nodes = nodedict | |
237 self._data = data | |
238 self._default = default | |
239 | |
240 @classmethod | |
241 def _from_iterable(cls, it): | |
242 try: | |
243 return set(it) | |
244 except TypeError as err: | |
245 if "unhashable" in str(err): | |
246 msg = " : Could be b/c data=True or your values are unhashable" | |
247 raise TypeError(str(err) + msg) | |
248 raise | |
249 | |
250 def __len__(self): | |
251 return len(self._nodes) | |
252 | |
253 def __iter__(self): | |
254 data = self._data | |
255 if data is False: | |
256 return iter(self._nodes) | |
257 if data is True: | |
258 return iter(self._nodes.items()) | |
259 return ((n, dd[data] if data in dd else self._default) | |
260 for n, dd in self._nodes.items()) | |
261 | |
262 def __contains__(self, n): | |
263 try: | |
264 node_in = n in self._nodes | |
265 except TypeError: | |
266 n, d = n | |
267 return n in self._nodes and self[n] == d | |
268 if node_in is True: | |
269 return node_in | |
270 try: | |
271 n, d = n | |
272 except (TypeError, ValueError): | |
273 return False | |
274 return n in self._nodes and self[n] == d | |
275 | |
276 def __getitem__(self, n): | |
277 ddict = self._nodes[n] | |
278 data = self._data | |
279 if data is False or data is True: | |
280 return ddict | |
281 return ddict[data] if data in ddict else self._default | |
282 | |
283 def __str__(self): | |
284 return str(list(self)) | |
285 | |
286 def __repr__(self): | |
287 if self._data is False: | |
288 return '%s(%r)' % (self.__class__.__name__, tuple(self)) | |
289 if self._data is True: | |
290 return '%s(%r)' % (self.__class__.__name__, dict(self)) | |
291 return '%s(%r, data=%r)' % \ | |
292 (self.__class__.__name__, dict(self), self._data) | |
293 | |
294 | |
295 # DegreeViews | |
296 class DiDegreeView(object): | |
297 """A View class for degree of nodes in a NetworkX Graph | |
298 | |
299 The functionality is like dict.items() with (node, degree) pairs. | |
300 Additional functionality includes read-only lookup of node degree, | |
301 and calling with optional features nbunch (for only a subset of nodes) | |
302 and weight (use edge weights to compute degree). | |
303 | |
304 Parameters | |
305 ========== | |
306 graph : NetworkX graph-like class | |
307 nbunch : node, container of nodes, or None meaning all nodes (default=None) | |
308 weight : bool or string (default=None) | |
309 | |
310 Notes | |
311 ----- | |
312 DegreeView can still lookup any node even if nbunch is specified. | |
313 | |
314 Examples | |
315 -------- | |
316 >>> G = nx.path_graph(3) | |
317 >>> DV = G.degree() | |
318 >>> assert(DV[2] == 1) | |
319 >>> assert(sum(deg for n, deg in DV) == 4) | |
320 | |
321 >>> DVweight = G.degree(weight="span") | |
322 >>> G.add_edge(1, 2, span=34) | |
323 >>> DVweight[2] | |
324 34 | |
325 >>> DVweight[0] # default edge weight is 1 | |
326 1 | |
327 >>> sum(span for n, span in DVweight) # sum weighted degrees | |
328 70 | |
329 | |
330 >>> DVnbunch = G.degree(nbunch=(1, 2)) | |
331 >>> assert(len(list(DVnbunch)) == 2) # iteration over nbunch only | |
332 """ | |
333 | |
334 def __init__(self, G, nbunch=None, weight=None): | |
335 self._graph = G | |
336 self._succ = G._succ if hasattr(G, "_succ") else G._adj | |
337 self._pred = G._pred if hasattr(G, "_pred") else G._adj | |
338 self._nodes = self._succ if nbunch is None \ | |
339 else list(G.nbunch_iter(nbunch)) | |
340 self._weight = weight | |
341 | |
342 def __call__(self, nbunch=None, weight=None): | |
343 if nbunch is None: | |
344 if weight == self._weight: | |
345 return self | |
346 return self.__class__(self._graph, None, weight) | |
347 try: | |
348 if nbunch in self._nodes: | |
349 if weight == self._weight: | |
350 return self[nbunch] | |
351 return self.__class__(self._graph, None, weight)[nbunch] | |
352 except TypeError: | |
353 pass | |
354 return self.__class__(self._graph, nbunch, weight) | |
355 | |
356 def __getitem__(self, n): | |
357 weight = self._weight | |
358 succs = self._succ[n] | |
359 preds = self._pred[n] | |
360 if weight is None: | |
361 return len(succs) + len(preds) | |
362 return sum(dd.get(weight, 1) for dd in succs.values()) + \ | |
363 sum(dd.get(weight, 1) for dd in preds.values()) | |
364 | |
365 def __iter__(self): | |
366 weight = self._weight | |
367 if weight is None: | |
368 for n in self._nodes: | |
369 succs = self._succ[n] | |
370 preds = self._pred[n] | |
371 yield (n, len(succs) + len(preds)) | |
372 else: | |
373 for n in self._nodes: | |
374 succs = self._succ[n] | |
375 preds = self._pred[n] | |
376 deg = sum(dd.get(weight, 1) for dd in succs.values()) \ | |
377 + sum(dd.get(weight, 1) for dd in preds.values()) | |
378 yield (n, deg) | |
379 | |
380 def __len__(self): | |
381 return len(self._nodes) | |
382 | |
383 def __str__(self): | |
384 return str(list(self)) | |
385 | |
386 def __repr__(self): | |
387 return '%s(%r)' % (self.__class__.__name__, dict(self)) | |
388 | |
389 | |
390 class DegreeView(DiDegreeView): | |
391 """A DegreeView class to act as G.degree for a NetworkX Graph | |
392 | |
393 Typical usage focuses on iteration over `(node, degree)` pairs. | |
394 The degree is by default the number of edges incident to the node. | |
395 Optional argument `weight` enables weighted degree using the edge | |
396 attribute named in the `weight` argument. Reporting and iteration | |
397 can also be restricted to a subset of nodes using `nbunch`. | |
398 | |
399 Additional functionality include node lookup so that `G.degree[n]` | |
400 reported the (possibly weighted) degree of node `n`. Calling the | |
401 view creates a view with different arguments `nbunch` or `weight`. | |
402 | |
403 Parameters | |
404 ========== | |
405 graph : NetworkX graph-like class | |
406 nbunch : node, container of nodes, or None meaning all nodes (default=None) | |
407 weight : string or None (default=None) | |
408 | |
409 Notes | |
410 ----- | |
411 DegreeView can still lookup any node even if nbunch is specified. | |
412 | |
413 Examples | |
414 -------- | |
415 >>> G = nx.path_graph(3) | |
416 >>> DV = G.degree() | |
417 >>> assert(DV[2] == 1) | |
418 >>> assert(G.degree[2] == 1) | |
419 >>> assert(sum(deg for n, deg in DV) == 4) | |
420 | |
421 >>> DVweight = G.degree(weight="span") | |
422 >>> G.add_edge(1, 2, span=34) | |
423 >>> DVweight[2] | |
424 34 | |
425 >>> DVweight[0] # default edge weight is 1 | |
426 1 | |
427 >>> sum(span for n, span in DVweight) # sum weighted degrees | |
428 70 | |
429 | |
430 >>> DVnbunch = G.degree(nbunch=(1, 2)) | |
431 >>> assert(len(list(DVnbunch)) == 2) # iteration over nbunch only | |
432 """ | |
433 | |
434 def __getitem__(self, n): | |
435 weight = self._weight | |
436 nbrs = self._succ[n] | |
437 if weight is None: | |
438 return len(nbrs) + (n in nbrs) | |
439 return sum(dd.get(weight, 1) for dd in nbrs.values()) + \ | |
440 (n in nbrs and nbrs[n].get(weight, 1)) | |
441 | |
442 def __iter__(self): | |
443 weight = self._weight | |
444 if weight is None: | |
445 for n in self._nodes: | |
446 nbrs = self._succ[n] | |
447 yield (n, len(nbrs) + (n in nbrs)) | |
448 else: | |
449 for n in self._nodes: | |
450 nbrs = self._succ[n] | |
451 deg = sum(dd.get(weight, 1) for dd in nbrs.values()) + \ | |
452 (n in nbrs and nbrs[n].get(weight, 1)) | |
453 yield (n, deg) | |
454 | |
455 | |
456 class OutDegreeView(DiDegreeView): | |
457 """A DegreeView class to report out_degree for a DiGraph; See DegreeView""" | |
458 | |
459 def __getitem__(self, n): | |
460 weight = self._weight | |
461 nbrs = self._succ[n] | |
462 if self._weight is None: | |
463 return len(nbrs) | |
464 return sum(dd.get(self._weight, 1) for dd in nbrs.values()) | |
465 | |
466 def __iter__(self): | |
467 weight = self._weight | |
468 if weight is None: | |
469 for n in self._nodes: | |
470 succs = self._succ[n] | |
471 yield (n, len(succs)) | |
472 else: | |
473 for n in self._nodes: | |
474 succs = self._succ[n] | |
475 deg = sum(dd.get(weight, 1) for dd in succs.values()) | |
476 yield (n, deg) | |
477 | |
478 | |
479 class InDegreeView(DiDegreeView): | |
480 """A DegreeView class to report in_degree for a DiGraph; See DegreeView""" | |
481 | |
482 def __getitem__(self, n): | |
483 weight = self._weight | |
484 nbrs = self._pred[n] | |
485 if weight is None: | |
486 return len(nbrs) | |
487 return sum(dd.get(weight, 1) for dd in nbrs.values()) | |
488 | |
489 def __iter__(self): | |
490 weight = self._weight | |
491 if weight is None: | |
492 for n in self._nodes: | |
493 preds = self._pred[n] | |
494 yield (n, len(preds)) | |
495 else: | |
496 for n in self._nodes: | |
497 preds = self._pred[n] | |
498 deg = sum(dd.get(weight, 1) for dd in preds.values()) | |
499 yield (n, deg) | |
500 | |
501 | |
502 class MultiDegreeView(DiDegreeView): | |
503 """A DegreeView class for undirected multigraphs; See DegreeView""" | |
504 | |
505 def __getitem__(self, n): | |
506 weight = self._weight | |
507 nbrs = self._succ[n] | |
508 if weight is None: | |
509 return sum(len(keys) for keys in nbrs.values()) + \ | |
510 (n in nbrs and len(nbrs[n])) | |
511 # edge weighted graph - degree is sum of nbr edge weights | |
512 deg = sum(d.get(weight, 1) for key_dict in nbrs.values() | |
513 for d in key_dict.values()) | |
514 if n in nbrs: | |
515 deg += sum(d.get(weight, 1) for d in nbrs[n].values()) | |
516 return deg | |
517 | |
518 def __iter__(self): | |
519 weight = self._weight | |
520 if weight is None: | |
521 for n in self._nodes: | |
522 nbrs = self._succ[n] | |
523 deg = sum(len(keys) for keys in nbrs.values()) + \ | |
524 (n in nbrs and len(nbrs[n])) | |
525 yield (n, deg) | |
526 else: | |
527 for n in self._nodes: | |
528 nbrs = self._succ[n] | |
529 deg = sum(d.get(weight, 1) for key_dict in nbrs.values() | |
530 for d in key_dict.values()) | |
531 if n in nbrs: | |
532 deg += sum(d.get(weight, 1) for d in nbrs[n].values()) | |
533 yield (n, deg) | |
534 | |
535 | |
536 class DiMultiDegreeView(DiDegreeView): | |
537 """A DegreeView class for MultiDiGraph; See DegreeView""" | |
538 | |
539 def __getitem__(self, n): | |
540 weight = self._weight | |
541 succs = self._succ[n] | |
542 preds = self._pred[n] | |
543 if weight is None: | |
544 return sum(len(keys) for keys in succs.values()) + \ | |
545 sum(len(keys) for keys in preds.values()) | |
546 # edge weighted graph - degree is sum of nbr edge weights | |
547 deg = sum(d.get(weight, 1) for key_dict in succs.values() | |
548 for d in key_dict.values()) + \ | |
549 sum(d.get(weight, 1) for key_dict in preds.values() | |
550 for d in key_dict.values()) | |
551 return deg | |
552 | |
553 def __iter__(self): | |
554 weight = self._weight | |
555 if weight is None: | |
556 for n in self._nodes: | |
557 succs = self._succ[n] | |
558 preds = self._pred[n] | |
559 deg = sum(len(keys) for keys in succs.values()) + \ | |
560 sum(len(keys) for keys in preds.values()) | |
561 yield (n, deg) | |
562 else: | |
563 for n in self._nodes: | |
564 succs = self._succ[n] | |
565 preds = self._pred[n] | |
566 deg = sum(d.get(weight, 1) for key_dict in succs.values() | |
567 for d in key_dict.values()) + \ | |
568 sum(d.get(weight, 1) for key_dict in preds.values() | |
569 for d in key_dict.values()) | |
570 yield (n, deg) | |
571 | |
572 | |
573 class InMultiDegreeView(DiDegreeView): | |
574 """A DegreeView class for inward degree of MultiDiGraph; See DegreeView""" | |
575 | |
576 def __getitem__(self, n): | |
577 weight = self._weight | |
578 nbrs = self._pred[n] | |
579 if weight is None: | |
580 return sum(len(data) for data in nbrs.values()) | |
581 # edge weighted graph - degree is sum of nbr edge weights | |
582 return sum(d.get(weight, 1) for key_dict in nbrs.values() | |
583 for d in key_dict.values()) | |
584 | |
585 def __iter__(self): | |
586 weight = self._weight | |
587 if weight is None: | |
588 for n in self._nodes: | |
589 nbrs = self._pred[n] | |
590 deg = sum(len(data) for data in nbrs.values()) | |
591 yield (n, deg) | |
592 else: | |
593 for n in self._nodes: | |
594 nbrs = self._pred[n] | |
595 deg = sum(d.get(weight, 1) for key_dict in nbrs.values() | |
596 for d in key_dict.values()) | |
597 yield (n, deg) | |
598 | |
599 | |
600 class OutMultiDegreeView(DiDegreeView): | |
601 """A DegreeView class for outward degree of MultiDiGraph; See DegreeView""" | |
602 | |
603 def __getitem__(self, n): | |
604 weight = self._weight | |
605 nbrs = self._succ[n] | |
606 if weight is None: | |
607 return sum(len(data) for data in nbrs.values()) | |
608 # edge weighted graph - degree is sum of nbr edge weights | |
609 return sum(d.get(weight, 1) for key_dict in nbrs.values() | |
610 for d in key_dict.values()) | |
611 | |
612 def __iter__(self): | |
613 weight = self._weight | |
614 if weight is None: | |
615 for n in self._nodes: | |
616 nbrs = self._succ[n] | |
617 deg = sum(len(data) for data in nbrs.values()) | |
618 yield (n, deg) | |
619 else: | |
620 for n in self._nodes: | |
621 nbrs = self._succ[n] | |
622 deg = sum(d.get(weight, 1) for key_dict in nbrs.values() | |
623 for d in key_dict.values()) | |
624 yield (n, deg) | |
625 | |
626 | |
627 # EdgeDataViews | |
628 class OutEdgeDataView(object): | |
629 """EdgeDataView for outward edges of DiGraph; See EdgeDataView""" | |
630 __slots__ = ('_viewer', '_nbunch', '_data', '_default', | |
631 '_adjdict', '_nodes_nbrs', '_report') | |
632 | |
633 def __getstate__(self): | |
634 return {'viewer': self._viewer, | |
635 'nbunch': self._nbunch, | |
636 'data': self._data, | |
637 'default': self._default} | |
638 | |
639 def __setstate__(self, state): | |
640 self.__init__(**state) | |
641 | |
642 def __init__(self, viewer, nbunch=None, data=False, default=None): | |
643 self._viewer = viewer | |
644 adjdict = self._adjdict = viewer._adjdict | |
645 if nbunch is None: | |
646 self._nodes_nbrs = adjdict.items | |
647 else: | |
648 nbunch = list(viewer._graph.nbunch_iter(nbunch)) | |
649 self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch] | |
650 self._nbunch = nbunch | |
651 self._data = data | |
652 self._default = default | |
653 # Set _report based on data and default | |
654 if data is True: | |
655 self._report = lambda n, nbr, dd: (n, nbr, dd) | |
656 elif data is False: | |
657 self._report = lambda n, nbr, dd: (n, nbr) | |
658 else: # data is attribute name | |
659 self._report = lambda n, nbr, dd: \ | |
660 (n, nbr, dd[data]) if data in dd else (n, nbr, default) | |
661 | |
662 def __len__(self): | |
663 return sum(len(nbrs) for n, nbrs in self._nodes_nbrs()) | |
664 | |
665 def __iter__(self): | |
666 return (self._report(n, nbr, dd) for n, nbrs in self._nodes_nbrs() | |
667 for nbr, dd in nbrs.items()) | |
668 | |
669 def __contains__(self, e): | |
670 try: | |
671 u, v = e[:2] | |
672 ddict = self._adjdict[u][v] | |
673 except KeyError: | |
674 return False | |
675 return e == self._report(u, v, ddict) | |
676 | |
677 def __str__(self): | |
678 return str(list(self)) | |
679 | |
680 def __repr__(self): | |
681 return '%s(%r)' % (self.__class__.__name__, list(self)) | |
682 | |
683 | |
684 class EdgeDataView(OutEdgeDataView): | |
685 """A EdgeDataView class for edges of Graph | |
686 | |
687 This view is primarily used to iterate over the edges reporting | |
688 edges as node-tuples with edge data optionally reported. The | |
689 argument `nbunch` allows restriction to edges incident to nodes | |
690 in that container/singleton. The default (nbunch=None) | |
691 reports all edges. The arguments `data` and `default` control | |
692 what edge data is reported. The default `data is False` reports | |
693 only node-tuples for each edge. If `data is True` the entire edge | |
694 data dict is returned. Otherwise `data` is assumed to hold the name | |
695 of the edge attribute to report with default `default` if that | |
696 edge attribute is not present. | |
697 | |
698 Parameters | |
699 ---------- | |
700 nbunch : container of nodes, node or None (default None) | |
701 data : False, True or string (default False) | |
702 default : default value (default None) | |
703 | |
704 Examples | |
705 -------- | |
706 >>> G = nx.path_graph(3) | |
707 >>> G.add_edge(1, 2, foo='bar') | |
708 >>> list(G.edges(data='foo', default='biz')) | |
709 [(0, 1, 'biz'), (1, 2, 'bar')] | |
710 >>> assert((0, 1, 'biz') in G.edges(data='foo', default='biz')) | |
711 """ | |
712 __slots__ = () | |
713 | |
714 def __len__(self): | |
715 return sum(1 for e in self) | |
716 | |
717 def __iter__(self): | |
718 seen = {} | |
719 for n, nbrs in self._nodes_nbrs(): | |
720 for nbr, dd in nbrs.items(): | |
721 if nbr not in seen: | |
722 yield self._report(n, nbr, dd) | |
723 seen[n] = 1 | |
724 del seen | |
725 | |
726 def __contains__(self, e): | |
727 try: | |
728 u, v = e[:2] | |
729 ddict = self._adjdict[u][v] | |
730 except KeyError: | |
731 try: | |
732 ddict = self._adjdict[v][u] | |
733 except KeyError: | |
734 return False | |
735 return e == self._report(u, v, ddict) | |
736 | |
737 | |
738 class InEdgeDataView(OutEdgeDataView): | |
739 """An EdgeDataView class for outward edges of DiGraph; See EdgeDataView""" | |
740 __slots__ = () | |
741 | |
742 def __iter__(self): | |
743 return (self._report(nbr, n, dd) for n, nbrs in self._nodes_nbrs() | |
744 for nbr, dd in nbrs.items()) | |
745 | |
746 def __contains__(self, e): | |
747 try: | |
748 u, v = e[:2] | |
749 ddict = self._adjdict[v][u] | |
750 except KeyError: | |
751 return False | |
752 return e == self._report(u, v, ddict) | |
753 | |
754 | |
755 class OutMultiEdgeDataView(OutEdgeDataView): | |
756 """An EdgeDataView for outward edges of MultiDiGraph; See EdgeDataView""" | |
757 __slots__ = ('keys',) | |
758 | |
759 def __getstate__(self): | |
760 return {'viewer': self._viewer, | |
761 'nbunch': self._nbunch, | |
762 'keys': self.keys, | |
763 'data': self._data, | |
764 'default': self._default} | |
765 | |
766 def __setstate__(self, state): | |
767 self.__init__(**state) | |
768 | |
769 def __init__(self, viewer, nbunch=None, | |
770 data=False, keys=False, default=None): | |
771 self._viewer = viewer | |
772 adjdict = self._adjdict = viewer._adjdict | |
773 self.keys = keys | |
774 if nbunch is None: | |
775 self._nodes_nbrs = adjdict.items | |
776 else: | |
777 nbunch = list(viewer._graph.nbunch_iter(nbunch)) | |
778 self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch] | |
779 self._nbunch = nbunch | |
780 self._data = data | |
781 self._default = default | |
782 # Set _report based on data and default | |
783 if data is True: | |
784 if keys is True: | |
785 self._report = lambda n, nbr, k, dd: (n, nbr, k, dd) | |
786 else: | |
787 self._report = lambda n, nbr, k, dd: (n, nbr, dd) | |
788 elif data is False: | |
789 if keys is True: | |
790 self._report = lambda n, nbr, k, dd: (n, nbr, k) | |
791 else: | |
792 self._report = lambda n, nbr, k, dd: (n, nbr) | |
793 else: # data is attribute name | |
794 if keys is True: | |
795 self._report = lambda n, nbr, k, dd: (n, nbr, k, dd[data]) \ | |
796 if data in dd else (n, nbr, k, default) | |
797 else: | |
798 self._report = lambda n, nbr, k, dd: (n, nbr, dd[data]) \ | |
799 if data in dd else (n, nbr, default) | |
800 | |
801 def __len__(self): | |
802 return sum(1 for e in self) | |
803 | |
804 def __iter__(self): | |
805 return (self._report(n, nbr, k, dd) for n, nbrs in self._nodes_nbrs() | |
806 for nbr, kd in nbrs.items() for k, dd in kd.items()) | |
807 | |
808 def __contains__(self, e): | |
809 u, v = e[:2] | |
810 try: | |
811 kdict = self._adjdict[u][v] | |
812 except KeyError: | |
813 return False | |
814 if self.keys is True: | |
815 k = e[2] | |
816 try: | |
817 dd = kdict[k] | |
818 except KeyError: | |
819 return False | |
820 return e == self._report(u, v, k, dd) | |
821 for k, dd in kdict.items(): | |
822 if e == self._report(u, v, k, dd): | |
823 return True | |
824 return False | |
825 | |
826 | |
827 class MultiEdgeDataView(OutMultiEdgeDataView): | |
828 """An EdgeDataView class for edges of MultiGraph; See EdgeDataView""" | |
829 __slots__ = () | |
830 | |
831 def __iter__(self): | |
832 seen = {} | |
833 for n, nbrs in self._nodes_nbrs(): | |
834 for nbr, kd in nbrs.items(): | |
835 if nbr not in seen: | |
836 for k, dd in kd.items(): | |
837 yield self._report(n, nbr, k, dd) | |
838 seen[n] = 1 | |
839 del seen | |
840 | |
841 def __contains__(self, e): | |
842 u, v = e[:2] | |
843 try: | |
844 kdict = self._adjdict[u][v] | |
845 except KeyError: | |
846 try: | |
847 kdict = self._adjdict[v][u] | |
848 except KeyError: | |
849 return False | |
850 if self.keys is True: | |
851 k = e[2] | |
852 try: | |
853 dd = kdict[k] | |
854 except KeyError: | |
855 return False | |
856 return e == self._report(u, v, k, dd) | |
857 for k, dd in kdict.items(): | |
858 if e == self._report(u, v, k, dd): | |
859 return True | |
860 return False | |
861 | |
862 | |
863 class InMultiEdgeDataView(OutMultiEdgeDataView): | |
864 """An EdgeDataView for inward edges of MultiDiGraph; See EdgeDataView""" | |
865 __slots__ = () | |
866 | |
867 def __iter__(self): | |
868 return (self._report(nbr, n, k, dd) for n, nbrs in self._nodes_nbrs() | |
869 for nbr, kd in nbrs.items() for k, dd in kd.items()) | |
870 | |
871 def __contains__(self, e): | |
872 u, v = e[:2] | |
873 try: | |
874 kdict = self._adjdict[v][u] | |
875 except KeyError: | |
876 return False | |
877 if self.keys is True: | |
878 k = e[2] | |
879 dd = kdict[k] | |
880 return e == self._report(u, v, k, dd) | |
881 for k, dd in kdict.items(): | |
882 if e == self._report(u, v, k, dd): | |
883 return True | |
884 return False | |
885 | |
886 | |
887 # EdgeViews have set operations and no data reported | |
888 class OutEdgeView(Set, Mapping): | |
889 """A EdgeView class for outward edges of a DiGraph""" | |
890 __slots__ = ('_adjdict', '_graph', '_nodes_nbrs') | |
891 | |
892 def __getstate__(self): | |
893 return {'_graph': self._graph} | |
894 | |
895 def __setstate__(self, state): | |
896 self._graph = G = state['_graph'] | |
897 self._adjdict = G._succ if hasattr(G, "succ") else G._adj | |
898 self._nodes_nbrs = self._adjdict.items | |
899 | |
900 @classmethod | |
901 def _from_iterable(cls, it): | |
902 return set(it) | |
903 | |
904 dataview = OutEdgeDataView | |
905 | |
906 def __init__(self, G): | |
907 self._graph = G | |
908 self._adjdict = G._succ if hasattr(G, "succ") else G._adj | |
909 self._nodes_nbrs = self._adjdict.items | |
910 | |
911 # Set methods | |
912 def __len__(self): | |
913 return sum(len(nbrs) for n, nbrs in self._nodes_nbrs()) | |
914 | |
915 def __iter__(self): | |
916 for n, nbrs in self._nodes_nbrs(): | |
917 for nbr in nbrs: | |
918 yield (n, nbr) | |
919 | |
920 def __contains__(self, e): | |
921 try: | |
922 u, v = e | |
923 return v in self._adjdict[u] | |
924 except KeyError: | |
925 return False | |
926 | |
927 # Mapping Methods | |
928 def __getitem__(self, e): | |
929 u, v = e | |
930 return self._adjdict[u][v] | |
931 | |
932 # EdgeDataView methods | |
933 def __call__(self, nbunch=None, data=False, default=None): | |
934 if nbunch is None and data is False: | |
935 return self | |
936 return self.dataview(self, nbunch, data, default) | |
937 | |
938 def data(self, data=True, default=None, nbunch=None): | |
939 if nbunch is None and data is False: | |
940 return self | |
941 return self.dataview(self, nbunch, data, default) | |
942 | |
943 # String Methods | |
944 def __str__(self): | |
945 return str(list(self)) | |
946 | |
947 def __repr__(self): | |
948 return "{0.__class__.__name__}({1!r})".format(self, list(self)) | |
949 | |
950 | |
951 class EdgeView(OutEdgeView): | |
952 """A EdgeView class for edges of a Graph | |
953 | |
954 This densely packed View allows iteration over edges, data lookup | |
955 like a dict and set operations on edges represented by node-tuples. | |
956 In addition, edge data can be controlled by calling this object | |
957 possibly creating an EdgeDataView. Typically edges are iterated over | |
958 and reported as `(u, v)` node tuples or `(u, v, key)` node/key tuples | |
959 for multigraphs. Those edge representations can also be using to | |
960 lookup the data dict for any edge. Set operations also are available | |
961 where those tuples are the elements of the set. | |
962 Calling this object with optional arguments `data`, `default` and `keys` | |
963 controls the form of the tuple (see EdgeDataView). Optional argument | |
964 `nbunch` allows restriction to edges only involving certain nodes. | |
965 | |
966 If `data is False` (the default) then iterate over 2-tuples `(u, v)`. | |
967 If `data is True` iterate over 3-tuples `(u, v, datadict)`. | |
968 Otherwise iterate over `(u, v, datadict.get(data, default))`. | |
969 For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key` above. | |
970 | |
971 Parameters | |
972 ========== | |
973 graph : NetworkX graph-like class | |
974 nbunch : (default= all nodes in graph) only report edges with these nodes | |
975 keys : (only for MultiGraph. default=False) report edge key in tuple | |
976 data : bool or string (default=False) see above | |
977 default : object (default=None) | |
978 | |
979 Examples | |
980 ======== | |
981 >>> G = nx.path_graph(4) | |
982 >>> EV = G.edges() | |
983 >>> (2, 3) in EV | |
984 True | |
985 >>> for u, v in EV: print((u, v)) | |
986 (0, 1) | |
987 (1, 2) | |
988 (2, 3) | |
989 >>> assert(EV & {(1, 2), (3, 4)} == {(1, 2)}) | |
990 | |
991 >>> EVdata = G.edges(data='color', default='aqua') | |
992 >>> G.add_edge(2, 3, color='blue') | |
993 >>> assert((2, 3, 'blue') in EVdata) | |
994 >>> for u, v, c in EVdata: print("({}, {}) has color: {}".format(u, v, c)) | |
995 (0, 1) has color: aqua | |
996 (1, 2) has color: aqua | |
997 (2, 3) has color: blue | |
998 | |
999 >>> EVnbunch = G.edges(nbunch=2) | |
1000 >>> assert((2, 3) in EVnbunch) | |
1001 >>> assert((0, 1) in EVnbunch) # nbunch is ignored in __contains__ | |
1002 >>> for u, v in EVnbunch: assert(u == 2 or v == 2) | |
1003 | |
1004 >>> MG = nx.path_graph(4, create_using=nx.MultiGraph) | |
1005 >>> EVmulti = MG.edges(keys=True) | |
1006 >>> (2, 3, 0) in EVmulti | |
1007 True | |
1008 >>> (2, 3) in EVmulti # 2-tuples work even when keys is True | |
1009 True | |
1010 >>> key = MG.add_edge(2, 3) | |
1011 >>> for u, v, k in EVmulti: print((u, v, k)) | |
1012 (0, 1, 0) | |
1013 (1, 2, 0) | |
1014 (2, 3, 0) | |
1015 (2, 3, 1) | |
1016 """ | |
1017 __slots__ = () | |
1018 | |
1019 dataview = EdgeDataView | |
1020 | |
1021 def __len__(self): | |
1022 num_nbrs = (len(nbrs) + (n in nbrs) for n, nbrs in self._nodes_nbrs()) | |
1023 return sum(num_nbrs) // 2 | |
1024 | |
1025 def __iter__(self): | |
1026 seen = {} | |
1027 for n, nbrs in self._nodes_nbrs(): | |
1028 for nbr in list(nbrs): | |
1029 if nbr not in seen: | |
1030 yield (n, nbr) | |
1031 seen[n] = 1 | |
1032 del seen | |
1033 | |
1034 def __contains__(self, e): | |
1035 try: | |
1036 u, v = e[:2] | |
1037 return v in self._adjdict[u] or u in self._adjdict[v] | |
1038 except (KeyError, ValueError): | |
1039 return False | |
1040 | |
1041 | |
1042 class InEdgeView(OutEdgeView): | |
1043 """A EdgeView class for inward edges of a DiGraph""" | |
1044 __slots__ = () | |
1045 | |
1046 def __setstate__(self, state): | |
1047 self._graph = G = state['_graph'] | |
1048 self._adjdict = G._pred if hasattr(G, "pred") else G._adj | |
1049 self._nodes_nbrs = self._adjdict.items | |
1050 | |
1051 dataview = InEdgeDataView | |
1052 | |
1053 def __init__(self, G): | |
1054 self._graph = G | |
1055 self._adjdict = G._pred if hasattr(G, "pred") else G._adj | |
1056 self._nodes_nbrs = self._adjdict.items | |
1057 | |
1058 def __iter__(self): | |
1059 for n, nbrs in self._nodes_nbrs(): | |
1060 for nbr in nbrs: | |
1061 yield (nbr, n) | |
1062 | |
1063 def __contains__(self, e): | |
1064 try: | |
1065 u, v = e | |
1066 return u in self._adjdict[v] | |
1067 except KeyError: | |
1068 return False | |
1069 | |
1070 def __getitem__(self, e): | |
1071 u, v = e | |
1072 return self._adjdict[v][u] | |
1073 | |
1074 | |
1075 class OutMultiEdgeView(OutEdgeView): | |
1076 """A EdgeView class for outward edges of a MultiDiGraph""" | |
1077 __slots__ = () | |
1078 | |
1079 dataview = OutMultiEdgeDataView | |
1080 | |
1081 def __len__(self): | |
1082 return sum(len(kdict) for n, nbrs in self._nodes_nbrs() | |
1083 for nbr, kdict in nbrs.items()) | |
1084 | |
1085 def __iter__(self): | |
1086 for n, nbrs in self._nodes_nbrs(): | |
1087 for nbr, kdict in nbrs.items(): | |
1088 for key in kdict: | |
1089 yield (n, nbr, key) | |
1090 | |
1091 def __contains__(self, e): | |
1092 N = len(e) | |
1093 if N == 3: | |
1094 u, v, k = e | |
1095 elif N == 2: | |
1096 u, v = e | |
1097 k = 0 | |
1098 else: | |
1099 raise ValueError("MultiEdge must have length 2 or 3") | |
1100 try: | |
1101 return k in self._adjdict[u][v] | |
1102 except KeyError: | |
1103 return False | |
1104 | |
1105 def __getitem__(self, e): | |
1106 u, v, k = e | |
1107 return self._adjdict[u][v][k] | |
1108 | |
1109 def __call__(self, nbunch=None, data=False, keys=False, default=None): | |
1110 if nbunch is None and data is False and keys is True: | |
1111 return self | |
1112 return self.dataview(self, nbunch, data, keys, default) | |
1113 | |
1114 def data(self, data=True, keys=False, default=None, nbunch=None): | |
1115 if nbunch is None and data is False and keys is True: | |
1116 return self | |
1117 return self.dataview(self, nbunch, data, keys, default) | |
1118 | |
1119 | |
1120 class MultiEdgeView(OutMultiEdgeView): | |
1121 """A EdgeView class for edges of a MultiGraph""" | |
1122 __slots__ = () | |
1123 | |
1124 dataview = MultiEdgeDataView | |
1125 | |
1126 def __len__(self): | |
1127 return sum(1 for e in self) | |
1128 | |
1129 def __iter__(self): | |
1130 seen = {} | |
1131 for n, nbrs in self._nodes_nbrs(): | |
1132 for nbr, kd in nbrs.items(): | |
1133 if nbr not in seen: | |
1134 for k, dd in kd.items(): | |
1135 yield (n, nbr, k) | |
1136 seen[n] = 1 | |
1137 del seen | |
1138 | |
1139 | |
1140 class InMultiEdgeView(OutMultiEdgeView): | |
1141 """A EdgeView class for inward edges of a MultiDiGraph""" | |
1142 __slots__ = () | |
1143 | |
1144 def __setstate__(self, state): | |
1145 self._graph = G = state['_graph'] | |
1146 self._adjdict = G._pred if hasattr(G, "pred") else G._adj | |
1147 self._nodes_nbrs = self._adjdict.items | |
1148 | |
1149 dataview = InMultiEdgeDataView | |
1150 | |
1151 def __init__(self, G): | |
1152 self._graph = G | |
1153 self._adjdict = G._pred if hasattr(G, "pred") else G._adj | |
1154 self._nodes_nbrs = self._adjdict.items | |
1155 | |
1156 def __iter__(self): | |
1157 for n, nbrs in self._nodes_nbrs(): | |
1158 for nbr, kdict in nbrs.items(): | |
1159 for key in kdict: | |
1160 yield (nbr, n, key) | |
1161 | |
1162 def __contains__(self, e): | |
1163 N = len(e) | |
1164 if N == 3: | |
1165 u, v, k = e | |
1166 elif N == 2: | |
1167 u, v = e | |
1168 k = 0 | |
1169 else: | |
1170 raise ValueError("MultiEdge must have length 2 or 3") | |
1171 try: | |
1172 return k in self._adjdict[v][u] | |
1173 except KeyError: | |
1174 return False | |
1175 | |
1176 def __getitem__(self, e): | |
1177 u, v, k = e | |
1178 return self._adjdict[v][u][k] |