Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/networkx/classes/function.py: 22%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

288 statements  

1"""Functional interface to graph methods and assorted utilities.""" 

2 

3from collections import Counter 

4from itertools import chain 

5 

6import networkx as nx 

7from networkx.utils import not_implemented_for, pairwise 

8 

9__all__ = [ 

10 "nodes", 

11 "edges", 

12 "degree", 

13 "degree_histogram", 

14 "neighbors", 

15 "number_of_nodes", 

16 "number_of_edges", 

17 "density", 

18 "is_directed", 

19 "freeze", 

20 "is_frozen", 

21 "subgraph", 

22 "induced_subgraph", 

23 "edge_subgraph", 

24 "restricted_view", 

25 "to_directed", 

26 "to_undirected", 

27 "add_star", 

28 "add_path", 

29 "add_cycle", 

30 "create_empty_copy", 

31 "set_node_attributes", 

32 "get_node_attributes", 

33 "remove_node_attributes", 

34 "set_edge_attributes", 

35 "get_edge_attributes", 

36 "remove_edge_attributes", 

37 "all_neighbors", 

38 "non_neighbors", 

39 "non_edges", 

40 "common_neighbors", 

41 "is_weighted", 

42 "is_negatively_weighted", 

43 "is_empty", 

44 "selfloop_edges", 

45 "nodes_with_selfloops", 

46 "number_of_selfloops", 

47 "path_weight", 

48 "is_path", 

49] 

50 

51 

52def nodes(G): 

53 """Returns a NodeView over the graph nodes. 

54 

55 This function wraps the :func:`G.nodes <networkx.Graph.nodes>` property. 

56 """ 

57 return G.nodes() 

58 

59 

60def edges(G, nbunch=None): 

61 """Returns an edge view of edges incident to nodes in nbunch. 

62 

63 Return all edges if nbunch is unspecified or nbunch=None. 

64 

65 For digraphs, edges=out_edges 

66 

67 This function wraps the :func:`G.edges <networkx.Graph.edges>` property. 

68 """ 

69 return G.edges(nbunch) 

70 

71 

72def degree(G, nbunch=None, weight=None): 

73 """Returns a degree view of single node or of nbunch of nodes. 

74 If nbunch is omitted, then return degrees of *all* nodes. 

75 

76 This function wraps the :func:`G.degree <networkx.Graph.degree>` property. 

77 """ 

78 return G.degree(nbunch, weight) 

79 

80 

81def neighbors(G, n): 

82 """Returns an iterator over all neighbors of node n. 

83 

84 This function wraps the :func:`G.neighbors <networkx.Graph.neighbors>` function. 

85 """ 

86 return G.neighbors(n) 

87 

88 

89def number_of_nodes(G): 

90 """Returns the number of nodes in the graph. 

91 

92 This function wraps the :func:`G.number_of_nodes <networkx.Graph.number_of_nodes>` function. 

93 """ 

94 return G.number_of_nodes() 

95 

96 

97def number_of_edges(G): 

98 """Returns the number of edges in the graph. 

99 

100 This function wraps the :func:`G.number_of_edges <networkx.Graph.number_of_edges>` function. 

101 """ 

102 return G.number_of_edges() 

103 

104 

105def density(G): 

106 r"""Returns the density of a graph. 

107 

108 The density for undirected graphs is 

109 

110 .. math:: 

111 

112 d = \frac{2m}{n(n-1)}, 

113 

114 and for directed graphs is 

115 

116 .. math:: 

117 

118 d = \frac{m}{n(n-1)}, 

119 

120 where `n` is the number of nodes and `m` is the number of edges in `G`. 

121 

122 Notes 

123 ----- 

124 The density is 0 for a graph without edges and 1 for a complete graph. 

125 The density of multigraphs can be higher than 1. 

126 

127 Self loops are counted in the total number of edges so graphs with self 

128 loops can have density higher than 1. 

129 """ 

130 n = number_of_nodes(G) 

131 m = number_of_edges(G) 

132 if m == 0 or n <= 1: 

133 return 0 

134 d = m / (n * (n - 1)) 

135 if not G.is_directed(): 

136 d *= 2 

137 return d 

138 

139 

140def degree_histogram(G): 

141 """Returns a list of the frequency of each degree value. 

142 

143 Parameters 

144 ---------- 

145 G : Networkx graph 

146 A graph 

147 

148 Returns 

149 ------- 

150 hist : list 

151 A list of frequencies of degrees. 

152 The degree values are the index in the list. 

153 

154 Notes 

155 ----- 

156 Note: the bins are width one, hence len(list) can be large 

157 (Order(number_of_edges)) 

158 """ 

159 counts = Counter(d for n, d in G.degree()) 

160 return [counts.get(i, 0) for i in range(max(counts) + 1 if counts else 0)] 

161 

162 

163def is_directed(G): 

164 """Return True if graph is directed.""" 

165 return G.is_directed() 

166 

167 

168def frozen(*args, **kwargs): 

169 """Dummy method for raising errors when trying to modify frozen graphs""" 

170 raise nx.NetworkXError("Frozen graph can't be modified") 

171 

172 

173def freeze(G): 

174 """Modify graph to prevent further change by adding or removing 

175 nodes or edges. 

176 

177 Node and edge data can still be modified. 

178 

179 Parameters 

180 ---------- 

181 G : graph 

182 A NetworkX graph 

183 

184 Examples 

185 -------- 

186 >>> G = nx.path_graph(4) 

187 >>> G = nx.freeze(G) 

188 >>> try: 

189 ... G.add_edge(4, 5) 

190 ... except nx.NetworkXError as err: 

191 ... print(str(err)) 

192 Frozen graph can't be modified 

193 

194 Notes 

195 ----- 

196 To "unfreeze" a graph you must make a copy by creating a new graph object: 

197 

198 >>> graph = nx.path_graph(4) 

199 >>> frozen_graph = nx.freeze(graph) 

200 >>> unfrozen_graph = nx.Graph(frozen_graph) 

201 >>> nx.is_frozen(unfrozen_graph) 

202 False 

203 

204 See Also 

205 -------- 

206 is_frozen 

207 """ 

