Source code for networkx.algorithms.bipartite.edgelist

"""
********************
Bipartite Edge Lists
********************
Read and write NetworkX graphs as bipartite edge lists.

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

For each edge (u, v) the node u is assigned to part 0 and the node v to part 1.
"""

__all__ = ["generate_edgelist", "write_edgelist", "parse_edgelist", "read_edgelist"]

import networkx as nx
from networkx.utils import not_implemented_for, open_file


[docs] @open_file(1, mode="wb") def write_edgelist(G, path, comments="#", delimiter=" ", data=True, encoding="utf-8"): """Write a bipartite graph as a list of edges. Parameters ---------- G : Graph A NetworkX bipartite 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 -------- >>> from pathlib import Path >>> import tempfile, gzip >>> tmp_path = Path(tempfile.gettempdir()) >>> G = nx.path_graph(4) >>> G.add_nodes_from([0, 2], bipartite=0) >>> G.add_nodes_from([1, 3], bipartite=1) Write out the bipartite edgelist to file >>> fpath = tmp_path / "test.edgelist" >>> nx.bipartite.write_edgelist(G, fpath) >>> with open(fpath) as fh: ... print(fh.read()) 0 1 {} 2 1 {} 2 3 {} <BLANKLINE> A filename ending with ".gz" or ".bz2" will be automatically compressed >>> fpath = tmp_path / "test_edgelist.gz" >>> nx.bipartite.write_edgelist(G, fpath) >>> with gzip.open(fpath) as fh: ... print(fh.read().decode()) 0 1 {} 2 1 {} 2 3 {} <BLANKLINE> The `data` keyword argument is used to toggle whether edge attribute data are included: >>> G = nx.Graph() >>> G.add_node(1, bipartite=0) >>> G.add_node(2, bipartite=1) >>> G.add_edge(1, 2, weight=7, color="red") >>> fpath = tmp_path / "test.edgelist" >>> nx.bipartite.write_edgelist(G, fpath, data=False) >>> with open(fpath) as fh: ... print(fh.read()) 1 2 <BLANKLINE> Or to specify which edge attribute data to include: >>> nx.bipartite.write_edgelist(G, fpath, data=["color"]) >>> with open(fpath) as fh: ... print(fh.read()) 1 2 red <BLANKLINE> >>> nx.bipartite.write_edgelist(G, fpath, data=["color", "weight"]) >>> with open(fpath) as fh: ... print(fh.read()) 1 2 red 7 <BLANKLINE> See Also -------- write_edgelist generate_edgelist """ for line in generate_edgelist(G, delimiter, data): line += "\n" path.write(line.encode(encoding))
[docs] @not_implemented_for("directed") def generate_edgelist(G, delimiter=" ", data=True): """Generate a single line of the bipartite graph G in edge list format. Parameters ---------- G : NetworkX graph The graph is assumed to have node attribute `part` set to 0,1 representing the two graph parts 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 -------- >>> from networkx.algorithms import bipartite >>> G = nx.path_graph(4) >>> G.add_nodes_from([0, 2], bipartite=0) >>> G.add_nodes_from([1, 3], bipartite=1) >>> G[1][2]["weight"] = 3 >>> G[2][3]["capacity"] = 12 >>> for line in bipartite.generate_edgelist(G, data=False): ... print(line) 0 1 2 1 2 3 >>> for line in bipartite.generate_edgelist(G): ... print(line) 0 1 {} 2 1 {'weight': 3} 2 3 {'capacity': 12} >>> for line in bipartite.generate_edgelist(G, data=["weight"]): ... print(line) 0 1 2 1 3 2 3 """ try: part0 = [n for n, d in G.nodes.items() if d["bipartite"] == 0] except BaseException as err: raise AttributeError("Missing node attribute `bipartite`") from err if data is True or data is False: for n in part0: for edge in G.edges(n, data=data): yield delimiter.join(map(str, edge)) else: for n in part0: for u, v, d in G.edges(n, data=True): edge = [u, v] try: edge.extend(d[k] for k in data) except KeyError: pass # missing data for this edge, should warn? yield delimiter.join(map(str, edge))
[docs] @nx._dispatchable(name="bipartite_parse_edgelist", graphs=None, returns_graph=True) def parse_edgelist( lines, comments="#", delimiter=None, create_using=None, nodetype=None, data=True ): """Parse lines of an edge list representation of a bipartite graph. Parameters ---------- lines : list or iterator of strings Input data in edgelist format comments : string, optional Marker for comment lines delimiter : string, optional Separator for node labels create_using: NetworkX graph container, optional Use given NetworkX graph for holding nodes or edges. nodetype : Python type, optional Convert nodes to this type. 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 bipartite graph corresponding to lines Examples -------- Edgelist with no data: >>> from networkx.algorithms import bipartite >>> lines = ["1 2", "2 3", "3 4"] >>> G = bipartite.parse_edgelist(lines, nodetype=int) >>> sorted(G.nodes()) [1, 2, 3, 4] >>> sorted(G.nodes(data=True)) [(1, {'bipartite': 0}), (2, {'bipartite': 0}), (3, {'bipartite': 0}), (4, {'bipartite': 1})] >>> sorted(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 = bipartite.parse_edgelist(lines, nodetype=int) >>> sorted(G.nodes()) [1, 2, 3, 4] >>> sorted(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 = bipartite.parse_edgelist(lines, nodetype=int, data=(("weight", float),)) >>> sorted(G.nodes()) [1, 2, 3, 4] >>> sorted(G.edges(data=True)) [(1, 2, {'weight': 3.0}), (2, 3, {'weight': 27.0}), (3, 4, {'weight': 3.0})] See Also -------- """ 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 len(line): continue # split line, should have 2 or more s = line.rstrip("\n").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 BaseException 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 edgedata = dict(literal_eval(" ".join(d))) except BaseException 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 BaseException as err: raise TypeError( f"Failed to convert {edge_key} data " f"{edge_value} to type {edge_type}." ) from err edgedata.update({edge_key: edge_value}) G.add_node(u, bipartite=0) G.add_node(v, bipartite=1) G.add_edge(u, v, **edgedata) return G
[docs] @open_file(0, mode="rb") @nx._dispatchable(name="bipartite_read_edgelist", graphs=None, returns_graph=True) def read_edgelist( path, comments="#", delimiter=None, create_using=None, nodetype=None, data=True, edgetype=None, encoding="utf-8", ): """Read a bipartite 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 decompressed. 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 : Graph container, optional, Use specified container to build graph. The default is networkx.Graph, an undirected graph. 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 -------- >>> from pathlib import Path >>> import tempfile >>> tmp_path = Path(tempfile.gettempdir()) >>> fpath = tmp_path / "test.edgelist" >>> G = nx.path_graph(4) >>> G.add_nodes_from([0, 2], bipartite=0) >>> G.add_nodes_from([1, 3], bipartite=1) >>> nx.bipartite.write_edgelist(G, fpath) >>> H = nx.bipartite.read_edgelist(fpath) >>> H.nodes(data="bipartite") NodeDataView({'0': 0, '1': 1, '2': 0, '3': 1}, data='bipartite') >>> H.edges EdgeView([('0', '1'), ('1', '2'), ('2', '3')]) Nodes read from the file are interpreted as strings by default. The `nodetype` parameter can be used to convert the nodes to another type: >>> H = nx.bipartite.read_edgelist(fpath, nodetype=int) >>> H.edges EdgeView([(0, 1), (1, 2), (2, 3)]) The optional `create_using` parameter indicates the type of NetworkX graph created. By default an undirected ``nx.Graph`` is returned. To read the data as a directed graph use: >>> H = nx.bipartite.read_edgelist(fpath, create_using=nx.DiGraph) >>> H.is_directed() True By default, edge data is expected to be stored in a serialized dict-like format keyed by attribute name: >>> fpath = tmp_path / "test_edgelist.data" >>> with open(fpath, "w") as fh: ... _ = fh.write("1 2 {'weight': 3}") >>> G = nx.bipartite.read_edgelist(fpath, nodetype=int) >>> G.nodes(data="bipartite") NodeDataView({1: 0, 2: 1}, data='bipartite') >>> G.edges(data=True) EdgeDataView([(1, 2, {'weight': 3})]) Alternatively, edge data stored as a flat list without keys can be parsed by passing in the attribute name and data type via `data`: >>> textline = "1 2 red 10" >>> fpath = tmp_path / "test_edgelist.multidata" >>> with open(fpath, "w") as fh: # Create a file with multi edge data attrs ... _ = fh.write(textline) >>> G = nx.bipartite.read_edgelist( ... fpath, nodetype=int, data=[("color", str), ("weight", float)] ... ) >>> G.nodes(data="bipartite") NodeDataView({1: 0, 2: 1}, data='bipartite') >>> G.edges(data=True) EdgeDataView([(1, 2, {'color': 'red', 'weight': 10.0})]) See Also -------- parse_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.decode(encoding) for line in path) return parse_edgelist( lines, comments=comments, delimiter=delimiter, create_using=create_using, nodetype=nodetype, data=data, )