| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- """
- **************
- Adjacency List
- **************
- Read and write NetworkX graphs as adjacency lists.
- Adjacency list format is useful for graphs without data associated
- with nodes or edges and for nodes that can be meaningfully represented
- as strings.
- Format
- ------
- The adjacency list format consists of lines with node labels. The
- first label in a line is the source node. Further labels in the line
- are considered target nodes and are added to the graph along with an edge
- between the source node and target node.
- The graph with edges a-b, a-c, d-e can be represented as the following
- adjacency list (anything following the # in a line is a comment)::
- a b c # source target target
- d e
- """
- __all__ = ["generate_adjlist", "write_adjlist", "parse_adjlist", "read_adjlist"]
- import networkx as nx
- from networkx.utils import open_file
- def generate_adjlist(G, delimiter=" "):
- """Generate lines representing a graph in adjacency list format.
- Parameters
- ----------
- G : NetworkX graph
- delimiter : str, default=" "
- Separator for node labels.
- Yields
- ------
- str
- Adjacency list for a node in `G`. The first item is the node label,
- followed by the labels of its neighbors.
- Examples
- --------
- >>> G = nx.lollipop_graph(4, 3)
- >>> for line in nx.generate_adjlist(G):
- ... print(line)
- 0 1 2 3
- 1 2 3
- 2 3
- 3 4
- 4 5
- 5 6
- 6
- When `G` is undirected, each edge is only listed once. For directed graphs,
- edges appear once for each direction.
- >>> G = nx.complete_graph(3, create_using=nx.DiGraph)
- >>> for line in nx.generate_adjlist(G):
- ... print(line)
- 0 1 2
- 1 0 2
- 2 0 1
- Node labels are shown multiple times for multiedges, but edge data (including keys)
- are not included in the output.
- >>> G = nx.MultiGraph([(0, 1, {"weight": 1}), (0, 1, {"weight": 2})])
- >>> for line in nx.generate_adjlist(G):
- ... print(line)
- 0 1 1
- 1
- See Also
- --------
- write_adjlist, read_adjlist
- Notes
- -----
- The default `delimiter=" "` will result in unexpected results if node names contain
- whitespace characters. To avoid this problem, specify an alternate delimiter when spaces are
- valid in node names.
- NB: This option is not available for data that isn't user-generated.
- """
- seen = set()
- directed = G.is_directed()
- multigraph = G.is_multigraph()
- for s, nbrs in G.adjacency():
- nodes = [str(s)]
- for t, data in nbrs.items():
- if t in seen:
- continue
- if multigraph and len(data) > 1:
- nodes.extend((str(t),) * len(data))
- else:
- nodes.append(str(t))
- if not directed:
- seen.add(s)
- yield delimiter.join(nodes)
- @open_file(1, mode="wb")
- def write_adjlist(G, path, comments="#", delimiter=" ", encoding="utf-8"):
- """Write graph G in single-line adjacency-list format to path.
- Parameters
- ----------
- G : NetworkX graph
- path : string or file
- Filename or file handle for data output.
- Filenames ending in .gz or .bz2 will be compressed.
- comments : string, optional
- Marker for comment lines
- delimiter : string, optional
- Separator for node labels
- encoding : string, optional
- Text encoding.
- Examples
- --------
- >>> G = nx.path_graph(4)
- >>> nx.write_adjlist(G, "path4.adjlist")
- The path can be a filehandle or a string with the name of the file. If a
- filehandle is provided, it has to be opened in 'wb' mode.
- >>> fh = open("path4.adjlist2", "wb")
- >>> nx.write_adjlist(G, fh)
- Notes
- -----
- The default `delimiter=" "` will result in unexpected results if node names contain
- whitespace characters. To avoid this problem, specify an alternate delimiter when spaces are
- valid in node names.
- NB: This option is not available for data that isn't user-generated.
- This format does not store graph, node, or edge data.
- See Also
- --------
- read_adjlist, generate_adjlist
- """
- import sys
- import time
- pargs = comments + " ".join(sys.argv) + "\n"
- header = (
- pargs
- + comments
- + f" GMT {time.asctime(time.gmtime())}\n"
- + comments
- + f" {G.name}\n"
- )
- path.write(header.encode(encoding))
- for line in generate_adjlist(G, delimiter):
- line += "\n"
- path.write(line.encode(encoding))
- @nx._dispatchable(graphs=None, returns_graph=True)
- def parse_adjlist(
- lines, comments="#", delimiter=None, create_using=None, nodetype=None
- ):
- """Parse lines of a graph adjacency list representation.
- Parameters
- ----------
- lines : list or iterator of strings
- Input data in adjlist format
- 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.
- comments : string, optional
- Marker for comment lines
- delimiter : string, optional
- Separator for node labels. The default is whitespace.
- Returns
- -------
- G: NetworkX graph
- The graph corresponding to the lines in adjacency list format.
- Examples
- --------
- >>> lines = ["1 2 5", "2 3 4", "3 5", "4", "5"]
- >>> G = nx.parse_adjlist(lines, nodetype=int)
- >>> nodes = [1, 2, 3, 4, 5]
- >>> all(node in G for node in nodes)
- True
- >>> edges = [(1, 2), (1, 5), (2, 3), (2, 4), (3, 5)]
- >>> all((u, v) in G.edges() or (v, u) in G.edges() for (u, v) in edges)
- True
- See Also
- --------
- read_adjlist
- """
- G = nx.empty_graph(0, create_using)
- for line in lines:
- p = line.find(comments)
- if p >= 0:
- line = line[:p]
- if not len(line):
- continue
- vlist = line.rstrip("\n").split(delimiter)
- u = vlist.pop(0)
- # convert types
- if nodetype is not None:
- try:
- u = nodetype(u)
- except BaseException as err:
- raise TypeError(
- f"Failed to convert node ({u}) to type {nodetype}"
- ) from err
- G.add_node(u)
- if nodetype is not None:
- try:
- vlist = list(map(nodetype, vlist))
- except BaseException as err:
- raise TypeError(
- f"Failed to convert nodes ({','.join(vlist)}) to type {nodetype}"
- ) from err
- G.add_edges_from([(u, v) for v in vlist])
- return G
- @open_file(0, mode="rb")
- @nx._dispatchable(graphs=None, returns_graph=True)
- def read_adjlist(
- path,
- comments="#",
- delimiter=None,
- create_using=None,
- nodetype=None,
- encoding="utf-8",
- ):
- """Read graph in adjacency list format from path.
- Parameters
- ----------
- path : string or file
- Filename or file handle to read.
- Filenames ending in .gz or .bz2 will be decompressed.
- 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.
- comments : string, optional
- Marker for comment lines
- delimiter : string, optional
- Separator for node labels. The default is whitespace.
- Returns
- -------
- G: NetworkX graph
- The graph corresponding to the lines in adjacency list format.
- Examples
- --------
- >>> G = nx.path_graph(4)
- >>> nx.write_adjlist(G, "test.adjlist")
- >>> G = nx.read_adjlist("test.adjlist")
- The path can be a filehandle or a string with the name of the file. If a
- filehandle is provided, it has to be opened in 'rb' mode.
- >>> fh = open("test.adjlist", "rb")
- >>> G = nx.read_adjlist(fh)
- Filenames ending in .gz or .bz2 will be compressed.
- >>> nx.write_adjlist(G, "test.adjlist.gz")
- >>> G = nx.read_adjlist("test.adjlist.gz")
- The optional nodetype is a function to convert node strings to nodetype.
- For example
- >>> G = nx.read_adjlist("test.adjlist", nodetype=int)
- will attempt to convert all nodes to integer type.
- Since nodes must be hashable, the function nodetype must return hashable
- types (e.g. int, float, str, frozenset - or tuples of those, etc.)
- The optional create_using parameter indicates the type of NetworkX graph
- created. The default is `nx.Graph`, an undirected graph.
- To read the data as a directed graph use
- >>> G = nx.read_adjlist("test.adjlist", create_using=nx.DiGraph)
- Notes
- -----
- This format does not store graph or node data.
- See Also
- --------
- write_adjlist
- """
- lines = (line.decode(encoding) for line in path)
- return parse_adjlist(
- lines,
- comments=comments,
- delimiter=delimiter,
- create_using=create_using,
- nodetype=nodetype,
- )
|