208 G.add_node = frozen 

209 G.add_nodes_from = frozen 

210 G.remove_node = frozen 

211 G.remove_nodes_from = frozen 

212 G.add_edge = frozen 

213 G.add_edges_from = frozen 

214 G.add_weighted_edges_from = frozen 

215 G.remove_edge = frozen 

216 G.remove_edges_from = frozen 

217 G.clear = frozen 

218 G.clear_edges = frozen 

219 G.frozen = True 

220 return G 

221 

222 

223def is_frozen(G): 

224 """Returns True if graph is frozen. 

225 

226 Parameters 

227 ---------- 

228 G : graph 

229 A NetworkX graph 

230 

231 See Also 

232 -------- 

233 freeze 

234 """ 

235 try: 

236 return G.frozen 

237 except AttributeError: 

238 return False 

239 

240 

241def add_star(G_to_add_to, nodes_for_star, **attr): 

242 """Add a star to Graph G_to_add_to. 

243 

244 The first node in `nodes_for_star` is the middle of the star. 

245 It is connected to all other nodes. 

246 

247 Parameters 

248 ---------- 

249 G_to_add_to : graph 

250 A NetworkX graph 

251 nodes_for_star : iterable container 

252 A container of nodes. 

253 attr : keyword arguments, optional (default= no attributes) 

254 Attributes to add to every edge in star. 

255 

256 See Also 

257 -------- 

258 add_path, add_cycle 

259 

260 Examples 

261 -------- 

262 >>> G = nx.Graph() 

263 >>> nx.add_star(G, [0, 1, 2, 3]) 

264 >>> nx.add_star(G, [10, 11, 12], weight=2) 

265 """ 

266 nlist = iter(nodes_for_star) 

267 try: 

268 v = next(nlist) 

269 except StopIteration: 

270 return 

271 G_to_add_to.add_node(v) 

272 edges = ((v, n) for n in nlist) 

273 G_to_add_to.add_edges_from(edges, **attr) 

274 

275 

276def add_path(G_to_add_to, nodes_for_path, **attr): 

277 """Add a path to the Graph G_to_add_to. 

278 

279 Parameters 

280 ---------- 

281 G_to_add_to : graph 

282 A NetworkX graph 

283 nodes_for_path : iterable container 

284 A container of nodes. A path will be constructed from 

285 the nodes (in order) and added to the graph. 

286 attr : keyword arguments, optional (default= no attributes) 

287 Attributes to add to every edge in path. 

288 

289 See Also 

290 -------- 

291 add_star, add_cycle 

292 

293 Examples 

294 -------- 

295 >>> G = nx.Graph() 

296 >>> nx.add_path(G, [0, 1, 2, 3]) 

297 >>> nx.add_path(G, [10, 11, 12], weight=7) 

298 """ 

299 nlist = iter(nodes_for_path) 

300 try: 

301 first_node = next(nlist) 

302 except StopIteration: 

303 return 

304 G_to_add_to.add_node(first_node) 

305 G_to_add_to.add_edges_from(pairwise(chain((first_node,), nlist)), **attr) 

306 

307 

308def add_cycle(G_to_add_to, nodes_for_cycle, **attr): 

309 """Add a cycle to the Graph G_to_add_to. 

310 

311 Parameters 

312 ---------- 

313 G_to_add_to : graph 

314 A NetworkX graph 

315 nodes_for_cycle: iterable container 

316 A container of nodes. A cycle will be constructed from 

317 the nodes (in order) and added to the graph. 

318 attr : keyword arguments, optional (default= no attributes) 

319 Attributes to add to every edge in cycle. 

320 

321 See Also 

322 -------- 

323 add_path, add_star 

324 

325 Examples 

326 -------- 

327 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc 

328 >>> nx.add_cycle(G, [0, 1, 2, 3]) 

329 >>> nx.add_cycle(G, [10, 11, 12], weight=7) 

330 """ 

331 nlist = iter(nodes_for_cycle) 

332 try: 

333 first_node = next(nlist) 

334 except StopIteration: 

335 return 

336 G_to_add_to.add_node(first_node) 

337 G_to_add_to.add_edges_from( 

338 pairwise(chain((first_node,), nlist), cyclic=True), **attr 

339 ) 

340 

341 

342def subgraph(G, nbunch): 

343 """Returns the subgraph induced on nodes in nbunch. 

344 

345 Parameters 

346 ---------- 

347 G : graph 

348 A NetworkX graph 

349 

350 nbunch : list, iterable 

351 A container of nodes that will be iterated through once (thus 

352 it should be an iterator or be iterable). Each element of the 

353 container should be a valid node type: any hashable type except 

354 None. If nbunch is None, return all edges data in the graph. 

355 Nodes in nbunch that are not in the graph will be (quietly) 

356 ignored. 

357 

358 Notes 

359 ----- 

360 subgraph(G) calls G.subgraph() 

361 """ 

362 return G.subgraph(nbunch) 

363 

364 

365def induced_subgraph(G, nbunch): 

366 """Returns a SubGraph view of `G` showing only nodes in nbunch. 

367 

368 The induced subgraph of a graph on a set of nodes N is the 

369 graph with nodes N and edges from G which have both ends in N. 

370 

371 Parameters 

372 ---------- 

373 G : NetworkX Graph 

374 nbunch : node, container of nodes or None (for all nodes) 

375 

376 Returns 

377 ------- 

378 subgraph : SubGraph View 

379 A read-only view of the subgraph in `G` induced by the nodes. 

380 Changes to the graph `G` will be reflected in the view. 

381 

382 Notes 

383 ----- 

384 To create a mutable subgraph with its own copies of nodes 

385 edges and attributes use `subgraph.copy()` or `Graph(subgraph)` 

386 

387 For an inplace reduction of a graph to a subgraph you can remove nodes: 

388 `G.remove_nodes_from(n in G if n not in set(nbunch))` 

389 

390 If you are going to compute subgraphs of your subgraphs you could 

391 end up with a chain of views that can be very slow once the chain 

392 has about 15 views in it. If they are all induced subgraphs, you 

393 can short-cut the chain by making them all subgraphs of the original 

394 graph. The graph class method `G.subgraph` does this when `G` is 

395 a subgraph. In contrast, this function allows you to choose to build 

396 chains or not, as you wish. The returned subgraph is a view on `G`. 

397 

398 Examples 

399 -------- 

400 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc 

401 >>> H = nx.induced_subgraph(G, [0, 1, 3]) 

402 >>> list(H.edges) 

403 [(0, 1)] 

404 >>> list(H.nodes) 

405 [0, 1, 3] 

406 """ 

