484 lines
14 KiB
Python
484 lines
14 KiB
Python
|
"""
|
||
|
**********
|
||
|
Edge Lists
|
||
|
**********
|
||
|
Read and write NetworkX graphs as edge lists.
|
||
|
|
||
|
The multi-line adjacency list format is useful for graphs with nodes
|
||
|
that can be meaningfully represented as strings. With the edgelist
|
||
|
format simple edge data can be stored but node or graph data is not.
|
||
|
There is no way of representing isolated nodes unless the node has a
|
||
|
self-loop edge.
|
||
|
|
||
|
Format
|
||
|
------
|
||
|
You can read or write three formats of edge lists with these functions.
|
||
|
|
||
|
Node pairs with no data::
|
||
|
|
||
|
1 2
|
||
|
|
||
|
Python dictionary as data::
|
||
|
|
||
|
1 2 {'weight':7, 'color':'green'}
|
||
|
|
||
|
Arbitrary data::
|
||
|
|
||
|
1 2 7 green
|
||
|
"""
|
||
|
|
||
|
__all__ = [
|
||
|
"generate_edgelist",
|
||
|
"write_edgelist",
|
||
|
"parse_edgelist",
|
||
|
"read_edgelist",
|
||
|
"read_weighted_edgelist",
|
||
|
"write_weighted_edgelist",
|
||
|
]
|
||
|
|
||
|
from networkx.utils import open_file
|
||
|
import networkx as nx
|
||
|
|
||
|
|
||
|
def generate_edgelist(G, delimiter=" ", data=True):
|
||
|
"""Generate a single line of the graph G in edge list format.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
G : NetworkX graph
|
||
|
|
||
|
delimiter : string, optional
|
||
|
Separator for node labels
|
||
|
|
||
|
data : bool or list of keys
|
||
|
If False generate no edge data. If True use a dictionary
|
||
|
representation of edge data. If a list of keys use a list of data
|
||
|
values corresponding to the keys.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
lines : string
|
||
|
Lines of data in adjlist format.
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
>>> G = nx.lollipop_graph(4, 3)
|
||
|
>>> G[1][2]["weight"] = 3
|
||
|
>>> G[3][4]["capacity"] = 12
|
||
|
>>> for line in nx.generate_edgelist(G, data=False):
|
||
|
... print(line)
|
||
|
0 1
|
||
|
0 2
|
||
|
0 3
|
||
|
1 2
|
||
|
1 3
|
||
|
2 3
|
||
|
3 4
|
||
|
4 5
|
||
|
5 6
|
||
|
|
||
|
>>> for line in nx.generate_edgelist(G):
|
||
|
... print(line)
|
||
|
0 1 {}
|
||
|
0 2 {}
|
||
|
0 3 {}
|
||
|
1 2 {'weight': 3}
|
||
|
1 3 {}
|
||
|
2 3 {}
|
||
|
3 4 {'capacity': 12}
|
||
|
4 5 {}
|
||
|
5 6 {}
|
||
|
|
||
|
>>> for line in nx.generate_edgelist(G, data=["weight"]):
|
||
|
... print(line)
|
||
|
0 1
|
||
|
0 2
|
||
|
0 3
|
||
|
1 2 3
|
||
|
1 3
|
||
|
2 3
|
||
|
3 4
|
||
|
4 5
|
||
|
5 6
|
||
|
|
||
|
See Also
|
||
|
--------
|
||
|
write_adjlist, read_adjlist
|
||
|
"""
|
||
|
if data is True:
|
||
|
for u, v, d in G.edges(data=True):
|
||
|
e = u, v, dict(d)
|
||
|
yield delimiter.join(map(str, e))
|
||
|
elif data is False:
|
||
|
for u, v in G.edges(data=False):
|
||
|
e = u, v
|
||
|
yield delimiter.join(map(str, e))
|
||
|
else:
|
||
|
for u, v, d in G.edges(data=True):
|
||
|
e = [u, v]
|
||
|
try:
|
||
|
e.extend(d[k] for k in data)
|
||
|
except KeyError:
|
||
|
pass # missing data for this edge, should warn?
|
||
|
yield delimiter.join(map(str, e))
|
||
|
|
||
|
|
||
|
@open_file(1, mode="wb")
|
||
|
def write_edgelist(G, path, comments="#", delimiter=" ", data=True, encoding="utf-8"):
|
||
|
"""Write graph as a list of edges.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
G : graph
|
||
|
A NetworkX graph
|
||
|
path : file or string
|
||
|
File or filename to write. If a file is provided, it must be
|
||
|
opened in 'wb' mode. Filenames ending in .gz or .bz2 will be compressed.
|
||
|
comments : string, optional
|
||
|
The character used to indicate the start of a comment
|
||
|
delimiter : string, optional
|
||
|
The string used to separate values. The default is whitespace.
|
||
|
data : bool or list, optional
|
||
|
If False write no edge data.
|
||
|
If True write a string representation of the edge data dictionary..
|
||
|
If a list (or other iterable) is provided, write the keys specified
|
||
|
in the list.
|
||
|
encoding: string, optional
|
||
|
Specify which encoding to use when writing file.
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
>>> G = nx.path_graph(4)
|
||
|
>>> nx.write_edgelist(G, "test.edgelist")
|
||
|
>>> G = nx.path_graph(4)
|
||
|
>>> fh = open("test.edgelist", "wb")
|
||
|
>>> nx.write_edgelist(G, fh)
|
||
|
>>> nx.write_edgelist(G, "test.edgelist.gz")
|
||
|
>>> nx.write_edgelist(G, "test.edgelist.gz", data=False)
|
||
|
|
||
|
>>> G = nx.Graph()
|
||
|
>>> G.add_edge(1, 2, weight=7, color="red")
|
||
|
>>> nx.write_edgelist(G, "test.edgelist", data=False)
|
||
|
>>> nx.write_edgelist(G, "test.edgelist", data=["color"])
|
||
|
>>> nx.write_edgelist(G, "test.edgelist", data=["color", "weight"])
|
||
|
|
||
|
See Also
|
||
|
--------
|
||
|
read_edgelist
|
||
|
write_weighted_edgelist
|
||
|
"""
|
||
|
|
||
|
for line in generate_edgelist(G, delimiter, data):
|
||
|
line += "\n"
|
||
|
path.write(line.encode(encoding))
|
||
|
|
||
|
|
||
|
def parse_edgelist(
|
||
|
lines, comments="#", delimiter=None, create_using=None, nodetype=None, data=True
|
||
|
):
|
||
|
"""Parse lines of an edge list representation of a graph.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
lines : list or iterator of strings
|
||
|
Input data in edgelist format
|
||
|
comments : string, optional
|
||
|
Marker for comment lines. Default is `'#'`
|
||
|
delimiter : string, optional
|
||
|
Separator for node labels. Default is `None`, meaning any whitespace.
|
||
|
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||
|
Graph type to create. If graph instance, then cleared before populated.
|
||
|
nodetype : Python type, optional
|
||
|
Convert nodes to this type. Default is `None`, meaning no conversion is
|
||
|
performed.
|
||
|
data : bool or list of (label,type) tuples
|
||
|
If `False` generate no edge data or if `True` use a dictionary
|
||
|
representation of edge data or a list tuples specifying dictionary
|
||
|
key names and types for edge data.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
G: NetworkX Graph
|
||
|
The graph corresponding to lines
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
Edgelist with no data:
|
||
|
|
||
|
>>> lines = ["1 2", "2 3", "3 4"]
|
||
|
>>> G = nx.parse_edgelist(lines, nodetype=int)
|
||
|
>>> list(G)
|
||
|
[1, 2, 3, 4]
|
||
|
>>> list(G.edges())
|
||
|
[(1, 2), (2, 3), (3, 4)]
|
||
|
|
||
|
Edgelist with data in Python dictionary representation:
|
||
|
|
||
|
>>> lines = ["1 2 {'weight': 3}", "2 3 {'weight': 27}", "3 4 {'weight': 3.0}"]
|
||
|
>>> G = nx.parse_edgelist(lines, nodetype=int)
|
||
|
>>> list(G)
|
||
|
[1, 2, 3, 4]
|
||
|
>>> list(G.edges(data=True))
|
||
|
[(1, 2, {'weight': 3}), (2, 3, {'weight': 27}), (3, 4, {'weight': 3.0})]
|
||
|
|
||
|
Edgelist with data in a list:
|
||
|
|
||
|
>>> lines = ["1 2 3", "2 3 27", "3 4 3.0"]
|
||
|
>>> G = nx.parse_edgelist(lines, nodetype=int, data=(("weight", float),))
|
||
|
>>> list(G)
|
||
|
[1, 2, 3, 4]
|
||
|
>>> list(G.edges(data=True))
|
||
|
[(1, 2, {'weight': 3.0}), (2, 3, {'weight': 27.0}), (3, 4, {'weight': 3.0})]
|
||
|
|
||
|
See Also
|
||
|
--------
|
||
|
read_weighted_edgelist
|
||
|
"""
|
||
|
from ast import literal_eval
|
||
|
|
||
|
G = nx.empty_graph(0, create_using)
|
||
|
for line in lines:
|
||
|
p = line.find(comments)
|
||
|
if p >= 0:
|
||
|
line = line[:p]
|
||
|
if not line:
|
||
|
continue
|
||
|
# split line, should have 2 or more
|
||
|
s = line.strip().split(delimiter)
|
||
|
if len(s) < 2:
|
||
|
continue
|
||
|
u = s.pop(0)
|
||
|
v = s.pop(0)
|
||
|
d = s
|
||
|
if nodetype is not None:
|
||
|
try:
|
||
|
u = nodetype(u)
|
||
|
v = nodetype(v)
|
||
|
except Exception as e:
|
||
|
raise TypeError(
|
||
|
f"Failed to convert nodes {u},{v} to type {nodetype}."
|
||
|
) from e
|
||
|
|
||
|
if len(d) == 0 or data is False:
|
||
|
# no data or data type specified
|
||
|
edgedata = {}
|
||
|
elif data is True:
|
||
|
# no edge types specified
|
||
|
try: # try to evaluate as dictionary
|
||
|
if delimiter == ",":
|
||
|
edgedata_str = ",".join(d)
|
||
|
else:
|
||
|
edgedata_str = " ".join(d)
|
||
|
edgedata = dict(literal_eval(edgedata_str.strip()))
|
||
|
except Exception as e:
|
||
|
raise TypeError(
|
||
|
f"Failed to convert edge data ({d}) to dictionary."
|
||
|
) from e
|
||
|
else:
|
||
|
# convert edge data to dictionary with specified keys and type
|
||
|
if len(d) != len(data):
|
||
|
raise IndexError(
|
||
|
f"Edge data {d} and data_keys {data} are not the same length"
|
||
|
)
|
||
|
edgedata = {}
|
||
|
for (edge_key, edge_type), edge_value in zip(data, d):
|
||
|
try:
|
||
|
edge_value = edge_type(edge_value)
|
||
|
except Exception as e:
|
||
|
raise TypeError(
|
||
|
f"Failed to convert {edge_key} data {edge_value} "
|
||
|
f"to type {edge_type}."
|
||
|
) from e
|
||
|
edgedata.update({edge_key: edge_value})
|
||
|
G.add_edge(u, v, **edgedata)
|
||
|
return G
|
||
|
|
||
|
|
||
|
@open_file(0, mode="rb")
|
||
|
def read_edgelist(
|
||
|
path,
|
||
|
comments="#",
|
||
|
delimiter=None,
|
||
|
create_using=None,
|
||
|
nodetype=None,
|
||
|
data=True,
|
||
|
edgetype=None,
|
||
|
encoding="utf-8",
|
||
|
):
|
||
|
"""Read a graph from a list of edges.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
path : file or string
|
||
|
File or filename to read. If a file is provided, it must be
|
||
|
opened in 'rb' mode.
|
||
|
Filenames ending in .gz or .bz2 will be uncompressed.
|
||
|
comments : string, optional
|
||
|
The character used to indicate the start of a comment.
|
||
|
delimiter : string, optional
|
||
|
The string used to separate values. The default is whitespace.
|
||
|
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||
|
Graph type to create. If graph instance, then cleared before populated.
|
||
|
nodetype : int, float, str, Python type, optional
|
||
|
Convert node data from strings to specified type
|
||
|
data : bool or list of (label,type) tuples
|
||
|
Tuples specifying dictionary key names and types for edge data
|
||
|
edgetype : int, float, str, Python type, optional OBSOLETE
|
||
|
Convert edge data from strings to specified type and use as 'weight'
|
||
|
encoding: string, optional
|
||
|
Specify which encoding to use when reading file.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
G : graph
|
||
|
A networkx Graph or other type specified with create_using
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
>>> nx.write_edgelist(nx.path_graph(4), "test.edgelist")
|
||
|
>>> G = nx.read_edgelist("test.edgelist")
|
||
|
|
||
|
>>> fh = open("test.edgelist", "rb")
|
||
|
>>> G = nx.read_edgelist(fh)
|
||
|
>>> fh.close()
|
||
|
|
||
|
>>> G = nx.read_edgelist("test.edgelist", nodetype=int)
|
||
|
>>> G = nx.read_edgelist("test.edgelist", create_using=nx.DiGraph)
|
||
|
|
||
|
Edgelist with data in a list:
|
||
|
|
||
|
>>> textline = "1 2 3"
|
||
|
>>> fh = open("test.edgelist", "w")
|
||
|
>>> d = fh.write(textline)
|
||
|
>>> fh.close()
|
||
|
>>> G = nx.read_edgelist("test.edgelist", nodetype=int, data=(("weight", float),))
|
||
|
>>> list(G)
|
||
|
[1, 2]
|
||
|
>>> list(G.edges(data=True))
|
||
|
[(1, 2, {'weight': 3.0})]
|
||
|
|
||
|
See parse_edgelist() for more examples of formatting.
|
||
|
|
||
|
See Also
|
||
|
--------
|
||
|
parse_edgelist
|
||
|
write_edgelist
|
||
|
|
||
|
Notes
|
||
|
-----
|
||
|
Since nodes must be hashable, the function nodetype must return hashable
|
||
|
types (e.g. int, float, str, frozenset - or tuples of those, etc.)
|
||
|
"""
|
||
|
lines = (line if isinstance(line, str) else line.decode(encoding) for line in path)
|
||
|
return parse_edgelist(
|
||
|
lines,
|
||
|
comments=comments,
|
||
|
delimiter=delimiter,
|
||
|
create_using=create_using,
|
||
|
nodetype=nodetype,
|
||
|
data=data,
|
||
|
)
|
||
|
|
||
|
|
||
|
def write_weighted_edgelist(G, path, comments="#", delimiter=" ", encoding="utf-8"):
|
||
|
"""Write graph G as a list of edges with numeric weights.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
G : graph
|
||
|
A NetworkX graph
|
||
|
path : file or string
|
||
|
File or filename to write. If a file is provided, it must be
|
||
|
opened in 'wb' mode.
|
||
|
Filenames ending in .gz or .bz2 will be compressed.
|
||
|
comments : string, optional
|
||
|
The character used to indicate the start of a comment
|
||
|
delimiter : string, optional
|
||
|
The string used to separate values. The default is whitespace.
|
||
|
encoding: string, optional
|
||
|
Specify which encoding to use when writing file.
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
>>> G = nx.Graph()
|
||
|
>>> G.add_edge(1, 2, weight=7)
|
||
|
>>> nx.write_weighted_edgelist(G, "test.weighted.edgelist")
|
||
|
|
||
|
See Also
|
||
|
--------
|
||
|
read_edgelist
|
||
|
write_edgelist
|
||
|
read_weighted_edgelist
|
||
|
"""
|
||
|
write_edgelist(
|
||
|
G,
|
||
|
path,
|
||
|
comments=comments,
|
||
|
delimiter=delimiter,
|
||
|
data=("weight",),
|
||
|
encoding=encoding,
|
||
|
)
|
||
|
|
||
|
|
||
|
def read_weighted_edgelist(
|
||
|
path,
|
||
|
comments="#",
|
||
|
delimiter=None,
|
||
|
create_using=None,
|
||
|
nodetype=None,
|
||
|
encoding="utf-8",
|
||
|
):
|
||
|
"""Read a graph as list of edges with numeric weights.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
path : file or string
|
||
|
File or filename to read. If a file is provided, it must be
|
||
|
opened in 'rb' mode.
|
||
|
Filenames ending in .gz or .bz2 will be uncompressed.
|
||
|
comments : string, optional
|
||
|
The character used to indicate the start of a comment.
|
||
|
delimiter : string, optional
|
||
|
The string used to separate values. The default is whitespace.
|
||
|
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||
|
Graph type to create. If graph instance, then cleared before populated.
|
||
|
nodetype : int, float, str, Python type, optional
|
||
|
Convert node data from strings to specified type
|
||
|
encoding: string, optional
|
||
|
Specify which encoding to use when reading file.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
G : graph
|
||
|
A networkx Graph or other type specified with create_using
|
||
|
|
||
|
Notes
|
||
|
-----
|
||
|
Since nodes must be hashable, the function nodetype must return hashable
|
||
|
types (e.g. int, float, str, frozenset - or tuples of those, etc.)
|
||
|
|
||
|
Example edgelist file format.
|
||
|
|
||
|
With numeric edge data::
|
||
|
|
||
|
# read with
|
||
|
# >>> G=nx.read_weighted_edgelist(fh)
|
||
|
# source target data
|
||
|
a b 1
|
||
|
a c 3.14159
|
||
|
d e 42
|
||
|
|
||
|
See Also
|
||
|
--------
|
||
|
write_weighted_edgelist
|
||
|
"""
|
||
|
return read_edgelist(
|
||
|
path,
|
||
|
comments=comments,
|
||
|
delimiter=delimiter,
|
||
|
create_using=create_using,
|
||
|
nodetype=nodetype,
|
||
|
data=(("weight", float),),
|
||
|
encoding=encoding,
|
||
|
)
|