Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/convert.py: 25%

150 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-20 07:00 +0000

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""" 

18import warnings 

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 # pygraphviz agraph 

94 if hasattr(data, "is_strict"): 

95 try: 

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

97 except Exception as err: 

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

99 

100 # dict of dicts/lists 

101 if isinstance(data, dict): 

102 try: 

103 return from_dict_of_dicts( 

104 data, create_using=create_using, multigraph_input=multigraph_input 

105 ) 

106 except Exception as err1: 

107 if multigraph_input is True: 

108 raise nx.NetworkXError( 

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

110 ) 

111 try: 

112 return from_dict_of_lists(data, create_using=create_using) 

113 except Exception as err2: 

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

115 

116 # Pandas DataFrame 

117 try: 

118 import pandas as pd 

119 

120 if isinstance(data, pd.DataFrame): 

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

122 try: 

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

124 except Exception as err: 

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

126 raise nx.NetworkXError(msg) from err 

127 else: 

128 try: 

129 return nx.from_pandas_edgelist( 

130 data, edge_attr=True, create_using=create_using 

131 ) 

132 except Exception as err: 

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

134 raise nx.NetworkXError(msg) from err 

135 except ImportError: 

136 warnings.warn("pandas not found, skipping conversion test.", ImportWarning) 

137 

138 # numpy array 

139 try: 

140 import numpy as np 

141 

142 if isinstance(data, np.ndarray): 

143 try: 

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

145 except Exception as err: 

146 raise nx.NetworkXError( 

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

148 ) from err 

149 except ImportError: 

150 warnings.warn("numpy not found, skipping conversion test.", ImportWarning) 

151 

152 # scipy sparse array - any format 

153 try: 

154 import scipy 

155 

156 if hasattr(data, "format"): 

157 try: 

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

159 except Exception as err: 

160 raise nx.NetworkXError( 

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

162 ) from err 

163 except ImportError: 

164 warnings.warn("scipy not found, skipping conversion test.", ImportWarning) 

165 

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

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

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

169 

170 if isinstance(data, (Collection, Generator, Iterator)): 

171 try: 

172 return from_edgelist(data, create_using=create_using) 

173 except Exception as err: 

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

175 

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

177 

178 

179@nx._dispatch 

180def to_dict_of_lists(G, nodelist=None): 

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

182 

183 Parameters 

184 ---------- 

185 G : graph 

186 A NetworkX graph 

187 

188 nodelist : list 

189 Use only nodes specified in nodelist 

190 

191 Notes 

192 ----- 

193 Completely ignores edge data for MultiGraph and MultiDiGraph. 

194 

195 """ 

196 if nodelist is None: 

197 nodelist = G 

198 

199 d = {} 

200 for n in nodelist: 

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

202 return d 

203 

204 

205@nx._dispatch(graphs=None) 

206def from_dict_of_lists(d, create_using=None): 

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

208 

209 Parameters 

210 ---------- 

211 d : dictionary of lists 

212 A dictionary of lists adjacency representation. 

213 

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

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

216 

217 Examples 

218 -------- 

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

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

221 

222 or 

223 

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

225 

226 """ 

227 G = nx.empty_graph(0, create_using) 

228 G.add_nodes_from(d) 

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

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

231 # each edge shows up twice in the dict_of_lists. 

232 # So we need to treat this case separately. 

233 seen = {} 

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

235 for nbr in nbrlist: 

236 if nbr not in seen: 

237 G.add_edge(node, nbr) 

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

239 else: 

240 G.add_edges_from( 

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

242 ) 

243 return G 

244 

245 

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

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

248 

249 Parameters 

250 ---------- 

251 G : graph 

252 A NetworkX graph 

253 

254 nodelist : list 

255 Use only nodes specified in nodelist 

256 

257 edge_data : scalar, optional 

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

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

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

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

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

263 handling edge data. `edge_data` should *not* be a container. 

264 

265 Returns 

266 ------- 

267 dod : dict 

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

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

270 (see Examples). 

271 

272 See Also 

273 -------- 

274 from_dict_of_dicts, to_dict_of_lists 

275 

276 Notes 

277 ----- 

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

279 

280 dod = { 

281 n: { 

282 nbr: custom(n, nbr, dd) for nbr, dd in nbrdict.items() 

283 } 

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

285 } 

286 

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

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

289 

290 Examples 

291 -------- 

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

293 >>> nx.to_dict_of_dicts(G) 

294 {0: {1: {}}, 1: {0: {}, 2: {}}, 2: {1: {}}} 

295 

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

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

298 edge data: 

299 

300 >>> G = nx.Graph() 

301 >>> G.add_edges_from( 

302 ... [ 

303 ... (0, 1, {'weight': 1.0}), 

304 ... (1, 2, {'weight': 2.0}), 

305 ... (2, 0, {'weight': 1.0}), 

306 ... ] 

307 ... ) 

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

309 >>> d # doctest: +SKIP 

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

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

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

313 >>> d[1][2]['weight'] 

314 2.0 

315 

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

317 replaced: 

318 

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

320 >>> d 

321 {0: {1: 1, 2: 1}, 1: {0: 1, 2: 1}, 2: {1: 1, 0: 1}} 

322 >>> d[1][2] 

323 1 

324 

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

326 

327 >>> G = nx.MultiGraph() 

328 >>> G.add_edge(0, 1, key='a', weight=1.0) 

329 'a' 

330 >>> G.add_edge(0, 1, key='b', weight=5.0) 

331 'b' 

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

333 >>> d # doctest: +SKIP 

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

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

336 >>> d[0][1]['b']['weight'] 

337 5.0 

338 

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

340 

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

342 >>> d 

343 {0: {1: 10}, 1: {0: 10}} 

344 """ 