407 induced_nodes = nx.filters.show_nodes(G.nbunch_iter(nbunch)) 

408 return nx.subgraph_view(G, filter_node=induced_nodes) 

409 

410 

411def edge_subgraph(G, edges): 

412 """Returns a view of the subgraph induced by the specified edges. 

413 

414 The induced subgraph contains each edge in `edges` and each 

415 node incident to any of those edges. 

416 

417 Parameters 

418 ---------- 

419 G : NetworkX Graph 

420 edges : iterable 

421 An iterable of edges. Edges not present in `G` are ignored. 

422 

423 Returns 

424 ------- 

425 subgraph : SubGraph View 

426 A read-only edge-induced subgraph of `G`. 

427 Changes to `G` are reflected in the view. 

428 

429 Notes 

430 ----- 

431 To create a mutable subgraph with its own copies of nodes 

432 edges and attributes use `subgraph.copy()` or `Graph(subgraph)` 

433 

434 If you create a subgraph of a subgraph recursively you can end up 

435 with a chain of subgraphs that becomes very slow with about 15 

436 nested subgraph views. Luckily the edge_subgraph filter nests 

437 nicely so you can use the original graph as G in this function 

438 to avoid chains. We do not rule out chains programmatically so 

439 that odd cases like an `edge_subgraph` of a `restricted_view` 

440 can be created. 

441 

442 Examples 

443 -------- 

444 >>> G = nx.path_graph(5) 

445 >>> H = G.edge_subgraph([(0, 1), (3, 4)]) 

446 >>> list(H.nodes) 

447 [0, 1, 3, 4] 

448 >>> list(H.edges) 

449 [(0, 1), (3, 4)] 

450 """ 

451 nxf = nx.filters 

452 edges = set(edges) 

453 nodes = set() 

454 for e in edges: 

455 nodes.update(e[:2]) 

456 induced_nodes = nxf.show_nodes(nodes) 

457 if G.is_multigraph(): 

458 if G.is_directed(): 

459 induced_edges = nxf.show_multidiedges(edges) 

460 else: 

461 induced_edges = nxf.show_multiedges(edges) 

462 else: 

463 if G.is_directed(): 

464 induced_edges = nxf.show_diedges(edges) 

465 else: 

466 induced_edges = nxf.show_edges(edges) 

467 return nx.subgraph_view(G, filter_node=induced_nodes, filter_edge=induced_edges) 

468 

469 

470def restricted_view(G, nodes, edges): 

471 """Returns a view of `G` with hidden nodes and edges. 

472 

473 The resulting subgraph filters out node `nodes` and edges `edges`. 

474 Filtered out nodes also filter out any of their edges. 

475 

476 Parameters 

477 ---------- 

478 G : NetworkX Graph 

479 nodes : iterable 

480 An iterable of nodes. Nodes not present in `G` are ignored. 

481 edges : iterable 

482 An iterable of edges. Edges not present in `G` are ignored. 

483 

484 Returns 

485 ------- 

486 subgraph : SubGraph View 

487 A read-only restricted view of `G` filtering out nodes and edges. 

488 Changes to `G` are reflected in the view. 

489 

490 Notes 

491 ----- 

492 To create a mutable subgraph with its own copies of nodes 

493 edges and attributes use `subgraph.copy()` or `Graph(subgraph)` 

494 

495 If you create a subgraph of a subgraph recursively you may end up 

496 with a chain of subgraph views. Such chains can get quite slow 

497 for lengths near 15. To avoid long chains, try to make your subgraph 

498 based on the original graph. We do not rule out chains programmatically 

499 so that odd cases like an `edge_subgraph` of a `restricted_view` 

500 can be created. 

501 

502 Examples 

503 -------- 

504 >>> G = nx.path_graph(5) 

505 >>> H = nx.restricted_view(G, [0], [(1, 2), (3, 4)]) 

506 >>> list(H.nodes) 

507 [1, 2, 3, 4] 

508 >>> list(H.edges) 

509 [(2, 3)] 

510 """ 

511 nxf = nx.filters 

512 hide_nodes = nxf.hide_nodes(nodes) 

513 if G.is_multigraph(): 

514 if G.is_directed(): 

515 hide_edges = nxf.hide_multidiedges(edges) 

516 else: 

517 hide_edges = nxf.hide_multiedges(edges) 

518 else: 

519 if G.is_directed(): 

520 hide_edges = nxf.hide_diedges(edges) 

521 else: 

522 hide_edges = nxf.hide_edges(edges) 

523 return nx.subgraph_view(G, filter_node=hide_nodes, filter_edge=hide_edges) 

524 

525 

526def to_directed(graph): 

527 """Returns a directed view of the graph `graph`. 

528 

529 Identical to graph.to_directed(as_view=True) 

530 Note that graph.to_directed defaults to `as_view=False` 

531 while this function always provides a view. 

532 """ 

533 return graph.to_directed(as_view=True) 

534 

535 

536def to_undirected(graph): 

537 """Returns an undirected view of the graph `graph`. 

538 

539 Identical to graph.to_undirected(as_view=True) 

540 Note that graph.to_undirected defaults to `as_view=False` 

541 while this function always provides a view. 

542 """ 

543 return graph.to_undirected(as_view=True) 

544 

545 

546def create_empty_copy(G, with_data=True): 

547 """Returns a copy of the graph G with all of the edges removed. 

548 

549 Parameters 

550 ---------- 

551 G : graph 

552 A NetworkX graph 

553 

554 with_data : bool (default=True) 

555 Propagate Graph and Nodes data to the new graph. 

556 

557 See Also 

558 -------- 

559 empty_graph 

560 

561 """ 

