Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/networkx/convert.py: 24%

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

155 statements  

1"""Functions to convert NetworkX graphs to and from other formats. 

2 

3The preferred way of converting data to a NetworkX graph is through the 

4graph constructor. The constructor calls the to_networkx_graph() function 

5which attempts to guess the input type and convert it automatically. 

6 

7Examples 

8-------- 

9Create a graph with a single edge from a dictionary of dictionaries 

10 

11>>> d = {0: {1: 1}} # dict-of-dicts single edge (0,1) 

12>>> G = nx.Graph(d) 

13 

14See Also 

15-------- 

16nx_agraph, nx_pydot 

17""" 

18 

19from collections.abc import Collection, Generator, Iterator 

20 

21import networkx as nx 

22 

23__all__ = [ 

24 "to_networkx_graph", 

25 "from_dict_of_dicts", 

26 "to_dict_of_dicts", 

27 "from_dict_of_lists", 

28 "to_dict_of_lists", 

29 "from_edgelist", 

30 "to_edgelist", 

31] 

32 

33 

34def to_networkx_graph(data, create_using=None, multigraph_input=False): 

35 """Make a NetworkX graph from a known data structure. 

36 

37 The preferred way to call this is automatically 

38 from the class constructor 

39 

40 >>> d = {0: {1: {"weight": 1}}} # dict-of-dicts single edge (0,1) 

41 >>> G = nx.Graph(d) 

42 

43 instead of the equivalent 

44 

45 >>> G = nx.from_dict_of_dicts(d) 

46 

47 Parameters 

48 ---------- 

49 data : object to be converted 

50 

51 Current known types are: 

52 any NetworkX graph 

53 dict-of-dicts 

54 dict-of-lists 

55 container (e.g. set, list, tuple) of edges 

56 iterator (e.g. itertools.chain) that produces edges 

57 generator of edges 

58 Pandas DataFrame (row per edge) 

59 2D numpy array 

60 scipy sparse array 

61 pygraphviz agraph 

62 

63 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

64 Graph type to create. If graph instance, then cleared before populated. 

65 

66 multigraph_input : bool (default False) 

67 If True and data is a dict_of_dicts, 

68 try to create a multigraph assuming dict_of_dict_of_lists. 

69 If data and create_using are both multigraphs then create 

70 a multigraph from a multigraph. 

71 

72 """ 

73 # NX graph 

74 if hasattr(data, "adj"): 

75 try: 

76 result = from_dict_of_dicts( 

77 data.adj, 

78 create_using=create_using, 

79 multigraph_input=data.is_multigraph(), 

80 ) 

81 # data.graph should be dict-like 

82 result.graph.update(data.graph) 

83 # data.nodes should be dict-like 

84 # result.add_node_from(data.nodes.items()) possible but 

85 # for custom node_attr_dict_factory which may be hashable 

86 # will be unexpected behavior 

87 for n, dd in data.nodes.items(): 

88 result._node[n].update(dd) 

89 return result 

90 except Exception as err: 

91 raise nx.NetworkXError("Input is not a correct NetworkX graph.") from err 

92 

93 # dict of dicts/lists 

94 if isinstance(data, dict): 

95 try: 

96 return from_dict_of_dicts( 

97 data, create_using=create_using, multigraph_input=multigraph_input 

98 ) 

99 except Exception as err1: 

100 if multigraph_input is True: 

101 raise nx.NetworkXError( 

102 f"converting multigraph_input raised:\n{type(err1)}: {err1}" 

103 ) 

104 try: 

105 return from_dict_of_lists(data, create_using=create_using) 

106 except Exception as err2: 

107 raise TypeError("Input is not known type.") from err2 

108 

109 # edgelists 

110 if isinstance(data, list | tuple | nx.reportviews.EdgeViewABC | Iterator): 

111 try: 

112 return from_edgelist(data, create_using=create_using) 

113 except: 

114 pass 

115 

116 # pygraphviz agraph 

117 if hasattr(data, "is_strict"): 

118 try: 

119 return nx.nx_agraph.from_agraph(data, create_using=create_using) 

120 except Exception as err: 

121 raise nx.NetworkXError("Input is not a correct pygraphviz graph.") from err 

122 

123 # Pandas DataFrame 

124 try: 

125 import pandas as pd 

126 

127 if isinstance(data, pd.DataFrame): 

128 if data.shape[0] == data.shape[1]: 

129 try: 

130 return nx.from_pandas_adjacency(data, create_using=create_using) 

131 except Exception as err: 

