from itertools import count
import networkx as nx
__all__ = ["node_link_data", "node_link_graph"]
def _to_tuple(x):
"""将列表转换为元组,包括嵌套列表。
所有其他非列表输入保持不变。此函数旨在用于将可能嵌套的列表从JSON文件转换为有效的节点。
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_data(
G,
*,
source="source",
target="target",
name="id",
key="key",
link="links",
nodes="nodes",
):
"""返回适合JSON序列化和在JavaScript文档中使用的节点链接格式数据。
Parameters
----------
G : NetworkX图
source : 字符串
用于存储NetworkX内部图数据的'source'属性名称。
target : 字符串
用于存储NetworkX内部图数据的'target'属性名称。
name : 字符串
用于存储NetworkX内部图数据的'name'属性名称。
key : 字符串
用于存储NetworkX内部图数据的'key'属性名称。
link : 字符串
用于存储NetworkX内部图数据的'link'属性名称。
nodes : 字符串
用于存储NetworkX内部图数据的'nodes'属性名称。
Returns
-------
data : 字典
包含节点链接格式数据的字典。
Raises
------
NetworkXError
如果'source'、'target'和'key'的值不唯一。
Examples
--------
>>> from pprint import pprint
>>> G = nx.Graph([("A", "B")])
>>> data1 = nx.node_link_data(G)
>>> pprint(data1)
{'directed': False,
'graph': {},
'links': [{'source': 'A', 'target': 'B'}],
'multigraph': False,
'nodes': [{'id': 'A'}, {'id': 'B'}]}
使用JSON进行序列化
>>> import json
>>> s1 = json.dumps(data1)
>>> s1
'{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": "A"}, {"id": "B"}], "links": [{"source": "A", "target": "B"}]}'
图也可以通过传递 `node_link_data` 作为编码器函数进行序列化。这两种方法是等效的。
>>> s1 = json.dumps(G, default=nx.node_link_data)
>>> s1
'{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": "A"}, {"id": "B"}], "links": [{"source": "A", "target": "B"}]}'
存储NetworkX内部图数据的属性名称可以指定为关键字选项。
>>> H = nx.gn_graph(2)
>>> data2 = nx.node_link_data(
... H, link="edges", source="from", target="to", nodes="vertices"
... )
>>> pprint(data2)
{'directed': True,
'edges': [{'from': 1, 'to': 0}],
'graph': {},
'multigraph': False,
'vertices': [{'id': 0}, {'id': 1}]}
Notes
-----
图、节点和链接属性以这种格式存储。注意,属性键将被转换为字符串以符合JSON规范。
属性'key'仅用于多重图。
要结合使用 `node_link_data` 和 `node_link_graph` ,属性的关键字名称必须匹配。
See Also
--------
node_link_graph, adjacency_data, tree_data
"""
multigraph = G.is_multigraph()
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
key = None if not multigraph else 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: [{**G.nodes[n], name: n} for n in G],
}
if multigraph:
data[link] = [
{**d, source: u, target: v, key: k}
for u, v, k, d in G.edges(keys=True, data=True)
]
else:
data[link] = [{**d, source: u, target: v} for u, v, d in G.edges(data=True)]
return data
[docs]
@nx._dispatchable(graphs=None, returns_graph=True)
def node_link_graph(
data,
directed=False,
multigraph=True,
*,
source="source",
target="target",
name="id",
key="key",
link="links",
nodes="nodes",
):
"""返回从节点-链接数据格式构建的图。
适用于从JSON反序列化。
Parameters
----------
data : dict
节点-链接格式的图数据
directed : bool
如果为True,且数据中未指定方向,则返回有向图。
multigraph : bool
如果为True,且数据中未指定多图,则返回多图。
source : string
提供用于存储NetworkX内部图数据的'source'属性名称的字符串。
target : string
提供用于存储NetworkX内部图数据的'target'属性名称的字符串。
name : string
提供用于存储NetworkX内部图数据的'name'属性名称的字符串。
key : string
提供用于存储NetworkX内部图数据的'key'属性名称的字符串。
link : string
提供用于存储NetworkX内部图数据的'link'属性名称的字符串。
nodes : string
提供用于存储NetworkX内部图数据的'nodes'属性名称的字符串。
Returns
-------
G : NetworkX graph
NetworkX图对象
Examples
--------
通过转换图来创建节点-链接格式的数据。
>>> from pprint import pprint
>>> G = nx.Graph([("A", "B")])
>>> data = nx.node_link_data(G)
>>> pprint(data)
{'directed': False,
'graph': {},
'links': [{'source': 'A', 'target': 'B'}],
'multigraph': False,
'nodes': [{'id': 'A'}, {'id': 'B'}]}
将节点-链接格式的数据还原为图。
>>> H = nx.node_link_graph(data)
>>> print(H.edges)
[('A', 'B')]
使用JSON序列化和反序列化图,
>>> import json
>>> d = json.dumps(nx.node_link_data(G))
>>> H = nx.node_link_graph(json.loads(d))
>>> print(G.edges, H.edges)
[('A', 'B')] [('A', 'B')]
Notes
-----
属性'key'仅用于多图。
要结合使用 `node_link_data` 和 `node_link_graph` ,
属性的关键字名称必须匹配。
See Also
--------
node_link_data, adjacency_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()
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
key = None if not multigraph else 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[link]:
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