562 H = G.__class__() 

563 H.add_nodes_from(G.nodes(data=with_data)) 

564 if with_data: 

565 H.graph.update(G.graph) 

566 return H 

567 

568 

569@nx._dispatchable(preserve_node_attrs=True, mutates_input=True) 

570def set_node_attributes(G, values, name=None): 

571 """Sets node attributes from a given value or dictionary of values. 

572 

573 .. Warning:: The call order of arguments `values` and `name` 

574 switched between v1.x & v2.x. 

575 

576 Parameters 

577 ---------- 

578 G : NetworkX Graph 

579 

580 values : scalar value, dict-like 

581 What the node attribute should be set to. If `values` is 

582 not a dictionary, then it is treated as a single attribute value 

583 that is then applied to every node in `G`. This means that if 

584 you provide a mutable object, like a list, updates to that object 

585 will be reflected in the node attribute for every node. 

586 The attribute name will be `name`. 

587 

588 If `values` is a dict or a dict of dict, it should be keyed 

589 by node to either an attribute value or a dict of attribute key/value 

590 pairs used to update the node's attributes. 

591 

592 name : string (optional, default=None) 

593 Name of the node attribute to set if values is a scalar. 

594 

595 Examples 

596 -------- 

597 After computing some property of the nodes of a graph, you may want 

598 to assign a node attribute to store the value of that property for 

599 each node:: 

600 

601 >>> G = nx.path_graph(3) 

602 >>> bb = nx.betweenness_centrality(G) 

603 >>> isinstance(bb, dict) 

604 True 

605 >>> nx.set_node_attributes(G, bb, "betweenness") 

606 >>> G.nodes[1]["betweenness"] 

607 1.0 

608 

609 If you provide a list as the second argument, updates to the list 

610 will be reflected in the node attribute for each node:: 

611 

612 >>> G = nx.path_graph(3) 

613 >>> labels = [] 

614 >>> nx.set_node_attributes(G, labels, "labels") 

615 >>> labels.append("foo") 

616 >>> G.nodes[0]["labels"] 

617 ['foo'] 

618 >>> G.nodes[1]["labels"] 

619 ['foo'] 

620 >>> G.nodes[2]["labels"] 

621 ['foo'] 

622 

623 If you provide a dictionary of dictionaries as the second argument, 

624 the outer dictionary is assumed to be keyed by node to an inner 

625 dictionary of node attributes for that node:: 

626 

627 >>> G = nx.path_graph(3) 

628 >>> attrs = {0: {"attr1": 20, "attr2": "nothing"}, 1: {"attr2": 3}} 

629 >>> nx.set_node_attributes(G, attrs) 

630 >>> G.nodes[0]["attr1"] 

631 20 

632 >>> G.nodes[0]["attr2"] 

633 'nothing' 

634 >>> G.nodes[1]["attr2"] 

635 3 

636 >>> G.nodes[2] 

637 {} 

638 

639 Note that if the dictionary contains nodes that are not in `G`, the 

640 values are silently ignored:: 

641 

642 >>> G = nx.Graph() 

643 >>> G.add_node(0) 

644 >>> nx.set_node_attributes(G, {0: "red", 1: "blue"}, name="color") 

645 >>> G.nodes[0]["color"] 

646 'red' 

647 >>> 1 in G.nodes 

648 False 

649 

650 """ 

651 # Set node attributes based on type of `values` 

652 if name is not None: # `values` must not be a dict of dict 

653 try: # `values` is a dict 

654 for n, v in values.items(): 

655 try: 

656 G.nodes[n][name] = values[n] 

657 except KeyError: 

658 pass 

659 except AttributeError: # `values` is a constant 

660 for n in G: 

661 G.nodes[n][name] = values 

662 else: # `values` must be dict of dict 

663 for n, d in values.items(): 

664 try: 

665 G.nodes[n].update(d) 

666 except KeyError: 

667 pass 

668 nx._clear_cache(G) 

669 

670 

671@nx._dispatchable(node_attrs={"name": "default"}) 

672def get_node_attributes(G, name, default=None): 

673 """Get node attributes from graph 

674 

675 Parameters 

676 ---------- 

677 G : NetworkX Graph 

678 

679 name : string 

680 Attribute name 

681 

682 default: object (default=None) 

683 Default value of the node attribute if there is no value set for that 

684 node in graph. If `None` then nodes without this attribute are not 

685 included in the returned dict. 

686 

687 Returns 

688 ------- 

689 Dictionary of attributes keyed by node. 

690 

691 Examples 

692 -------- 

693 >>> G = nx.Graph() 

694 >>> G.add_nodes_from([1, 2, 3], color="red") 

695 >>> color = nx.get_node_attributes(G, "color") 

696 >>> color[1] 

697 'red' 

698 >>> G.add_node(4) 

699 >>> color = nx.get_node_attributes(G, "color", default="yellow") 

700 >>> color[4] 

701 'yellow' 

702 """ 

703 if default is not None: 

704 return {n: d.get(name, default) for n, d in G.nodes.items()} 

705 return {n: d[name] for n, d in G.nodes.items() if name in d} 

706 

707 

708@nx._dispatchable(preserve_node_attrs=True, mutates_input=True) 

709def remove_node_attributes(G, *attr_names, nbunch=None): 

710 """Remove node attributes from all nodes in the graph. 

711 

712 Parameters 

713 ---------- 

714 G : NetworkX Graph 

715 

716 *attr_names : List of Strings 

717 The attribute names to remove from the graph. 

718 

719 nbunch : List of Nodes 

720 Remove the node attributes only from the nodes in this list. 

721 

722 Examples 

723 -------- 

724 >>> G = nx.Graph() 

725 >>> G.add_nodes_from([1, 2, 3], color="blue") 

726 >>> nx.get_node_attributes(G, "color") 

727 {1: 'blue', 2: 'blue', 3: 'blue'} 

728 >>> nx.remove_node_attributes(G, "color") 

729 >>> nx.get_node_attributes(G, "color") 

730 {} 

731 """ 

732 

733 if nbunch is None: 

