Fixed database typo and removed unnecessary class identifier.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-14 10:10:37 -04:00
parent 00ad49a143
commit 45fb349a7d
5098 changed files with 952558 additions and 85 deletions

View file

@ -0,0 +1,19 @@
"""
*********
JSON data
*********
Generate and parse JSON serializable data for NetworkX graphs.
These formats are suitable for use with the d3.js examples https://d3js.org/
The three formats that you can generate with NetworkX are:
- node-link like in the d3.js example https://bl.ocks.org/mbostock/4062045
- tree like in the d3.js example https://bl.ocks.org/mbostock/4063550
- adjacency like in the d3.js example https://bost.ocks.org/mike/miserables/
"""
from networkx.readwrite.json_graph.node_link import *
from networkx.readwrite.json_graph.adjacency import *
from networkx.readwrite.json_graph.tree import *
from networkx.readwrite.json_graph.jit import *
from networkx.readwrite.json_graph.cytoscape import *

View file

@ -0,0 +1,156 @@
from itertools import chain
import networkx as nx
__all__ = ["adjacency_data", "adjacency_graph"]
_attrs = dict(id="id", key="key")
def adjacency_data(G, attrs=_attrs):
"""Returns data in adjacency format that is suitable for JSON serialization
and use in Javascript documents.
Parameters
----------
G : NetworkX graph
attrs : dict
A dictionary that contains two keys 'id' and 'key'. The corresponding
values provide the attribute names for storing NetworkX-internal graph
data. The values should be unique. Default value:
:samp:`dict(id='id', key='key')`.
If some user-defined graph data use these attribute names as data keys,
they may be silently dropped.
Returns
-------
data : dict
A dictionary with adjacency formatted data.
Raises
------
NetworkXError
If values in attrs are not unique.
Examples
--------
>>> from networkx.readwrite import json_graph
>>> G = nx.Graph([(1, 2)])
>>> data = json_graph.adjacency_data(G)
To serialize with json
>>> import json
>>> s = json.dumps(data)
Notes
-----
Graph, node, and link attributes will be written when using this format
but attribute keys must be strings if you want to serialize the resulting
data with JSON.
The default value of attrs will be changed in a future release of NetworkX.
See Also
--------
adjacency_graph, node_link_data, tree_data
"""
multigraph = G.is_multigraph()
id_ = attrs["id"]
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
key = None if not multigraph else attrs["key"]
if id_ == key:
raise nx.NetworkXError("Attribute names are not unique.")
data = {}
data["directed"] = G.is_directed()
data["multigraph"] = multigraph
data["graph"] = list(G.graph.items())
data["nodes"] = []
data["adjacency"] = []
for n, nbrdict in G.adjacency():
data["nodes"].append(dict(chain(G.nodes[n].items(), [(id_, n)])))
adj = []
if multigraph:
for nbr, keys in nbrdict.items():
for k, d in keys.items():
adj.append(dict(chain(d.items(), [(id_, nbr), (key, k)])))
else:
for nbr, d in nbrdict.items():
adj.append(dict(chain(d.items(), [(id_, nbr)])))
data["adjacency"].append(adj)
return data
def adjacency_graph(data, directed=False, multigraph=True, attrs=_attrs):
"""Returns graph from adjacency data format.
Parameters
----------
data : dict
Adjacency list formatted graph data
Returns
-------
G : NetworkX graph
A NetworkX graph object
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 two keys 'id' and 'key'. The corresponding
values provide the attribute names for storing NetworkX-internal graph
data. The values should be unique. Default value:
:samp:`dict(id='id', key='key')`.
Examples
--------
>>> from networkx.readwrite import json_graph
>>> G = nx.Graph([(1, 2)])
>>> data = json_graph.adjacency_data(G)
>>> H = json_graph.adjacency_graph(data)
Notes
-----
The default value of attrs will be changed in a future release of NetworkX.
See Also
--------
adjacency_graph, node_link_data, tree_data
"""
multigraph = data.get("multigraph", multigraph)
directed = data.get("directed", directed)
if multigraph:
graph = nx.MultiGraph()
else:
graph = nx.Graph()
if directed:
graph = graph.to_directed()
id_ = attrs["id"]
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
key = None if not multigraph else attrs["key"]
graph.graph = dict(data.get("graph", []))
mapping = []
for d in data["nodes"]:
node_data = d.copy()
node = node_data.pop(id_)
mapping.append(node)
graph.add_node(node)
graph.nodes[node].update(node_data)
for i, d in enumerate(data["adjacency"]):
source = mapping[i]
for tdata in d:
target_data = tdata.copy()
target = target_data.pop(id_)
if not multigraph:
graph.add_edge(source, target)
graph[source][target].update(tdata)
else:
ky = target_data.pop(key, None)
graph.add_edge(source, target, key=ky)
graph[source][target][ky].update(tdata)
return graph

