author shellac Mon, 22 Mar 2021 18:12:50 +0000
comparison
equal inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """
2 **********
3 Edge Lists
4 **********
5 Read and write NetworkX graphs as edge lists.
6
7 The multi-line adjacency list format is useful for graphs with nodes
8 that can be meaningfully represented as strings. With the edgelist
9 format simple edge data can be stored but node or graph data is not.
10 There is no way of representing isolated nodes unless the node has a
11 self-loop edge.
12
13 Format
14 ------
15 You can read or write three formats of edge lists with these functions.
16
17 Node pairs with no data::
18
19 1 2
20
21 Python dictionary as data::
22
23 1 2 {'weight':7, 'color':'green'}
24
25 Arbitrary data::
26
27 1 2 7 green
28 """
29
30 __all__ = [
31 "generate_edgelist",
32 "write_edgelist",
33 "parse_edgelist",
36 "write_weighted_edgelist",
37 ]
38
39 from networkx.utils import open_file
40 import networkx as nx
41
42
43 def generate_edgelist(G, delimiter=" ", data=True):
44 """Generate a single line of the graph G in edge list format.
45
46 Parameters
47 ----------
48 G : NetworkX graph
49
50 delimiter : string, optional
51 Separator for node labels
52
53 data : bool or list of keys
54 If False generate no edge data. If True use a dictionary
55 representation of edge data. If a list of keys use a list of data
56 values corresponding to the keys.
57
58 Returns
59 -------
60 lines : string
61 Lines of data in adjlist format.
62
63 Examples
64 --------
65 >>> G = nx.lollipop_graph(4, 3)
66 >>> G[1][2]["weight"] = 3
67 >>> G[3][4]["capacity"] = 12
68 >>> for line in nx.generate_edgelist(G, data=False):
69 ... print(line)
70 0 1
71 0 2
72 0 3
73 1 2
74 1 3
75 2 3
76 3 4
77 4 5
78 5 6
79
80 >>> for line in nx.generate_edgelist(G):
81 ... print(line)
82 0 1 {}
83 0 2 {}
84 0 3 {}
85 1 2 {'weight': 3}
86 1 3 {}
87 2 3 {}
88 3 4 {'capacity': 12}
89 4 5 {}
90 5 6 {}
91
92 >>> for line in nx.generate_edgelist(G, data=["weight"]):
93 ... print(line)
94 0 1
95 0 2
96 0 3
97 1 2 3
98 1 3
99 2 3
100 3 4
101 4 5
102 5 6
103
105 --------
107 """
108 if data is True:
109 for u, v, d in G.edges(data=True):
110 e = u, v, dict(d)
111 yield delimiter.join(map(str, e))
112 elif data is False:
113 for u, v in G.edges(data=False):
114 e = u, v
115 yield delimiter.join(map(str, e))
116 else:
117 for u, v, d in G.edges(data=True):
118 e = [u, v]
119 try:
120 e.extend(d[k] for k in data)
121 except KeyError:
122 pass # missing data for this edge, should warn?
123 yield delimiter.join(map(str, e))
124
125
126 @open_file(1, mode="wb")
127 def write_edgelist(G, path, comments="#", delimiter=" ", data=True, encoding="utf-8"):
128 """Write graph as a list of edges.
129
130 Parameters
131 ----------
132 G : graph
133 A NetworkX graph
134 path : file or string
135 File or filename to write. If a file is provided, it must be
136 opened in 'wb' mode. Filenames ending in .gz or .bz2 will be compressed.
138 The character used to indicate the start of a comment
139 delimiter : string, optional
140 The string used to separate values. The default is whitespace.
141 data : bool or list, optional
142 If False write no edge data.
143 If True write a string representation of the edge data dictionary..
144 If a list (or other iterable) is provided, write the keys specified
145 in the list.
146 encoding: string, optional
147 Specify which encoding to use when writing file.
148
149 Examples
150 --------
151 >>> G = nx.path_graph(4)
152 >>> nx.write_edgelist(G, "test.edgelist")
153 >>> G = nx.path_graph(4)
154 >>> fh = open("test.edgelist", "wb")
155 >>> nx.write_edgelist(G, fh)
156 >>> nx.write_edgelist(G, "test.edgelist.gz")
157 >>> nx.write_edgelist(G, "test.edgelist.gz", data=False)
158
159 >>> G = nx.Graph()
160 >>> G.add_edge(1, 2, weight=7, color="red")
161 >>> nx.write_edgelist(G, "test.edgelist", data=False)
162 >>> nx.write_edgelist(G, "test.edgelist", data=["color"])
163 >>> nx.write_edgelist(G, "test.edgelist", data=["color", "weight"])
164
166 --------
168 write_weighted_edgelist
169 """
170
171 for line in generate_edgelist(G, delimiter, data):
172 line += "\n"
173 path.write(line.encode(encoding))
174
175
176 def parse_edgelist(
177 lines, comments="#", delimiter=None, create_using=None, nodetype=None, data=True
178 ):
179 """Parse lines of an edge list representation of a graph.
180
181 Parameters
182 ----------
183 lines : list or iterator of strings
184 Input data in edgelist format
186 Marker for comment lines. Default is `'#'`
187 delimiter : string, optional
188 Separator for node labels. Default is `None`, meaning any whitespace.
189 create_using : NetworkX graph constructor, optional (default=nx.Graph)
190 Graph type to create. If graph instance, then cleared before populated.
191 nodetype : Python type, optional
192 Convert nodes to this type. Default is `None`, meaning no conversion is
193 performed.
194 data : bool or list of (label,type) tuples
195 If `False` generate no edge data or if `True` use a dictionary
196 representation of edge data or a list tuples specifying dictionary
197 key names and types for edge data.
198
199 Returns
200 -------
201 G: NetworkX Graph
202 The graph corresponding to lines
203
204 Examples
205 --------
206 Edgelist with no data:
207
208 >>> lines = ["1 2", "2 3", "3 4"]
209 >>> G = nx.parse_edgelist(lines, nodetype=int)
210 >>> list(G)
211 [1, 2, 3, 4]
212 >>> list(G.edges())
213 [(1, 2), (2, 3), (3, 4)]
214
215 Edgelist with data in Python dictionary representation:
216
217 >>> lines = ["1 2 {'weight': 3}", "2 3 {'weight': 27}", "3 4 {'weight': 3.0}"]
218 >>> G = nx.parse_edgelist(lines, nodetype=int)
219 >>> list(G)
220 [1, 2, 3, 4]
221 >>> list(G.edges(data=True))
222 [(1, 2, {'weight': 3}), (2, 3, {'weight': 27}), (3, 4, {'weight': 3.0})]
223
224 Edgelist with data in a list:
225
226 >>> lines = ["1 2 3", "2 3 27", "3 4 3.0"]
227 >>> G = nx.parse_edgelist(lines, nodetype=int, data=(("weight", float),))
228 >>> list(G)
229 [1, 2, 3, 4]
230 >>> list(G.edges(data=True))
231 [(1, 2, {'weight': 3.0}), (2, 3, {'weight': 27.0}), (3, 4, {'weight': 3.0})]
232
234 --------
236 """
237 from ast import literal_eval
238
239 G = nx.empty_graph(0, create_using)
240 for line in lines:
242 if p >= 0:
243 line = line[:p]
244 if not line:
245 continue
246 # split line, should have 2 or more
247 s = line.strip().split(delimiter)
248 if len(s) < 2:
249 continue
250 u = s.pop(0)
251 v = s.pop(0)
252 d = s
253 if nodetype is not None:
254 try:
255 u = nodetype(u)
256 v = nodetype(v)
257 except Exception as e:
258 raise TypeError(
259 f"Failed to convert nodes {u},{v} to type {nodetype}."
260 ) from e
261
262 if len(d) == 0 or data is False:
263 # no data or data type specified
264 edgedata = {}
265 elif data is True:
266 # no edge types specified
267 try: # try to evaluate as dictionary
268 if delimiter == ",":
269 edgedata_str = ",".join(d)
270 else:
271 edgedata_str = " ".join(d)
272 edgedata = dict(literal_eval(edgedata_str.strip()))
273 except Exception as e:
274 raise TypeError(
275 f"Failed to convert edge data ({d}) to dictionary."
276 ) from e
277 else:
278 # convert edge data to dictionary with specified keys and type
279 if len(d) != len(data):
280 raise IndexError(
281 f"Edge data {d} and data_keys {data} are not the same length"
282 )
283 edgedata = {}
284 for (edge_key, edge_type), edge_value in zip(data, d):
285 try:
286 edge_value = edge_type(edge_value)
287 except Exception as e:
288 raise TypeError(
289 f"Failed to convert {edge_key} data {edge_value} "
290 f"to type {edge_type}."
291 ) from e
292 edgedata.update({edge_key: edge_value})
294 return G
295
296
297 @open_file(0, mode="rb")
299 path,
301 delimiter=None,
302 create_using=None,
303 nodetype=None,
304 data=True,
305 edgetype=None,
306 encoding="utf-8",
307 ):
308 """Read a graph from a list of edges.
309
310 Parameters
311 ----------
312 path : file or string
313 File or filename to read. If a file is provided, it must be
314 opened in 'rb' mode.
315 Filenames ending in .gz or .bz2 will be uncompressed.
317 The character used to indicate the start of a comment.
318 delimiter : string, optional
319 The string used to separate values. The default is whitespace.
320 create_using : NetworkX graph constructor, optional (default=nx.Graph)
321 Graph type to create. If graph instance, then cleared before populated.
322 nodetype : int, float, str, Python type, optional
323 Convert node data from strings to specified type
324 data : bool or list of (label,type) tuples
325 Tuples specifying dictionary key names and types for edge data
326 edgetype : int, float, str, Python type, optional OBSOLETE
327 Convert edge data from strings to specified type and use as 'weight'
328 encoding: string, optional
329 Specify which encoding to use when reading file.
330
331 Returns
332 -------
333 G : graph
334 A networkx Graph or other type specified with create_using
335
336 Examples
337 --------
338 >>> nx.write_edgelist(nx.path_graph(4), "test.edgelist")
340
341 >>> fh = open("test.edgelist", "rb")
343 >>> fh.close()
344
345 >>> G = nx.read_edgelist("test.edgelist", nodetype=int)
346 >>> G = nx.read_edgelist("test.edgelist", create_using=nx.DiGraph)
347
348 Edgelist with data in a list:
349
350 >>> textline = "1 2 3"
351 >>> fh = open("test.edgelist", "w")
352 >>> d = fh.write(textline)
353 >>> fh.close()
354 >>> G = nx.read_edgelist("test.edgelist", nodetype=int, data=(("weight", float),))
355 >>> list(G)
356 [1, 2]
357 >>> list(G.edges(data=True))
358 [(1, 2, {'weight': 3.0})]
359
360 See parse_edgelist() for more examples of formatting.
361
363 --------
364 parse_edgelist
365 write_edgelist
366
367 Notes
368 -----
369 Since nodes must be hashable, the function nodetype must return hashable
370 types (e.g. int, float, str, frozenset - or tuples of those, etc.)
371 """
372 lines = (line if isinstance(line, str) else line.decode(encoding) for line in path)
373 return parse_edgelist(
374 lines,
376 delimiter=delimiter,
377 create_using=create_using,
378 nodetype=nodetype,
379 data=data,
380 )
381
382
383 def write_weighted_edgelist(G, path, comments="#", delimiter=" ", encoding="utf-8"):
384 """Write graph G as a list of edges with numeric weights.
385
386 Parameters
387 ----------
388 G : graph
389 A NetworkX graph
390 path : file or string
391 File or filename to write. If a file is provided, it must be
392 opened in 'wb' mode.
393 Filenames ending in .gz or .bz2 will be compressed.
395 The character used to indicate the start of a comment
396 delimiter : string, optional
397 The string used to separate values. The default is whitespace.
398 encoding: string, optional
399 Specify which encoding to use when writing file.
400
401 Examples
402 --------
403 >>> G = nx.Graph()
405 >>> nx.write_weighted_edgelist(G, "test.weighted.edgelist")
406
408 --------
410 write_edgelist
412 """
413 write_edgelist(
414 G,
415 path,
417 delimiter=delimiter,
418 data=("weight",),
419 encoding=encoding,
420 )
421
422
424 path,
426 delimiter=None,
427 create_using=None,
428 nodetype=None,
429 encoding="utf-8",
430 ):
431 """Read a graph as list of edges with numeric weights.
432
433 Parameters
434 ----------
435 path : file or string
436 File or filename to read. If a file is provided, it must be
437 opened in 'rb' mode.
438 Filenames ending in .gz or .bz2 will be uncompressed.
440 The character used to indicate the start of a comment.
441 delimiter : string, optional
442 The string used to separate values. The default is whitespace.
443 create_using : NetworkX graph constructor, optional (default=nx.Graph)
444 Graph type to create. If graph instance, then cleared before populated.
445 nodetype : int, float, str, Python type, optional
446 Convert node data from strings to specified type
447 encoding: string, optional
448 Specify which encoding to use when reading file.
449
450 Returns
451 -------
452 G : graph
453 A networkx Graph or other type specified with create_using
454
455 Notes
456 -----
457 Since nodes must be hashable, the function nodetype must return hashable
458 types (e.g. int, float, str, frozenset - or tuples of those, etc.)
459
460 Example edgelist file format.
461
462 With numeric edge data::
463
466 # source target data
467 a b 1
468 a c 3.14159
469 d e 42
470