734 nbunch = G.nodes() 

735 

736 for attr in attr_names: 

737 for n, d in G.nodes(data=True): 

738 if n in nbunch: 

739 try: 

740 del d[attr] 

741 except KeyError: 

742 pass 

743 

744 

745@nx._dispatchable(preserve_edge_attrs=True, mutates_input=True) 

746def set_edge_attributes(G, values, name=None): 

747 """Sets edge attributes from a given value or dictionary of values. 

748 

749 .. Warning:: The call order of arguments `values` and `name` 

750 switched between v1.x & v2.x. 

751 

752 Parameters 

753 ---------- 

754 G : NetworkX Graph 

755 

756 values : scalar value, dict-like 

757 What the edge attribute should be set to. If `values` is 

758 not a dictionary, then it is treated as a single attribute value 

759 that is then applied to every edge in `G`. This means that if 

760 you provide a mutable object, like a list, updates to that object 

761 will be reflected in the edge attribute for each edge. The attribute 

762 name will be `name`. 

763 

764 If `values` is a dict or a dict of dict, it should be keyed 

765 by edge tuple to either an attribute value or a dict of attribute 

766 key/value pairs used to update the edge's attributes. 

767 For multigraphs, the edge tuples must be of the form ``(u, v, key)``, 

768 where `u` and `v` are nodes and `key` is the edge key. 

769 For non-multigraphs, the keys must be tuples of the form ``(u, v)``. 

770 

771 name : string (optional, default=None) 

772 Name of the edge attribute to set if values is a scalar. 

773 

774 Examples 

775 -------- 

776 After computing some property of the edges of a graph, you may want 

777 to assign a edge attribute to store the value of that property for 

778 each edge:: 

779 

780 >>> G = nx.path_graph(3) 

781 >>> bb = nx.edge_betweenness_centrality(G, normalized=False) 

782 >>> nx.set_edge_attributes(G, bb, "betweenness") 

783 >>> G.edges[1, 2]["betweenness"] 

784 2.0 

785 

786 If you provide a list as the second argument, updates to the list 

787 will be reflected in the edge attribute for each edge:: 

788 

789 >>> labels = [] 

790 >>> nx.set_edge_attributes(G, labels, "labels") 

791 >>> labels.append("foo") 

792 >>> G.edges[0, 1]["labels"] 

793 ['foo'] 

794 >>> G.edges[1, 2]["labels"] 

795 ['foo'] 

796 

797 If you provide a dictionary of dictionaries as the second argument, 

798 the entire dictionary will be used to update edge attributes:: 

799 

800 >>> G = nx.path_graph(3) 

801 >>> attrs = {(0, 1): {"attr1": 20, "attr2": "nothing"}, (1, 2): {"attr2": 3}} 

802 >>> nx.set_edge_attributes(G, attrs) 

803 >>> G[0][1]["attr1"] 

804 20 

805 >>> G[0][1]["attr2"] 

806 'nothing' 

807 >>> G[1][2]["attr2"] 

808 3 

809 

810 The attributes of one Graph can be used to set those of another. 

811 

812 >>> H = nx.path_graph(3) 

813 >>> nx.set_edge_attributes(H, G.edges) 

814 

815 Note that if the dict contains edges that are not in `G`, they are 

816 silently ignored:: 

817 

818 >>> G = nx.Graph([(0, 1)]) 

819 >>> nx.set_edge_attributes(G, {(1, 2): {"weight": 2.0}}) 

820 >>> (1, 2) in G.edges() 

821 False 

822 

823 For multigraphs, the `values` dict is expected to be keyed by 3-tuples 

824 including the edge key:: 

825 

826 >>> MG = nx.MultiGraph() 

827 >>> edges = [(0, 1), (0, 1)] 

828 >>> MG.add_edges_from(edges) # Returns list of edge keys 

829 [0, 1] 

830 >>> attributes = {(0, 1, 0): {"cost": 21}, (0, 1, 1): {"cost": 7}} 

831 >>> nx.set_edge_attributes(MG, attributes) 

832 >>> MG[0][1][0]["cost"] 

833 21 

834 >>> MG[0][1][1]["cost"] 

835 7 

836 

837 If MultiGraph attributes are desired for a Graph, you must convert the 3-tuple 

838 multiedge to a 2-tuple edge and the last multiedge's attribute value will 

839 overwrite the previous values. Continuing from the previous case we get:: 

840 

841 >>> H = nx.path_graph([0, 1, 2]) 

842 >>> nx.set_edge_attributes(H, {(u, v): ed for u, v, ed in MG.edges.data()}) 

843 >>> nx.get_edge_attributes(H, "cost") 

844 {(0, 1): 7} 

845 

846 """ 

847 if name is not None: 

848 # `values` does not contain attribute names 

849 try: 

850 # if `values` is a dict using `.items()` => {edge: value} 

851 if G.is_multigraph(): 

852 for (u, v, key), value in values.items(): 

853 try: 

854 G._adj[u][v][key][name] = value 

855 except KeyError: 

856 pass 

857 else: 

858 for (u, v), value in values.items(): 

859 try: 

860 G._adj[u][v][name] = value 

861 except KeyError: 

862 pass 

863 except AttributeError: 

864 # treat `values` as a constant 

865 for u, v, data in G.edges(data=True): 

866 data[name] = values 

867 else: 

868 # `values` consists of doct-of-dict {edge: {attr: value}} shape 

869 if G.is_multigraph(): 

870 for (u, v, key), d in values.items(): 

871 try: 

872 G._adj[u][v][key].update(d) 

873 except KeyError: 

874 pass 

875 else: 

876 for (u, v), d in values.items(): 

877 try: 

878 G._adj[u][v].update(d) 

879 except KeyError: 

880 pass 

881 nx._clear_cache(G) 

882 

883 

884@nx._dispatchable(edge_attrs={"name": "default"}) 

885def get_edge_attributes(G, name, default=None): 

