123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- """
- **********
- 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",
- ]
- import networkx as nx
- from networkx.utils import open_file
- 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 `'#'`. To specify that no character
- should be treated as a comment, use ``comments=None``.
- 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:
- if comments is not None:
- 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 err:
- raise TypeError(
- f"Failed to convert nodes {u},{v} to type {nodetype}."
- ) from err
- 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 err:
- raise TypeError(
- f"Failed to convert edge data ({d}) to dictionary."
- ) from err
- 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 err:
- raise TypeError(
- f"Failed to convert {edge_key} data {edge_value} "
- f"to type {edge_type}."
- ) from err
- 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. To specify that
- no character should be treated as a comment, use ``comments=None``.
- 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,
- )
|