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
« 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.
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.
7Examples
8--------
9Create a graph with a single edge from a dictionary of dictionaries
11>>> d = {0: {1: 1}} # dict-of-dicts single edge (0,1)
12>>> G = nx.Graph(d)
14See Also
15--------
16nx_agraph, nx_pydot
17"""
18import warnings
19from collections.abc import Collection, Generator, Iterator
21import networkx as nx
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]
34def to_networkx_graph(data, create_using=None, multigraph_input=False):
35 """Make a NetworkX graph from a known data structure.
37 The preferred way to call this is automatically
38 from the class constructor
40 >>> d = {0: {1: {"weight": 1}}} # dict-of-dicts single edge (0,1)
41 >>> G = nx.Graph(d)
43 instead of the equivalent
45 >>> G = nx.from_dict_of_dicts(d)
47 Parameters
48 ----------
49 data : object to be converted
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
63 create_using : NetworkX graph constructor, optional (default=nx.Graph)
64 Graph type to create. If graph instance, then cleared before populated.
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.
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
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
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
116 # Pandas DataFrame
117 try:
118 import pandas as pd
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)
138 # numpy array
139 try:
140 import numpy as np
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)
152 # scipy sparse array - any format
153 try:
154 import scipy
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)
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
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
176 raise nx.NetworkXError("Input is not a known data type for conversion.")
179@nx._dispatch
180def to_dict_of_lists(G, nodelist=None):
181 """Returns adjacency representation of graph as a dictionary of lists.
183 Parameters
184 ----------
185 G : graph
186 A NetworkX graph
188 nodelist : list
189 Use only nodes specified in nodelist
191 Notes
192 -----
193 Completely ignores edge data for MultiGraph and MultiDiGraph.
195 """
196 if nodelist is None:
197 nodelist = G
199 d = {}
200 for n in nodelist:
201 d[n] = [nbr for nbr in G.neighbors(n) if nbr in nodelist]
202 return d
205@nx._dispatch(graphs=None)
206def from_dict_of_lists(d, create_using=None):
207 """Returns a graph from a dictionary of lists.
209 Parameters
210 ----------
211 d : dictionary of lists
212 A dictionary of lists adjacency representation.
214 create_using : NetworkX graph constructor, optional (default=nx.Graph)
215 Graph type to create. If graph instance, then cleared before populated.
217 Examples
218 --------
219 >>> dol = {0: [1]} # single edge (0,1)
220 >>> G = nx.from_dict_of_lists(dol)
222 or
224 >>> G = nx.Graph(dol) # use Graph constructor
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
246def to_dict_of_dicts(G, nodelist=None, edge_data=None):
247 """Returns adjacency representation of graph as a dictionary of dictionaries.
249 Parameters
250 ----------
251 G : graph
252 A NetworkX graph
254 nodelist : list
255 Use only nodes specified in nodelist
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.
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).
272 See Also
273 --------
274 from_dict_of_dicts, to_dict_of_lists
276 Notes
277 -----
278 For a more custom approach to handling edge data, try::
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 }
287 where `custom` returns the desired edge data for each edge between `n` and
288 `nbr`, given existing edge data `dd`.
290 Examples
291 --------
292 >>> G = nx.path_graph(3)
293 >>> nx.to_dict_of_dicts(G)
294 {0: {1: {}}, 1: {0: {}, 2: {}}, 2: {1: {}}}
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:
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
316 If `edge_data` is not `None`, edge data in the original graph (if any) is
317 replaced:
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
325 This also applies to MultiGraphs: edge data is preserved by default:
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
339 But multi edge data is lost if `edge_data` is not `None`:
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
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.
371 Parameters
372 ----------
373 d : dictionary of dictionaries
374 A dictionary of dictionaries adjacency representation.
376 create_using : NetworkX graph constructor, optional (default=nx.Graph)
377 Graph type to create. If graph instance, then cleared before populated.
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.
386 Examples
387 --------
388 >>> dod = {0: {1: {"weight": 1}}} # single edge (0,1)
389 >>> G = nx.from_dict_of_dicts(dod)
391 or
393 >>> G = nx.Graph(dod) # use Graph constructor
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))
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
454@nx._dispatch(preserve_edge_attrs=True)
455def to_edgelist(G, nodelist=None):
456 """Returns a list of edges in the graph.
458 Parameters
459 ----------
460 G : graph
461 A NetworkX graph
463 nodelist : list
464 Use only nodes specified in nodelist
466 """
467 if nodelist is None:
468 return G.edges(data=True)
469 return G.edges(nodelist, data=True)
472@nx._dispatch(graphs=None)
473def from_edgelist(edgelist, create_using=None):
474 """Returns a graph from a list of edges.
476 Parameters
477 ----------
478 edgelist : list or iterator
479 Edge tuples
481 create_using : NetworkX graph constructor, optional (default=nx.Graph)
482 Graph type to create. If graph instance, then cleared before populated.
484 Examples
485 --------
486 >>> edgelist = [(0, 1)] # single edge (0,1)
487 >>> G = nx.from_edgelist(edgelist)
489 or
491 >>> G = nx.Graph(edgelist) # use Graph constructor
493 """
494 G = nx.empty_graph(0, create_using)
495 G.add_edges_from(edgelist)
496 return G