886 """Get edge attributes from graph 

887 

888 Parameters 

889 ---------- 

890 G : NetworkX Graph 

891 

892 name : string 

893 Attribute name 

894 

895 default: object (default=None) 

896 Default value of the edge attribute if there is no value set for that 

897 edge in graph. If `None` then edges without this attribute are not 

898 included in the returned dict. 

899 

900 Returns 

901 ------- 

902 Dictionary of attributes keyed by edge. For (di)graphs, the keys are 

903 2-tuples of the form: (u, v). For multi(di)graphs, the keys are 3-tuples of 

904 the form: (u, v, key). 

905 

906 Examples 

907 -------- 

908 >>> G = nx.Graph() 

909 >>> nx.add_path(G, [1, 2, 3], color="red") 

910 >>> color = nx.get_edge_attributes(G, "color") 

911 >>> color[(1, 2)] 

912 'red' 

913 >>> G.add_edge(3, 4) 

914 >>> color = nx.get_edge_attributes(G, "color", default="yellow") 

915 >>> color[(3, 4)] 

916 'yellow' 

917 """ 

918 if G.is_multigraph(): 

919 edges = G.edges(keys=True, data=True) 

920 else: 

921 edges = G.edges(data=True) 

922 if default is not None: 

923 return {x[:-1]: x[-1].get(name, default) for x in edges} 

924 return {x[:-1]: x[-1][name] for x in edges if name in x[-1]} 

925 

926 

927@nx._dispatchable(preserve_edge_attrs=True, mutates_input=True) 

928def remove_edge_attributes(G, *attr_names, ebunch=None): 

929 """Remove edge attributes from all edges in the graph. 

930 

931 Parameters 

932 ---------- 

933 G : NetworkX Graph 

934 

935 *attr_names : List of Strings 

936 The attribute names to remove from the graph. 

937 

938 Examples 

939 -------- 

940 >>> G = nx.path_graph(3) 

941 >>> nx.set_edge_attributes(G, {(u, v): u + v for u, v in G.edges()}, name="weight") 

942 >>> nx.get_edge_attributes(G, "weight") 

943 {(0, 1): 1, (1, 2): 3} 

944 >>> remove_edge_attributes(G, "weight") 

945 >>> nx.get_edge_attributes(G, "weight") 

946 {} 

947 """ 

948 if ebunch is None: 

949 ebunch = G.edges(keys=True) if G.is_multigraph() else G.edges() 

950 

951 for attr in attr_names: 

952 edges = ( 

953 G.edges(keys=True, data=True) if G.is_multigraph() else G.edges(data=True) 

954 ) 

955 for *e, d in edges: 

956 if tuple(e) in ebunch: 

957 try: 

958 del d[attr] 

959 except KeyError: 

960 pass 

961 

962 

963def all_neighbors(graph, node): 

964 """Returns all of the neighbors of a node in the graph. 

965 

966 If the graph is directed returns predecessors as well as successors. 

967 

968 Parameters 

969 ---------- 

970 graph : NetworkX graph 

971 Graph to find neighbors. 

972 node : node 

973 The node whose neighbors will be returned. 

974 

975 Returns 

976 ------- 

977 neighbors : iterator 

978 Iterator of neighbors 

979 

980 Raises 

981 ------ 

982 NetworkXError 

983 If `node` is not in the graph. 

984 

985 Examples 

986 -------- 

987 For undirected graphs, this function is equivalent to ``G.neighbors(node)``. 

988 

989 >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc 

990 >>> list(nx.all_neighbors(G, 1)) 

991 [0, 2] 

992 

993 For directed graphs, this function returns both predecessors and successors, 

994 which may include duplicates if a node is both a predecessor and successor 

995 (e.g., in bidirectional edges or self-loops). 

996 

997 >>> DG = nx.DiGraph([(0, 1), (1, 2), (2, 1)]) 

998 >>> list(nx.all_neighbors(DG, 1)) 

999 [0, 2, 2] 

1000 

1001 Notes 

1002 ----- 

1003 This function iterates over all neighbors (both predecessors and successors). 

1004 

1005 See Also 

1006 -------- 

1007 Graph.neighbors : Returns successors for both Graph and DiGraph 

1008 DiGraph.predecessors : Returns predecessors for directed graphs only 

1009 DiGraph.successors : Returns successors for directed graphs only 

1010 """ 

1011 if graph.is_directed(): 

1012 values = chain(graph.predecessors(node), graph.successors(node)) 

1013 else: 

1014 values = graph.neighbors(node) 

1015 return values 

1016 

1017 

1018def non_neighbors(graph, node): 

1019 """Returns the non-neighbors of the node in the graph. 

1020 

1021 Parameters 

1022 ---------- 

1023 graph : NetworkX graph 

1024 Graph to find neighbors. 

1025 

1026 node : node 

1027 The node whose neighbors will be returned. 

1028 

1029 Returns 

1030 ------- 

1031 non_neighbors : set 

1032 Set of nodes in the graph that are not neighbors of the node. 

1033 """ 

1034 return graph._adj.keys() - graph._adj[node].keys() - {node} 

1035 

1036 

1037def non_edges(graph): 

1038 """Returns the nonexistent edges in the graph. 

1039 

1040 Parameters 

1041 ---------- 

1042 graph : NetworkX graph. 

1043 Graph to find nonexistent edges. 

1044 

1045 Returns 

1046 ------- 

1047 non_edges : iterator 

1048 Iterator of edges that are not in the graph. 

1049 """ 

1050 if graph.is_directed(): 

1051 for u in graph: 

1052 for v in non_neighbors(graph, u): 

1053 yield (u, v) 

1054 else: 

1055 nodes = set(graph) 

1056 while nodes: 

1057 u = nodes.pop() 

1058 for v in nodes - set(graph[u]): 

1059 yield (u, v) 

1060 

1061 

1062@not_implemented_for("directed") 

1063def common_neighbors(G, u, v): 

1064 """Returns the common neighbors of two nodes in a graph. 

1065 

1066 Parameters 

1067 ---------- 

1068 G : graph 

1069 A NetworkX undirected graph. 

1070 

1071 u, v : nodes 

1072 Nodes in the graph. 

1073 

1074 Returns 

1075 ------- 

1076 cnbors : set 

1077 Set of common neighbors of u and v in the graph. 

1078 

1079 Raises 

1080 ------ 

1081 NetworkXError 

1082 If u or v is not a node in the graph. 

1083 

1084 Examples 

1085 -------- 

1086 >>> G = nx.complete_graph(5) 

1087 >>> sorted(nx.common_neighbors(G, 0, 1)) 

1088 [2, 3, 4] 

1089 """ 