345 dod = {} 

346 if nodelist is None: 

347 if edge_data is None: 

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

349 dod[u] = nbrdict.copy() 

350 else: # edge_data is not None 

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

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

353 else: # nodelist is not None 

354 if edge_data is None: 

355 for u in nodelist: 

356 dod[u] = {} 

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

358 dod[u][v] = data 

359 else: # nodelist and edge_data are not None 

360 for u in nodelist: 

361 dod[u] = {} 

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

363 dod[u][v] = edge_data 

364 return dod 

365 

366 

367@nx._dispatch(graphs=None) 

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

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

370 

371 Parameters 

372 ---------- 

373 d : dictionary of dictionaries 

374 A dictionary of dictionaries adjacency representation. 

375 

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

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

378 

379 multigraph_input : bool (default False) 

380 When True, the dict `d` is assumed 

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

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

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

384 node to neighbor to edge data. 

385 

386 Examples 

387 -------- 

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

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

390 

391 or 

392 

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

394 

395 """ 

396 G = nx.empty_graph(0, create_using) 

397 G.add_nodes_from(d) 

398 # does dict d represent a MultiGraph or MultiDiGraph? 

399 if multigraph_input: 

400 if G.is_directed(): 

401 if G.is_multigraph(): 

402 G.add_edges_from( 

403 (u, v, key, data) 

404 for u, nbrs in d.items() 

405 for v, datadict in nbrs.items() 

406 for key, data in datadict.items() 

407 ) 

408 else: 

409 G.add_edges_from( 

410 (u, v, data) 

411 for u, nbrs in d.items() 

412 for v, datadict in nbrs.items() 

413 for key, data in datadict.items() 

414 ) 

415 else: # Undirected 

416 if G.is_multigraph(): 

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

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

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

420 if (u, v) not in seen: 

421 G.add_edges_from( 

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

423 ) 

424 seen.add((v, u)) 

425 else: 

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

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

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

429 if (u, v) not in seen: 

430 G.add_edges_from( 

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

432 ) 

433 seen.add((v, u)) 

434 

435 else: # not a multigraph to multigraph transfer 

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

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

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

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

440 seen = set() 

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

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

443 if (u, v) not in seen: 

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

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

446 seen.add((v, u)) 

447 else: 

448 G.add_edges_from( 

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

450 ) 

451 return G 

452 

453 

454@nx._dispatch(preserve_edge_attrs=True) 

455def to_edgelist(G, nodelist=None): 

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

457 

458 Parameters 

459 ---------- 

460 G : graph 

461 A NetworkX graph 

462 

463 nodelist : list 

464 Use only nodes specified in nodelist 

465 

466 """ 

467 if nodelist is None: 

468 return G.edges(data=True) 

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

470 

471 

472@nx._dispatch(graphs=None) 

473def from_edgelist(edgelist, create_using=None): 

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

475 

476 Parameters 

477 ---------- 

478 edgelist : list or iterator 

479 Edge tuples 

480 

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

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

483 

484 Examples 

485 -------- 

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

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

488 

489 or 

490 

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

492 

493 """ 

494 G = nx.empty_graph(0, create_using) 

495 G.add_edges_from(edgelist) 

496 return G