from itertools import chain
from itertools import count
import easygraph as eg
__all__ = ["node_link_graph"]
_attrs = dict(source="source", target="target", name="id", key="key", link="links")
def _to_tuple(x):
"""Converts lists to tuples, including nested lists.
All other non-list inputs are passed through unmodified. This function is
intended to be used to convert potentially nested lists from json files
into valid nodes.
Examples
--------
>>> _to_tuple([1, 2, [3, 4]])
(1, 2, (3, 4))
"""
if not isinstance(x, (tuple, list)):
return x
return tuple(map(_to_tuple, x))
[docs]
def node_link_graph(data, directed=False, multigraph=True, attrs=None):
"""Returns graph from node-link data format.
Parameters
----------
data : dict
node-link formatted graph data
directed : bool
If True, and direction not specified in data, return a directed graph.
multigraph : bool
If True, and multigraph not specified in data, return a multigraph.
attrs : dict
A dictionary that contains five keys 'source', 'target', 'name',
'key' and 'link'. The corresponding values provide the attribute
names for storing NetworkX-internal graph data. Default value:
dict(source='source', target='target', name='id',
key='key', link='links')
Returns
-------
G : EasyGraph graph
A EasyGraph graph object
Examples
--------
>>> from easygraph.readwrite import json_graph
>>> G = eg.Graph([("A", "B")])
>>> data = json_graph.node_link_data(G)
>>> H = json_graph.node_link_graph(data)
Notes
-----
Attribute 'key' is only used for multigraphs.
See Also
--------
node_link_data, adjacency_data, tree_data
"""
# Allow 'attrs' to keep default values.
if attrs is None:
attrs = _attrs
else:
attrs.update({k: v for k, v in _attrs.items() if k not in attrs})
multigraph = data.get("multigraph", multigraph)
directed = data.get("directed", directed)
if multigraph:
graph = eg.MultiGraph()
else:
graph = eg.Graph()
if directed:
graph = graph.to_directed()
name = attrs["name"]
source = attrs["source"]
target = attrs["target"]
links = attrs["link"]
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
key = None if not multigraph else attrs["key"]
graph.graph = data.get("graph", {})
c = count()
for d in data["nodes"]:
node = _to_tuple(d.get(name, next(c)))
nodedata = {str(k): v for k, v in d.items() if k != name}
graph.add_node(node, **nodedata)
for d in data[links]:
src = tuple(d[source]) if isinstance(d[source], list) else d[source]
tgt = tuple(d[target]) if isinstance(d[target], list) else d[target]
if not multigraph:
edgedata = {str(k): v for k, v in d.items() if k != source and k != target}
graph.add_edge(src, tgt, **edgedata)
else:
ky = d.get(key, None)
edgedata = {
str(k): v
for k, v in d.items()
if k != source and k != target and k != key
}
graph.add_edge(src, tgt, ky, **edgedata)
return graph