1090 if u not in G: 

1091 raise nx.NetworkXError("u is not in the graph.") 

1092 if v not in G: 

1093 raise nx.NetworkXError("v is not in the graph.") 

1094 

1095 return G._adj[u].keys() & G._adj[v].keys() - {u, v} 

1096 

1097 

1098@nx._dispatchable(preserve_edge_attrs=True) 

1099def is_weighted(G, edge=None, weight="weight"): 

1100 """Returns True if `G` has weighted edges. 

1101 

1102 Parameters 

1103 ---------- 

1104 G : graph 

1105 A NetworkX graph. 

1106 

1107 edge : tuple, optional 

1108 A 2-tuple specifying the only edge in `G` that will be tested. If 

1109 None, then every edge in `G` is tested. 

1110 

1111 weight: string, optional 

1112 The attribute name used to query for edge weights. 

1113 

1114 Returns 

1115 ------- 

1116 bool 

1117 A boolean signifying if `G`, or the specified edge, is weighted. 

1118 

1119 Raises 

1120 ------ 

1121 NetworkXError 

1122 If the specified edge does not exist. 

1123 

1124 Examples 

1125 -------- 

1126 >>> G = nx.path_graph(4) 

1127 >>> nx.is_weighted(G) 

1128 False 

1129 >>> nx.is_weighted(G, (2, 3)) 

1130 False 

1131 

1132 >>> G = nx.DiGraph() 

1133 >>> G.add_edge(1, 2, weight=1) 

1134 >>> nx.is_weighted(G) 

1135 True 

1136 

1137 """ 

1138 if edge is not None: 

1139 data = G.get_edge_data(*edge) 

1140 if data is None: 

1141 msg = f"Edge {edge!r} does not exist." 

1142 raise nx.NetworkXError(msg) 

1143 return weight in data 

1144 

1145 if is_empty(G): 

1146 # Special handling required since: all([]) == True 

1147 return False 

1148 

1149 return all(weight in data for u, v, data in G.edges(data=True)) 

1150 

1151 

1152@nx._dispatchable(edge_attrs="weight") 

1153def is_negatively_weighted(G, edge=None, weight="weight"): 

1154 """Returns True if `G` has negatively weighted edges. 

1155 

1156 Parameters 

1157 ---------- 

1158 G : graph 

1159 A NetworkX graph. 

1160 

1161 edge : tuple, optional 

1162 A 2-tuple specifying the only edge in `G` that will be tested. If 

1163 None, then every edge in `G` is tested. 

1164 

1165 weight: string, optional 

1166 The attribute name used to query for edge weights. 

1167 

1168 Returns 

1169 ------- 

1170 bool 

1171 A boolean signifying if `G`, or the specified edge, is negatively 

1172 weighted. 

1173 

1174 Raises 

1175 ------ 

1176 NetworkXError 

1177 If the specified edge does not exist. 

1178 

1179 Examples 

1180 -------- 

1181 >>> G = nx.Graph() 

1182 >>> G.add_edges_from([(1, 3), (2, 4), (2, 6)]) 

1183 >>> G.add_edge(1, 2, weight=4) 

1184 >>> nx.is_negatively_weighted(G, (1, 2)) 

1185 False 

1186 >>> G[2][4]["weight"] = -2 

1187 >>> nx.is_negatively_weighted(G) 

1188 True 

1189 >>> G = nx.DiGraph() 

1190 >>> edges = [("0", "3", 3), ("0", "1", -5), ("1", "0", -2)] 

1191 >>> G.add_weighted_edges_from(edges) 

1192 >>> nx.is_negatively_weighted(G) 

1193 True 

1194 

1195 """ 

1196 if edge is not None: 

1197 data = G.get_edge_data(*edge) 

1198 if data is None: 

1199 msg = f"Edge {edge!r} does not exist." 

1200 raise nx.NetworkXError(msg) 

1201 return weight in data and data[weight] < 0 

1202 

1203 return any(weight in data and data[weight] < 0 for u, v, data in G.edges(data=True)) 

1204 

1205 

1206@nx._dispatchable 

1207def is_empty(G): 

1208 """Returns True if `G` has no edges. 

1209 

1210 Parameters 

1211 ---------- 

1212 G : graph 

1213 A NetworkX graph. 

1214 

1215 Returns 

1216 ------- 

1217 bool 

1218 True if `G` has no edges, and False otherwise. 

1219 

1220 Notes 

1221 ----- 

1222 An empty graph can have nodes but not edges. The empty graph with zero 

1223 nodes is known as the null graph. This is an $O(n)$ operation where n 

1224 is the number of nodes in the graph. 

1225 

1226 """ 

1227 return not any(G._adj.values()) 

1228 

1229 

1230def nodes_with_selfloops(G): 

1231 """Returns an iterator over nodes with self loops. 

1232 

1233 A node with a self loop has an edge with both ends adjacent 

1234 to that node. 

1235 

1236 Returns 

1237 ------- 

1238 nodelist : iterator 

1239 A iterator over nodes with self loops. 

1240 

1241 See Also 

1242 -------- 

1243 selfloop_edges, number_of_selfloops 

1244 

1245 Examples 

1246 -------- 

1247 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc 

1248 >>> G.add_edge(1, 1) 

1249 >>> G.add_edge(1, 2) 

1250 >>> list(nx.nodes_with_selfloops(G)) 

1251 [1] 

1252 

1253 """ 

1254 return (n for n, nbrs in G._adj.items() if n in nbrs) 

1255 

1256 

1257def selfloop_edges(G, data=False, keys=False, default=None): 