View file

@ -0,0 +1,110 @@
import networkx as nx
__all__ = ["cytoscape_data", "cytoscape_graph"]
_attrs = dict(name="name", ident="id")
def cytoscape_data(G, attrs=None):
"""Returns data in Cytoscape JSON format (cyjs).
Parameters
----------
G : NetworkX Graph
Returns
-------
data: dict
A dictionary with cyjs formatted data.
Raises
------
NetworkXError
If values in attrs are not unique.
"""
if not attrs:
attrs = _attrs
else:
attrs.update({k: v for (k, v) in _attrs.items() if k not in attrs})
name = attrs["name"]
ident = attrs["ident"]
if len({name, ident}) < 2:
raise nx.NetworkXError("Attribute names are not unique.")
jsondata = {"data": list(G.graph.items())}
jsondata["directed"] = G.is_directed()
jsondata["multigraph"] = G.is_multigraph()
jsondata["elements"] = {"nodes": [], "edges": []}
nodes = jsondata["elements"]["nodes"]
edges = jsondata["elements"]["edges"]
for i, j in G.nodes.items():
n = {"data": j.copy()}
n["data"]["id"] = j.get(ident) or str(i)
n["data"]["value"] = i
n["data"]["name"] = j.get(name) or str(i)
nodes.append(n)
if G.is_multigraph():
for e in G.edges(keys=True):
n = {"data": G.adj[e[0]][e[1]][e[2]].copy()}
n["data"]["source"] = e[0]
n["data"]["target"] = e[1]
n["data"]["key"] = e[2]
edges.append(n)
else:
for e in G.edges():
n = {"data": G.adj[e[0]][e[1]].copy()}
n["data"]["source"] = e[0]
n["data"]["target"] = e[1]
edges.append(n)
return jsondata
def cytoscape_graph(data, attrs=None):
if not attrs:
attrs = _attrs
else:
attrs.update({k: v for (k, v) in _attrs.items() if k not in attrs})
name = attrs["name"]
ident = attrs["ident"]
if len({ident, name}) < 2:
raise nx.NetworkXError("Attribute names are not unique.")
multigraph = data.get("multigraph")
directed = data.get("directed")
if multigraph:
graph = nx.MultiGraph()
else:
graph = nx.Graph()
if directed:
graph = graph.to_directed()
graph.graph = dict(data.get("data"))
for d in data["elements"]["nodes"]:
node_data = d["data"].copy()
node = d["data"]["value"]
if d["data"].get(name):
node_data[name] = d["data"].get(name)
if d["data"].get(ident):
node_data[ident] = d["data"].get(ident)
graph.add_node(node)
graph.nodes[node].update(node_data)
for d in data["elements"]["edges"]:
edge_data = d["data"].copy()
sour = d["data"].pop("source")
targ = d["data"].pop("target")
if multigraph:
key = d["data"].get("key", 0)
graph.add_edge(sour, targ, key=key)
graph.edges[sour, targ, key].update(edge_data)
else:
graph.add_edge(sour, targ)
graph.edges[sour, targ].update(edge_data)
return graph

View file