132 msg = "Input is not a correct Pandas DataFrame adjacency matrix." 

133 raise nx.NetworkXError(msg) from err 

134 else: 

135 try: 

136 return nx.from_pandas_edgelist( 

137 data, edge_attr=True, create_using=create_using 

138 ) 

139 except Exception as err: 

140 msg = "Input is not a correct Pandas DataFrame edge-list." 

141 raise nx.NetworkXError(msg) from err 

142 except ImportError: 

143 pass 

144 

145 # numpy array 

146 try: 

147 import numpy as np 

148 

149 if isinstance(data, np.ndarray): 

150 try: 

151 return nx.from_numpy_array(data, create_using=create_using) 

152 except Exception as err: 

153 raise nx.NetworkXError( 

154 f"Failed to interpret array as an adjacency matrix." 

155 ) from err 

156 except ImportError: 

157 pass 

158 

159 # scipy sparse array - any format 

160 try: 

161 import scipy as sp 

162 

163 if hasattr(data, "format"): 

164 try: 

165 return nx.from_scipy_sparse_array(data, create_using=create_using) 

166 except Exception as err: 

167 raise nx.NetworkXError( 

168 "Input is not a correct scipy sparse array type." 

169 ) from err 

170 except ImportError: 

171 pass 

172 

173 # Note: most general check - should remain last in order of execution 

174 # Includes containers (e.g. list, set, dict, etc.), generators, and 

175 # iterators (e.g. itertools.chain) of edges 

176 

177 if isinstance(data, Collection | Generator | Iterator): 

178 try: 

179 return from_edgelist(data, create_using=create_using) 

180 except Exception as err: 

181 raise nx.NetworkXError("Input is not a valid edge list") from err 

182 

183 raise nx.NetworkXError("Input is not a known data type for conversion.") 

184 

185 

186@nx._dispatchable 

187def to_dict_of_lists(G, nodelist=None): 

188 """Returns adjacency representation of graph as a dictionary of lists. 

189 

190 Parameters 

191 ---------- 

192 G : graph 

193 A NetworkX graph 

194 

195 nodelist : list 

196 Use only nodes specified in nodelist 

197 

198 Notes 

199 ----- 

200 Completely ignores edge data for MultiGraph and MultiDiGraph. 

201 

202 """ 

203 if nodelist is None: 

204 nodelist = G 

205 

206 d = {} 

207 for n in nodelist: 

208 d[n] = [nbr for nbr in G.neighbors(n) if nbr in nodelist] 

209 return d 

210 

211 

212@nx._dispatchable(graphs=None, returns_graph=True) 

213def from_dict_of_lists(d, create_using=None): 

214 """Returns a graph from a dictionary of lists. 

215 

216 Parameters 

217 ---------- 

218 d : dictionary of lists 

219 A dictionary of lists adjacency representation. 

220 

221 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

222 Graph type to create. If graph instance, then cleared before populated. 

223 

224 Examples 

225 -------- 

226 >>> dol = {0: [1]} # single edge (0,1) 

227 >>> G = nx.from_dict_of_lists(dol) 

228 

229 or 

230 

231 >>> G = nx.Graph(dol) # use Graph constructor 

232 

233 """ 

234 G = nx.empty_graph(0, create_using) 

235 G.add_nodes_from(d) 

236 if G.is_multigraph() and not G.is_directed(): 

237 # a dict_of_lists can't show multiedges. BUT for undirected graphs, 

238 # each edge shows up twice in the dict_of_lists. 

239 # So we need to treat this case separately. 

240 seen = {} 

241 for node, nbrlist in d.items(): 

242 for nbr in nbrlist: 

243 if nbr not in seen: 

244 G.add_edge(node, nbr) 

245 seen[node] = 1 # don't allow reverse edge to show up 

246 else: 

247 G.add_edges_from( 

248 ((node, nbr) for node, nbrlist in d.items() for nbr in nbrlist) 

249 ) 

250 return G 

251 

252 

253def to_dict_of_dicts(G, nodelist=None, edge_data=None): 