1258 """Returns an iterator over selfloop edges. 

1259 

1260 A selfloop edge has the same node at both ends. 

1261 

1262 Parameters 

1263 ---------- 

1264 G : graph 

1265 A NetworkX graph. 

1266 data : string or bool, optional (default=False) 

1267 Return selfloop edges as two tuples (u, v) (data=False) 

1268 or three-tuples (u, v, datadict) (data=True) 

1269 or three-tuples (u, v, datavalue) (data='attrname') 

1270 keys : bool, optional (default=False) 

1271 If True, return edge keys with each edge. 

1272 default : value, optional (default=None) 

1273 Value used for edges that don't have the requested attribute. 

1274 Only relevant if data is not True or False. 

1275 

1276 Returns 

1277 ------- 

1278 edgeiter : iterator over edge tuples 

1279 An iterator over all selfloop edges. 

1280 

1281 See Also 

1282 -------- 

1283 nodes_with_selfloops, number_of_selfloops 

1284 

1285 Examples 

1286 -------- 

1287 >>> G = nx.MultiGraph() # or Graph, DiGraph, MultiDiGraph, etc 

1288 >>> ekey = G.add_edge(1, 1) 

1289 >>> ekey = G.add_edge(1, 2) 

1290 >>> list(nx.selfloop_edges(G)) 

1291 [(1, 1)] 

1292 >>> list(nx.selfloop_edges(G, data=True)) 

1293 [(1, 1, {})] 

1294 >>> list(nx.selfloop_edges(G, keys=True)) 

1295 [(1, 1, 0)] 

1296 >>> list(nx.selfloop_edges(G, keys=True, data=True)) 

1297 [(1, 1, 0, {})] 

1298 """ 

1299 if data is True: 

1300 if G.is_multigraph(): 

1301 if keys is True: 

1302 return ( 

1303 (n, n, k, d) 

1304 for n, nbrs in G._adj.items() 

1305 if n in nbrs 

1306 for k, d in nbrs[n].items() 

1307 ) 

1308 else: 

1309 return ( 

1310 (n, n, d) 

1311 for n, nbrs in G._adj.items() 

1312 if n in nbrs 

1313 for d in nbrs[n].values() 

1314 ) 

1315 else: 

1316 return ((n, n, nbrs[n]) for n, nbrs in G._adj.items() if n in nbrs) 

1317 elif data is not False: 

1318 if G.is_multigraph(): 

1319 if keys is True: 

1320 return ( 

1321 (n, n, k, d.get(data, default)) 

1322 for n, nbrs in G._adj.items() 

1323 if n in nbrs 

1324 for k, d in nbrs[n].items() 

1325 ) 

1326 else: 

1327 return ( 

1328 (n, n, d.get(data, default)) 

1329 for n, nbrs in G._adj.items() 

1330 if n in nbrs 

1331 for d in nbrs[n].values() 

1332 ) 

1333 else: 

1334 return ( 

1335 (n, n, nbrs[n].get(data, default)) 

1336 for n, nbrs in G._adj.items() 

1337 if n in nbrs 

1338 ) 

1339 else: 

1340 if G.is_multigraph(): 

1341 if keys is True: 

1342 return ( 

1343 (n, n, k) 

1344 for n, nbrs in G._adj.items() 

1345 if n in nbrs 

1346 for k in nbrs[n] 

1347 ) 

1348 else: 

1349 return ( 

1350 (n, n) 

1351 for n, nbrs in G._adj.items() 

1352 if n in nbrs 

1353 for i in range(len(nbrs[n])) # for easy edge removal (#4068) 

1354 ) 

1355 else: 

1356 return ((n, n) for n, nbrs in G._adj.items() if n in nbrs) 

1357 

1358 

1359@nx._dispatchable 

1360def number_of_selfloops(G): 

1361 """Returns the number of selfloop edges. 

1362 

1363 A selfloop edge has the same node at both ends. 

1364 

1365 Returns 

1366 ------- 

1367 nloops : int 

1368 The number of selfloops. 

1369 

1370 See Also 

1371 -------- 

1372 nodes_with_selfloops, selfloop_edges 

1373 

1374 Examples 

1375 -------- 

1376 >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc 

1377 >>> G.add_edge(1, 1) 

1378 >>> G.add_edge(1, 2) 

1379 >>> nx.number_of_selfloops(G) 

1380 1 

1381 """ 

1382 return sum(1 for _ in nx.selfloop_edges(G)) 

1383 

1384 

1385def is_path(G, path): 

1386 """Returns whether or not the specified path exists. 

1387 

1388 For it to return True, every node on the path must exist and 

1389 each consecutive pair must be connected via one or more edges. 

1390 

1391 Parameters 

1392 ---------- 

1393 G : graph 

1394 A NetworkX graph. 

1395 

1396 path : list 

1397 A list of nodes which defines the path to traverse 

1398 

1399 Returns 

1400 ------- 

1401 bool 

1402 True if `path` is a valid path in `G` 

1403 

1404 """ 

1405 try: 

1406 return all(nbr in G._adj[node] for node, nbr in nx.utils.pairwise(path)) 

1407 except (KeyError, TypeError): 

1408 return False 

1409 

1410 

1411def path_weight(G, path, weight): 

1412 """Returns total cost associated with specified path and weight 

1413 

1414 Parameters 

1415 ---------- 

1416 G : graph 

1417 A NetworkX graph. 

1418 

1419 path: list 

1420 A list of node labels which defines the path to traverse 

1421 

1422 weight: string 

1423 A string indicating which edge attribute to use for path cost 

1424 

1425 Returns 

1426 ------- 

1427 cost: int or float 

1428 An integer or a float representing the total cost with respect to the 

1429 specified weight of the specified path 

1430 

1431 Raises 

1432 ------ 

1433 NetworkXNoPath 

1434 If the specified edge does not exist. 

1435 """ 

1436 multigraph = G.is_multigraph() 

1437 cost = 0 

1438 

1439 if not nx.is_path(G, path): 

1440 raise nx.NetworkXNoPath("path does not exist") 

1441 for node, nbr in nx.utils.pairwise(path): 

1442 if multigraph: 

1443 cost += min(v[weight] for v in G._adj[node][nbr].values()) 

1444 else: 

1445 cost += G._adj[node][nbr][weight] 

1446 return cost