@ -0,0 +1,103 @@
"""
Read and write NetworkX graphs as JavaScript InfoVis Toolkit (JIT) format JSON.
See the `JIT documentation`_ for more examples.
Format
------
var json = [
{
"id": "aUniqueIdentifier",
"name": "usually a nodes name",
"data": {
"some key": "some value",
"some other key": "some other value"
},
"adjacencies": [
{
nodeTo:"aNodeId",
data: {} //put whatever you want here
},
'other adjacencies go here...'
},
'other nodes go here...'
];
.. _JIT documentation: http://thejit.org
"""
import json
import networkx as nx
from networkx.utils.decorators import not_implemented_for
__all__ = ["jit_graph", "jit_data"]
def jit_graph(data, create_using=None):
"""Read a graph from JIT JSON.
Parameters
----------
data : JSON Graph Object
create_using : Networkx Graph, optional (default: Graph())
Return graph of this type. The provided instance will be cleared.
Returns
-------
G : NetworkX Graph built from create_using if provided.
"""
if create_using is None:
G = nx.Graph()
else:
G = create_using
G.clear()
if isinstance(data, str):
data = json.loads(data)
for node in data:
G.add_node(node["id"], **node["data"])
if node.get("adjacencies") is not None:
for adj in node["adjacencies"]:
G.add_edge(node["id"], adj["nodeTo"], **adj["data"])
return G
@not_implemented_for("multigraph")
def jit_data(G, indent=None, default=None):
"""Returns data in JIT JSON format.
Parameters
----------
G : NetworkX Graph
indent: optional, default=None
If indent is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level.
An indent level of 0, or negative, will only insert newlines.
None (the default) selects the most compact representation.
default: optional, default=None
It will pass the value to the json.dumps function in order to
be able to serialize custom objects used as nodes.
Returns
-------
data: JIT JSON string
"""
json_graph = []
for node in G.nodes():
json_node = {"id": node, "name": node}
# node data
json_node["data"] = G.nodes[node]
# adjacencies
if G[node]:
json_node["adjacencies"] = []
for neighbour in G[node]:
adjacency = {"nodeTo": neighbour}
# adjacency data
adjacency["data"] = G.edges[node, neighbour]
json_node["adjacencies"].append(adjacency)
json_graph.append(json_node)
return json.dumps(json_graph, indent=indent, default=default)

View file

@ -0,0 +1,184 @@
from itertools import chain, count
import networkx as nx
from networkx.utils import to_tuple
__all__ = ["node_link_data", "node_link_graph"]
_attrs = dict(source="source", target="target", name="id", key="key", link="links")
def node_link_data(G, attrs=None):
"""Returns data in node-link format that is suitable for JSON serialization
and use in Javascript documents.
Parameters
----------
G : NetworkX graph
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. The values should
be unique. Default value::
dict(source='source', target='target', name='id',
key='key', link='links')
If some user-defined graph data use these attribute names as data keys,
they may be silently dropped.
Returns
-------
data : dict
A dictionary with node-link formatted data.
Raises
------
NetworkXError
If values in attrs are not unique.
Examples
--------
>>> from networkx.readwrite import json_graph
>>> G = nx.Graph([("A", "B")])
>>> data1 = json_graph.node_link_data(G)
>>> H = nx.gn_graph(2)
>>> data2 = json_graph.node_link_data(
... H, {"link": "edges", "source": "from", "target": "to"}
... )
To serialize with json
>>> import json
>>> s1 = json.dumps(data1)
>>> s2 = json.dumps(
... data2, default={"link": "edges", "source": "from", "target": "to"}
... )
Notes
-----
Graph, node, and link attributes are stored in this format. Note that
attribute keys will be converted to strings in order to comply with JSON.
Attribute 'key' is only used for multigraphs.
See Also
--------
node_link_graph, adjacency_data, tree_data
"""
multigraph = G.is_multigraph()
# 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})
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"]
if len({source, target, key}) < 3:
raise nx.NetworkXError("Attribute names are not unique.")
data = {
"directed": G.is_directed(),
"multigraph": multigraph,
"graph": G.graph,
"nodes": [dict(chain(G.nodes[n].items(), [(name, n)])) for n in G],
}
if multigraph:
data[links] = [
dict(chain(d.items(), [(source, u), (target, v), (key, k)]))
for u, v, k, d in G.edges(keys=True, data=True)
]
else:
data[links] = [
dict(chain(d.items(), [(source, u), (target, v)]))
for u, v, d in G.edges(data=True)
]
return data
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 : NetworkX graph
A NetworkX graph object
Examples
--------
>>> from networkx.readwrite import json_graph
>>> G = nx.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 = nx.MultiGraph()
else:
graph = nx.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

View file