254 """Returns adjacency representation of graph as a dictionary of dictionaries. 

255 

256 Parameters 

257 ---------- 

258 G : graph 

259 A NetworkX graph 

260 

261 nodelist : list 

262 Use only nodes specified in nodelist. If None, all nodes in G. 

263 

264 edge_data : scalar, optional (default: the G edgedatadict for each edge) 

265 If provided, the value of the dictionary will be set to `edge_data` for 

266 all edges. Usual values could be `1` or `True`. If `edge_data` is 

267 `None` (the default), the edgedata in `G` is used, resulting in a 

268 dict-of-dict-of-dicts. If `G` is a MultiGraph, the result will be a 

269 dict-of-dict-of-dict-of-dicts. See Notes for an approach to customize 

270 handling edge data. `edge_data` should *not* be a container as it will 

271 be the same container for all the edges. 

272 

273 Returns 

274 ------- 

275 dod : dict 

276 A nested dictionary representation of `G`. Note that the level of 

277 nesting depends on the type of `G` and the value of `edge_data` 

278 (see Examples). 

279 

280 See Also 

281 -------- 

282 from_dict_of_dicts, to_dict_of_lists 

283 

284 Notes 

285 ----- 

286 For a more custom approach to handling edge data, try:: 

287 

288 dod = { 

289 n: {nbr: custom(n, nbr, dd) for nbr, dd in nbrdict.items()} 

290 for n, nbrdict in G.adj.items() 

291 } 

292 

293 where `custom` returns the desired edge data for each edge between `n` and 

294 `nbr`, given existing edge data `dd`. 

295 

296 Examples 

297 -------- 

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

299 >>> nx.to_dict_of_dicts(G) 

300 {0: {1: {}}, 1: {0: {}, 2: {}}, 2: {1: {}}} 

301 

302 Edge data is preserved by default (``edge_data=None``), resulting 

303 in dict-of-dict-of-dicts where the innermost dictionary contains the 

304 edge data: 

305 

306 >>> G = nx.Graph() 

307 >>> G.add_edges_from( 

308 ... [ 

309 ... (0, 1, {"weight": 1.0}), 

310 ... (1, 2, {"weight": 2.0}), 

311 ... (2, 0, {"weight": 1.0}), 

312 ... ] 

313 ... ) 

314 >>> d = nx.to_dict_of_dicts(G) 

315 >>> d # doctest: +SKIP 

316 {0: {1: {'weight': 1.0}, 2: {'weight': 1.0}}, 

317 1: {0: {'weight': 1.0}, 2: {'weight': 2.0}}, 

318 2: {1: {'weight': 2.0}, 0: {'weight': 1.0}}} 

319 >>> d[1][2]["weight"] 

320 2.0 

321 

322 If `edge_data` is not `None`, edge data in the original graph (if any) is 

323 replaced: 

324 

325 >>> d = nx.to_dict_of_dicts(G, edge_data=1) 

326 >>> d 

327 {0: {1: 1, 2: 1}, 1: {0: 1, 2: 1}, 2: {1: 1, 0: 1}} 

328 >>> d[1][2] 

329 1 

330 

331 This also applies to MultiGraphs: edge data is preserved by default: 

332 

333 >>> G = nx.MultiGraph() 

334 >>> G.add_edge(0, 1, key="a", weight=1.0) 

335 'a' 

336 >>> G.add_edge(0, 1, key="b", weight=5.0) 

337 'b' 

338 >>> d = nx.to_dict_of_dicts(G) 

339 >>> d # doctest: +SKIP 

340 {0: {1: {'a': {'weight': 1.0}, 'b': {'weight': 5.0}}}, 

341 1: {0: {'a': {'weight': 1.0}, 'b': {'weight': 5.0}}}} 

342 >>> d[0][1]["b"]["weight"] 

343 5.0 

344 

345 But multi edge data is lost if `edge_data` is not `None`: 

346 

347 >>> d = nx.to_dict_of_dicts(G, edge_data=10) 

348 >>> d 

349 {0: {1: 10}, 1: {0: 10}} 

350 """ 

351 dod = {} 

352 if nodelist is None: 

353 if edge_data is None: 

354 for u, nbrdict in G.adjacency(): 

355 dod[u] = nbrdict.copy() 

356 else: # edge_data is not None 

357 for u, nbrdict in G.adjacency(): 

358 dod[u] = dod.fromkeys(nbrdict, edge_data) 

359 else: # nodelist is not None 

360 if edge_data is None: 

361 for u in nodelist: 

362 dod[u] = {} 

363 for v, data in ((v, data) for v, data in G[u].items() if v in nodelist): 

364 dod[u][v] = data 

365 else: # nodelist and edge_data are not None 

366 for u in nodelist: 

367 dod[u] = {} 

368 for v in (v for v in G[u] if v in nodelist): 

369 dod[u][v] = edge_data 

370 return dod 

371 

372 

373@nx._dispatchable(graphs=None, returns_graph=True) 

