| # -*- coding: utf-8 -*- |
| """ |
| ************************* |
| Multi-line Adjacency List |
| ************************* |
| Read and write NetworkX graphs as multi-line adjacency lists. |
| |
| The multi-line adjacency list format is useful for graphs with |
| nodes that can be meaningfully represented as strings. With this format |
| simple edge data can be stored but node or graph data is not. |
| |
| Format |
| ------ |
| The first label in a line is the source node label followed by the node degree |
| d. The next d lines are target node labels and optional edge data. |
| That pattern repeats for all nodes in the graph. |
| |
| 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):: |
| |
| # example.multiline-adjlist |
| a 2 |
| b |
| c |
| d 1 |
| e |
| """ |
| __author__ = '\n'.join(['Aric Hagberg <hagberg@lanl.gov>', |
| 'Dan Schult <dschult@colgate.edu>', |
| 'Loïc Séguin-C. <loicseguin@gmail.com>']) |
| # Copyright (C) 2004-2010 by |
| # Aric Hagberg <hagberg@lanl.gov> |
| # Dan Schult <dschult@colgate.edu> |
| # Pieter Swart <swart@lanl.gov> |
| # All rights reserved. |
| # BSD license. |
| |
| __all__ = ['generate_multiline_adjlist', |
| 'write_multiline_adjlist', |
| 'parse_multiline_adjlist', |
| 'read_multiline_adjlist'] |
| |
| from networkx.utils import make_str, open_file |
| import networkx as nx |
| |
| def generate_multiline_adjlist(G, delimiter = ' '): |
| """Generate a single line of the graph G in multiline adjacency list format. |
| |
| Parameters |
| ---------- |
| G : NetworkX graph |
| |
| delimiter : string, optional |
| Separator for node labels |
| |
| Returns |
| ------- |
| lines : string |
| Lines of data in multiline adjlist format. |
| |
| Examples |
| -------- |
| >>> G = nx.lollipop_graph(4, 3) |
| >>> for line in nx.generate_multiline_adjlist(G): |
| ... print(line) |
| 0 3 |
| 1 {} |
| 2 {} |
| 3 {} |
| 1 2 |
| 2 {} |
| 3 {} |
| 2 1 |
| 3 {} |
| 3 1 |
| 4 {} |
| 4 1 |
| 5 {} |
| 5 1 |
| 6 {} |
| 6 0 |
| |
| See Also |
| -------- |
| write_multiline_adjlist, read_multiline_adjlist |
| """ |
| if G.is_directed(): |
| if G.is_multigraph(): |
| for s,nbrs in G.adjacency_iter(): |
| nbr_edges=[ (u,data) |
| for u,datadict in nbrs.items() |
| for key,data in datadict.items()] |
| deg=len(nbr_edges) |
| yield make_str(s)+delimiter+"%i"%(deg) |
| for u,d in nbr_edges: |
| if d is None: |
| yield make_str(u) |
| else: |
| yield make_str(u)+delimiter+make_str(d) |
| else: # directed single edges |
| for s,nbrs in G.adjacency_iter(): |
| deg=len(nbrs) |
| yield make_str(s)+delimiter+"%i"%(deg) |
| for u,d in nbrs.items(): |
| if d is None: |
| yield make_str(u) |
| else: |
| yield make_str(u)+delimiter+make_str(d) |
| else: # undirected |
| if G.is_multigraph(): |
| seen=set() # helper dict used to avoid duplicate edges |
| for s,nbrs in G.adjacency_iter(): |
| nbr_edges=[ (u,data) |
| for u,datadict in nbrs.items() |
| if u not in seen |
| for key,data in datadict.items()] |
| deg=len(nbr_edges) |
| yield make_str(s)+delimiter+"%i"%(deg) |
| for u,d in nbr_edges: |
| if d is None: |
| yield make_str(u) |
| else: |
| yield make_str(u)+delimiter+make_str(d) |
| seen.add(s) |
| else: # undirected single edges |
| seen=set() # helper dict used to avoid duplicate edges |
| for s,nbrs in G.adjacency_iter(): |
| nbr_edges=[ (u,d) for u,d in nbrs.items() if u not in seen] |
| deg=len(nbr_edges) |
| yield make_str(s)+delimiter+"%i"%(deg) |
| for u,d in nbr_edges: |
| if d is None: |
| yield make_str(u) |
| else: |
| yield make_str(u)+delimiter+make_str(d) |
| seen.add(s) |
| |
| @open_file(1,mode='wb') |
| def write_multiline_adjlist(G, path, delimiter=' ', |
| comments='#', encoding = 'utf-8'): |
| """ Write the graph G in multiline adjacency list format to path |
| |
| Parameters |
| ---------- |
| G : NetworkX graph |
| |
| 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_multiline_adjlist(G,"test.adjlist") |
| |
| The path can be a file handle or a string with the name of the file. If a |
| file handle is provided, it has to be opened in 'wb' mode. |
| |
| >>> fh=open("test.adjlist",'wb') |
| >>> nx.write_multiline_adjlist(G,fh) |
| |
| Filenames ending in .gz or .bz2 will be compressed. |
| |
| >>> nx.write_multiline_adjlist(G,"test.adjlist.gz") |
| |
| See Also |
| -------- |
| read_multiline_adjlist |
| """ |
| import sys |
| import time |
| |
| pargs=comments+" ".join(sys.argv) |
| header = ("%s\n" % (pargs) |
| + comments + " GMT %s\n" % (time.asctime(time.gmtime())) |
| + comments + " %s\n" % (G.name)) |
| path.write(header.encode(encoding)) |
| |
| for multiline in generate_multiline_adjlist(G, delimiter): |
| multiline+='\n' |
| path.write(multiline.encode(encoding)) |
| |
| def parse_multiline_adjlist(lines, comments = '#', delimiter = None, |
| create_using = None, nodetype = None, |
| edgetype = None): |
| """Parse lines of a multiline adjacency list representation of a graph. |
| |
| Parameters |
| ---------- |
| lines : list or iterator of strings |
| Input data in multiline adjlist format |
| |
| create_using: NetworkX graph container |
| Use given NetworkX graph for holding nodes or edges. |
| |
| 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. |
| |
| create_using: NetworkX graph container |
| Use given NetworkX graph for holding nodes or edges. |
| |
| |
| Returns |
| ------- |
| G: NetworkX graph |
| The graph corresponding to the lines in multiline adjacency list format. |
| |
| Examples |
| -------- |
| >>> lines = ['1 2', |
| ... "2 {'weight':3, 'name': 'Frodo'}", |
| ... "3 {}", |
| ... "2 1", |
| ... "5 {'weight':6, 'name': 'Saruman'}"] |
| >>> G = nx.parse_multiline_adjlist(iter(lines), nodetype = int) |
| >>> G.nodes() |
| [1, 2, 3, 5] |
| """ |
| from ast import literal_eval |
| if create_using is None: |
| G=nx.Graph() |
| else: |
| try: |
| G=create_using |
| G.clear() |
| except: |
| raise TypeError("Input graph is not a networkx graph type") |
| |
| for line in lines: |
| p=line.find(comments) |
| if p>=0: |
| line = line[:p] |
| if not line: continue |
| try: |
| (u,deg)=line.strip().split(delimiter) |
| deg=int(deg) |
| except: |
| raise TypeError("Failed to read node and degree on line (%s)"%line) |
| if nodetype is not None: |
| try: |
| u=nodetype(u) |
| except: |
| raise TypeError("Failed to convert node (%s) to type %s"\ |
| %(u,nodetype)) |
| G.add_node(u) |
| for i in range(deg): |
| while True: |
| try: |
| line = next(lines) |
| except StopIteration: |
| msg = "Failed to find neighbor for node (%s)" % (u,) |
| raise TypeError(msg) |
| p=line.find(comments) |
| if p>=0: |
| line = line[:p] |
| if line: break |
| vlist=line.strip().split(delimiter) |
| numb=len(vlist) |
| if numb<1: |
| continue # isolated node |
| v=vlist.pop(0) |
| data=''.join(vlist) |
| if nodetype is not None: |
| try: |
| v=nodetype(v) |
| except: |
| raise TypeError( |
| "Failed to convert node (%s) to type %s"\ |
| %(v,nodetype)) |
| if edgetype is not None: |
| try: |
| edgedata={'weight':edgetype(data)} |
| except: |
| raise TypeError( |
| "Failed to convert edge data (%s) to type %s"\ |
| %(data, edgetype)) |
| else: |
| try: # try to evaluate |
| edgedata=literal_eval(data) |
| except: |
| edgedata={} |
| G.add_edge(u,v,attr_dict=edgedata) |
| |
| return G |
| |
| @open_file(0,mode='rb') |
| def read_multiline_adjlist(path, comments="#", delimiter=None, |
| create_using=None, |
| nodetype=None, edgetype=None, |
| encoding = 'utf-8'): |
| """Read graph in multi-line adjacency list format from path. |
| |
| Parameters |
| ---------- |
| path : string or file |
| Filename or file handle to read. |
| Filenames ending in .gz or .bz2 will be uncompressed. |
| |
| create_using: NetworkX graph container |
| Use given NetworkX graph for holding nodes or edges. |
| |
| nodetype : Python type, optional |
| Convert nodes to this type. |
| |
| edgetype : Python type, optional |
| Convert edge data to this type. |
| |
| comments : string, optional |
| Marker for comment lines |
| |
| delimiter : string, optional |
| Separator for node labels. The default is whitespace. |
| |
| create_using: NetworkX graph container |
| Use given NetworkX graph for holding nodes or edges. |
| |
| |
| Returns |
| ------- |
| G: NetworkX graph |
| |
| Examples |
| -------- |
| >>> G=nx.path_graph(4) |
| >>> nx.write_multiline_adjlist(G,"test.adjlist") |
| >>> G=nx.read_multiline_adjlist("test.adjlist") |
| |
| The path can be a file or a string with the name of the file. If a |
| file s provided, it has to be opened in 'rb' mode. |
| |
| >>> fh=open("test.adjlist", 'rb') |
| >>> G=nx.read_multiline_adjlist(fh) |
| |
| Filenames ending in .gz or .bz2 will be compressed. |
| |
| >>> nx.write_multiline_adjlist(G,"test.adjlist.gz") |
| >>> G=nx.read_multiline_adjlist("test.adjlist.gz") |
| |
| The optional nodetype is a function to convert node strings to nodetype. |
| |
| For example |
| |
| >>> G=nx.read_multiline_adjlist("test.adjlist", nodetype=int) |
| |
| will attempt to convert all nodes to integer type. |
| |
| The optional edgetype is a function to convert edge data strings to |
| edgetype. |
| |
| >>> G=nx.read_multiline_adjlist("test.adjlist") |
| |
| The optional create_using parameter is a NetworkX graph container. |
| The default is Graph(), an undirected graph. To read the data as |
| a directed graph use |
| |
| >>> G=nx.read_multiline_adjlist("test.adjlist", create_using=nx.DiGraph()) |
| |
| Notes |
| ----- |
| This format does not store graph, node, or edge data. |
| |
| See Also |
| -------- |
| write_multiline_adjlist |
| """ |
| lines = (line.decode(encoding) for line in path) |
| return parse_multiline_adjlist(lines, |
| comments = comments, |
| delimiter = delimiter, |
| create_using = create_using, |
| nodetype = nodetype, |
| edgetype = edgetype) |
| |
| |
| # fixture for nose tests |
| def teardown_module(module): |
| import os |
| os.unlink('test.adjlist') |
| os.unlink('test.adjlist.gz') |