@ -0,0 +1,58 @@
import json
import pytest
import networkx as nx
from networkx.readwrite.json_graph import adjacency_data, adjacency_graph
class TestAdjacency:
def test_graph(self):
G = nx.path_graph(4)
H = adjacency_graph(adjacency_data(G))
nx.is_isomorphic(G, H)
def test_graph_attributes(self):
G = nx.path_graph(4)
G.add_node(1, color="red")
G.add_edge(1, 2, width=7)
G.graph["foo"] = "bar"
G.graph[1] = "one"
H = adjacency_graph(adjacency_data(G))
assert H.graph["foo"] == "bar"
assert H.nodes[1]["color"] == "red"
assert H[1][2]["width"] == 7
d = json.dumps(adjacency_data(G))
H = adjacency_graph(json.loads(d))
assert H.graph["foo"] == "bar"
assert H.graph[1] == "one"
assert H.nodes[1]["color"] == "red"
assert H[1][2]["width"] == 7
def test_digraph(self):
G = nx.DiGraph()
nx.add_path(G, [1, 2, 3])
H = adjacency_graph(adjacency_data(G))
assert H.is_directed()
nx.is_isomorphic(G, H)
def test_multidigraph(self):
G = nx.MultiDiGraph()
nx.add_path(G, [1, 2, 3])
H = adjacency_graph(adjacency_data(G))
assert H.is_directed()
assert H.is_multigraph()
def test_multigraph(self):
G = nx.MultiGraph()
G.add_edge(1, 2, key="first")
G.add_edge(1, 2, key="second", color="blue")
H = adjacency_graph(adjacency_data(G))
nx.is_isomorphic(G, H)
assert H[1][2]["second"]["color"] == "blue"
def test_exception(self):
with pytest.raises(nx.NetworkXError):
G = nx.MultiDiGraph()
attrs = dict(id="node", key="node")
adjacency_data(G, attrs)

View file

@ -0,0 +1,63 @@
import json
import pytest
import networkx as nx
from networkx.readwrite.json_graph import cytoscape_data, cytoscape_graph
class TestCytoscape:
def test_graph(self):
G = nx.path_graph(4)
H = cytoscape_graph(cytoscape_data(G))
nx.is_isomorphic(G, H)
def test_graph_attributes(self):
G = nx.path_graph(4)
G.add_node(1, color="red")
G.add_edge(1, 2, width=7)
G.graph["foo"] = "bar"
G.graph[1] = "one"
G.add_node(3, name="node", id="123")
H = cytoscape_graph(cytoscape_data(G))
assert H.graph["foo"] == "bar"
assert H.nodes[1]["color"] == "red"
assert H[1][2]["width"] == 7
assert H.nodes[3]["name"] == "node"
assert H.nodes[3]["id"] == "123"
d = json.dumps(cytoscape_data(G))
H = cytoscape_graph(json.loads(d))
assert H.graph["foo"] == "bar"
assert H.graph[1] == "one"
assert H.nodes[1]["color"] == "red"
assert H[1][2]["width"] == 7
assert H.nodes[3]["name"] == "node"
assert H.nodes[3]["id"] == "123"
def test_digraph(self):
G = nx.DiGraph()
nx.add_path(G, [1, 2, 3])
H = cytoscape_graph(cytoscape_data(G))
assert H.is_directed()
nx.is_isomorphic(G, H)
def test_multidigraph(self):
G = nx.MultiDiGraph()
nx.add_path(G, [1, 2, 3])
H = cytoscape_graph(cytoscape_data(G))
assert H.is_directed()
assert H.is_multigraph()
def test_multigraph(self):
G = nx.MultiGraph()
G.add_edge(1, 2, key="first")
G.add_edge(1, 2, key="second", color="blue")
H = cytoscape_graph(cytoscape_data(G))
assert nx.is_isomorphic(G, H)
assert H[1][2]["second"]["color"] == "blue"
def test_exception(self):
with pytest.raises(nx.NetworkXError):
G = nx.MultiDiGraph()
attrs = dict(name="node", ident="node")
cytoscape_data(G, attrs)

View file

@ -0,0 +1,64 @@
import json
import pytest
import networkx as nx
from networkx.readwrite.json_graph import jit_data, jit_graph
class TestJIT:
def test_jit(self):
G = nx.Graph()
G.add_node("Node1", node_data="foobar")
G.add_node("Node3", node_data="bar")
G.add_node("Node4")
G.add_edge("Node1", "Node2", weight=9, something="isSomething")
G.add_edge("Node2", "Node3", weight=4, something="isNotSomething")
G.add_edge("Node1", "Node2")
d = jit_data(G)
K = jit_graph(json.loads(d))
assert nx.is_isomorphic(G, K)
def test_jit_2(self):
G = nx.Graph()
G.add_node(1, node_data=3)
G.add_node(3, node_data=0)
G.add_edge(1, 2, weight=9, something=0)
G.add_edge(2, 3, weight=4, something=3)
G.add_edge(1, 2)
d = jit_data(G)
K = jit_graph(json.loads(d))
assert nx.is_isomorphic(G, K)
def test_jit_directed(self):
G = nx.DiGraph()
G.add_node(1, node_data=3)
G.add_node(3, node_data=0)
G.add_edge(1, 2, weight=9, something=0)
G.add_edge(2, 3, weight=4, something=3)
G.add_edge(1, 2)
d = jit_data(G)
K = jit_graph(json.loads(d), create_using=nx.DiGraph())
assert nx.is_isomorphic(G, K)
def test_jit_multi_directed(self):
G = nx.MultiDiGraph()
G.add_node(1, node_data=3)
G.add_node(3, node_data=0)
G.add_edge(1, 2, weight=9, something=0)
G.add_edge(2, 3, weight=4, something=3)
G.add_edge(1, 2)
pytest.raises(nx.NetworkXNotImplemented, jit_data, G)
H = nx.DiGraph(G)
d = jit_data(H)
K = jit_graph(json.loads(d), create_using=nx.MultiDiGraph())
assert nx.is_isomorphic(H, K)
K.add_edge(1, 2)
assert not nx.is_isomorphic(H, K)
assert nx.is_isomorphic(G, K)
def test_jit_round_trip(self):
G = nx.Graph()
d = nx.jit_data(G)
H = jit_graph(json.loads(d))
K = jit_graph(d)
assert nx.is_isomorphic(H, K)