374def from_dict_of_dicts(d, create_using=None, multigraph_input=False): 

375 """Returns a graph from a dictionary of dictionaries. 

376 

377 Parameters 

378 ---------- 

379 d : dictionary of dictionaries 

380 A dictionary of dictionaries adjacency representation. 

381 

382 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

383 Graph type to create. If graph instance, then cleared before populated. 

384 

385 multigraph_input : bool (default False) 

386 When True, the dict `d` is assumed 

387 to be a dict-of-dict-of-dict-of-dict structure keyed by 

388 node to neighbor to edge keys to edge data for multi-edges. 

389 Otherwise this routine assumes dict-of-dict-of-dict keyed by 

390 node to neighbor to edge data. 

391 

392 Examples 

393 -------- 

394 >>> dod = {0: {1: {"weight": 1}}} # single edge (0,1) 

395 >>> G = nx.from_dict_of_dicts(dod) 

396 

397 or 

398 

399 >>> G = nx.Graph(dod) # use Graph constructor 

400 

401 """ 

402 G = nx.empty_graph(0, create_using) 

403 G.add_nodes_from(d) 

404 # does dict d represent a MultiGraph or MultiDiGraph? 

405 if multigraph_input: 

406 if G.is_directed(): 

407 if G.is_multigraph(): 

408 G.add_edges_from( 

409 (u, v, key, data) 

410 for u, nbrs in d.items() 

411 for v, datadict in nbrs.items() 

412 for key, data in datadict.items() 

413 ) 

414 else: 

415 G.add_edges_from( 

416 (u, v, data) 

417 for u, nbrs in d.items() 

418 for v, datadict in nbrs.items() 

419 for key, data in datadict.items() 

420 ) 

421 else: # Undirected 

422 if G.is_multigraph(): 

423 seen = set() # don't add both directions of undirected graph 

424 for u, nbrs in d.items(): 

425 for v, datadict in nbrs.items(): 

426 if (u, v) not in seen: 

427 G.add_edges_from( 

428 (u, v, key, data) for key, data in datadict.items() 

429 ) 

430 seen.add((v, u)) 

431 else: 

432 seen = set() # don't add both directions of undirected graph 

433 for u, nbrs in d.items(): 

434 for v, datadict in nbrs.items(): 

435 if (u, v) not in seen: 

436 G.add_edges_from( 

437 (u, v, data) for key, data in datadict.items() 

438 ) 

439 seen.add((v, u)) 

440 

441 else: # not a multigraph to multigraph transfer 

442 if G.is_multigraph() and not G.is_directed(): 

443 # d can have both representations u-v, v-u in dict. Only add one. 

444 # We don't need this check for digraphs since we add both directions, 

445 # or for Graph() since it is done implicitly (parallel edges not allowed) 

446 seen = set() 

447 for u, nbrs in d.items(): 

448 for v, data in nbrs.items(): 

449 if (u, v) not in seen: 

450 G.add_edge(u, v, key=0) 

451 G[u][v][0].update(data) 

452 seen.add((v, u)) 

453 else: 

454 G.add_edges_from( 

455 ((u, v, data) for u, nbrs in d.items() for v, data in nbrs.items()) 

456 ) 

457 return G 

458 

459 

460@nx._dispatchable(preserve_edge_attrs=True) 

461def to_edgelist(G, nodelist=None): 

462 """Returns a list of edges in the graph. 

463 

464 Parameters 

465 ---------- 

466 G : graph 

467 A NetworkX graph 

468 

469 nodelist : list 

470 Use only nodes specified in nodelist 

471 

472 """ 

473 if nodelist is None: 

474 return G.edges(data=True) 

475 return G.edges(nodelist, data=True) 

476 

477 

478@nx._dispatchable(graphs=None, returns_graph=True) 

479def from_edgelist(edgelist, create_using=None): 

480 """Returns a graph from a list of edges. 

481 

482 Parameters 

483 ---------- 

484 edgelist : list or iterator 

485 Edge tuples 

486 

487 create_using : NetworkX graph constructor, optional (default=nx.Graph) 

488 Graph type to create. If graph instance, then cleared before populated. 

489 

490 Examples 

491 -------- 

492 >>> edgelist = [(0, 1)] # single edge (0,1) 

493 >>> G = nx.from_edgelist(edgelist) 

494 

495 or 

496 

497 >>> G = nx.Graph(edgelist) # use Graph constructor 

498 

499 """ 

500 G = nx.empty_graph(0, create_using) 

501 G.add_edges_from(edgelist) 

502 return G