View file

@ -0,0 +1,104 @@
import json
import pytest
import networkx as nx
from networkx.readwrite.json_graph import node_link_data, node_link_graph
class TestNodeLink:
def test_graph(self):
G = nx.path_graph(4)
H = node_link_graph(node_link_data(G))
assert nx.is_isomorphic(G, H)
def test_graph_attributes(self):
G = nx.path_graph(4)
G.add_node(1, color="red")
G.add_edge(1, 2, width=7)
G.graph[1] = "one"
G.graph["foo"] = "bar"
H = node_link_graph(node_link_data(G))
assert H.graph["foo"] == "bar"
assert H.nodes[1]["color"] == "red"
assert H[1][2]["width"] == 7
d = json.dumps(node_link_data(G))
H = node_link_graph(json.loads(d))
assert H.graph["foo"] == "bar"
assert H.graph["1"] == "one"
assert H.nodes[1]["color"] == "red"
assert H[1][2]["width"] == 7
def test_digraph(self):
G = nx.DiGraph()
H = node_link_graph(node_link_data(G))
assert H.is_directed()
def test_multigraph(self):
G = nx.MultiGraph()
G.add_edge(1, 2, key="first")
G.add_edge(1, 2, key="second", color="blue")
H = node_link_graph(node_link_data(G))
nx.is_isomorphic(G, H)
assert H[1][2]["second"]["color"] == "blue"
def test_graph_with_tuple_nodes(self):
G = nx.Graph()
G.add_edge((0, 0), (1, 0), color=[255, 255, 0])
d = node_link_data(G)
dumped_d = json.dumps(d)
dd = json.loads(dumped_d)
H = node_link_graph(dd)
assert H.nodes[(0, 0)] == G.nodes[(0, 0)]
assert H[(0, 0)][(1, 0)]["color"] == [255, 255, 0]
def test_unicode_keys(self):
q = "qualité"
G = nx.Graph()
G.add_node(1, **{q: q})
s = node_link_data(G)
output = json.dumps(s, ensure_ascii=False)
data = json.loads(output)
H = node_link_graph(data)
assert H.nodes[1][q] == q
def test_exception(self):
with pytest.raises(nx.NetworkXError):
G = nx.MultiDiGraph()
attrs = dict(name="node", source="node", target="node", key="node")
node_link_data(G, attrs)
def test_string_ids(self):
q = "qualité"
G = nx.DiGraph()
G.add_node("A")
G.add_node(q)
G.add_edge("A", q)
data = node_link_data(G)
assert data["links"][0]["source"] == "A"
assert data["links"][0]["target"] == q
H = node_link_graph(data)
assert nx.is_isomorphic(G, H)
def test_custom_attrs(self):
G = nx.path_graph(4)
G.add_node(1, color="red")
G.add_edge(1, 2, width=7)
G.graph[1] = "one"
G.graph["foo"] = "bar"
attrs = dict(
source="c_source",
target="c_target",
name="c_id",
key="c_key",
link="c_links",
)
H = node_link_graph(
node_link_data(G, attrs=attrs), multigraph=False, attrs=attrs
)
assert nx.is_isomorphic(G, H)
assert H.graph["foo"] == "bar"
assert H.nodes[1]["color"] == "red"
assert H[1][2]["width"] == 7

View file

@ -0,0 +1,35 @@
import json
import pytest
import networkx as nx
from networkx.readwrite.json_graph import tree_data, tree_graph
class TestTree:
def test_graph(self):
G = nx.DiGraph()
G.add_nodes_from([1, 2, 3], color="red")
G.add_edge(1, 2, foo=7)
G.add_edge(1, 3, foo=10)
G.add_edge(3, 4, foo=10)
H = tree_graph(tree_data(G, 1))
nx.is_isomorphic(G, H)
def test_graph_attributes(self):
G = nx.DiGraph()
G.add_nodes_from([1, 2, 3], color="red")
G.add_edge(1, 2, foo=7)
G.add_edge(1, 3, foo=10)
G.add_edge(3, 4, foo=10)
H = tree_graph(tree_data(G, 1))
assert H.nodes[1]["color"] == "red"
d = json.dumps(tree_data(G, 1))
H = tree_graph(json.loads(d))
assert H.nodes[1]["color"] == "red"
def test_exception(self):
with pytest.raises(nx.NetworkXError):
G = nx.MultiDiGraph()
G.add_node(0)
attrs = dict(id="node", children="node")
tree_data(G, 0, attrs)

View file

@ -0,0 +1,146 @@
from itertools import chain
import networkx as nx
__all__ = ["tree_data", "tree_graph"]
_attrs = dict(id="id", children="children")
def tree_data(G, root, attrs=_attrs):
"""Returns data in tree format that is suitable for JSON serialization
and use in Javascript documents.
Parameters
----------
G : NetworkX graph
G must be an oriented tree
root : node
The root of the tree
attrs : dict
A dictionary that contains two keys 'id' and 'children'. The
corresponding values provide the attribute names for storing
NetworkX-internal graph data. The values should be unique. Default
value: :samp:`dict(id='id', children='children')`.
If some user-defined graph data use these attribute names as data keys,
they may be silently dropped.
Returns
-------
data : dict
A dictionary with node-link formatted data.
Raises
------
NetworkXError
If values in attrs are not unique.
Examples
--------
>>> from networkx.readwrite import json_graph
>>> G = nx.DiGraph([(1, 2)])
>>> data = json_graph.tree_data(G, root=1)
To serialize with json
>>> import json
>>> s = json.dumps(data)
Notes
-----
Node attributes are stored in this format but keys
for attributes must be strings if you want to serialize with JSON.
Graph and edge attributes are not stored.
The default value of attrs will be changed in a future release of NetworkX.
See Also
--------
tree_graph, node_link_data, node_link_data
"""
if G.number_of_nodes() != G.number_of_edges() + 1:
raise TypeError("G is not a tree.")
if not G.is_directed():
raise TypeError("G is not directed.")
id_ = attrs["id"]
children = attrs["children"]
if id_ == children:
raise nx.NetworkXError("Attribute names are not unique.")
def add_children(n, G):
nbrs = G[n]
if len(nbrs) == 0:
return []
children_ = []
for child in nbrs:
d = dict(chain(G.nodes[child].items(), [(id_, child)]))
c = add_children(child, G)
if c:
d[children] = c
children_.append(d)
return children_
data = dict(chain(G.nodes[root].items(), [(id_, root)]))
data[children] = add_children(root, G)
return data
def tree_graph(data, attrs=_attrs):
"""Returns graph from tree data format.
Parameters
----------
data : dict
Tree formatted graph data
Returns
-------
G : NetworkX DiGraph
attrs : dict
A dictionary that contains two keys 'id' and 'children'. The
corresponding values provide the attribute names for storing
NetworkX-internal graph data. The values should be unique. Default
value: :samp:`dict(id='id', children='children')`.
Examples
--------
>>> from networkx.readwrite import json_graph
>>> G = nx.DiGraph([(1, 2)])
>>> data = json_graph.tree_data(G, root=1)
>>> H = json_graph.tree_graph(data)
Notes
-----
The default value of attrs will be changed in a future release of NetworkX.
See Also
--------
tree_graph, node_link_data, adjacency_data
"""
graph = nx.DiGraph()
id_ = attrs["id"]
children = attrs["children"]
def add_children(parent, children_):
for data in children_:
child = data[id_]
graph.add_edge(parent, child)
grandchildren = data.get(children, [])
if grandchildren:
add_children(child, grandchildren)
nodedata = {
str(k): v for k, v in data.items() if k != id_ and k != children
}
graph.add_node(child, **nodedata)
root = data[id_]
children_ = data.get(children, [])
nodedata = {str(k): v for k, v in data.items() if k != id_ and k != children}
graph.add_node(root, **nodedata)
add_children(root, children_)
return graph