我正在使用treelib生成树,现在我需要易于阅读的树版本,因此我想将它们转换为图像.例如:

以下树的示例JSON数据:
有了数据:
>>> print(tree.to_json(with_data=True))
{"Harry": {"data": null, "children": [{"Bill": {"data": null}}, {"Jane": {"data": null, "children": [{"Diane": {"data": null}}, {"Mark": {"data": null}}]}}, {"Mary": {"data": null}}]}}
Run Code Online (Sandbox Code Playgroud)
没有数据:
>>> print(tree.to_json(with_data=False))
{"Harry": {"children": ["Bill", {"Jane": {"children": [{"Diane": {"children": ["Mary"]}}, "Mark"]}}]}}
Run Code Online (Sandbox Code Playgroud)
对于这样的树,不需要使用库:您可以直接生成Graphviz DOT语言语句.唯一棘手的部分是从JSON数据中提取树边.为此,我们首先将JSON字符串转换回Python dict,然后dict递归解析.
如果树字典中的名字没有子元素,则它是一个简单的字符串,否则,它是一个字典,我们需要扫描其"children"列表中的项目.我们找到的每个(父,子)对都会附加到全局列表中edges.
这有些神秘的线条:
name = next(iter(treedict.keys()))
Run Code Online (Sandbox Code Playgroud)
从中获取一个密钥treedict.这给了我们这个人的名字,因为这是唯一的关键treedict.在Python 2中我们可以做到
name = treedict.keys()[0]
Run Code Online (Sandbox Code Playgroud)
但是之前的代码适用于Python 2和Python 3.
from __future__ import print_function
import json
import sys
# Tree in JSON format
s = '{"Harry": {"children": ["Bill", {"Jane": {"children": [{"Diane": {"children": ["Mary"]}}, "Mark"]}}]}}'
# Convert JSON tree to a Python dict
data = json.loads(s)
# Convert back to JSON & print to stderr so we can verify that the tree is correct.
print(json.dumps(data, indent=4), file=sys.stderr)
# Extract tree edges from the dict
edges = []
def get_edges(treedict, parent=None):
name = next(iter(treedict.keys()))
if parent is not None:
edges.append((parent, name))
for item in treedict[name]["children"]:
if isinstance(item, dict):
get_edges(item, parent=name)
else:
edges.append((name, item))
get_edges(data)
# Dump edge list in Graphviz DOT format
print('strict digraph tree {')
for row in edges:
print(' {0} -> {1};'.format(*row))
print('}')
Run Code Online (Sandbox Code Playgroud)
stderr输出
{
"Harry": {
"children": [
"Bill",
{
"Jane": {
"children": [
{
"Diane": {
"children": [
"Mary"
]
}
},
"Mark"
]
}
}
]
}
}
Run Code Online (Sandbox Code Playgroud)
标准输出
strict digraph tree {
Harry -> Bill;
Harry -> Jane;
Jane -> Diane;
Diane -> Mary;
Jane -> Mark;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码在Python 2和Python 3上运行.它将JSON数据打印到stderr,以便我们可以验证它是否正确.然后它将Graphviz数据打印到stdout,以便我们可以将其捕获到文件或直接将其传递给Graphviz程序.例如,如果脚本名称为"tree_to_graph.py",则可以在命令行中执行此操作以将图形另存为名为"tree.png"的PNG文件:
python tree_to_graph.py | dot -Tpng -otree.png
Run Code Online (Sandbox Code Playgroud)
这是PNG输出:

根据 PM 2Ring 的答案,我创建了一个可以通过命令行使用的脚本:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Convert a JSON to a graph."""
from __future__ import print_function
import json
import sys
def tree2graph(data, verbose=True):
"""
Convert a JSON to a graph.
Run `dot -Tpng -otree.png`
Parameters
----------
json_filepath : str
Path to a JSON file
out_dot_path : str
Path where the output dot file will be stored
Examples
--------
>>> s = {"Harry": [ "Bill", \
{"Jane": [{"Diane": ["Mary", "Mark"]}]}]}
>>> tree2graph(s)
[('Harry', 'Bill'), ('Harry', 'Jane'), ('Jane', 'Diane'), ('Diane', 'Mary'), ('Diane', 'Mark')]
"""
# Extract tree edges from the dict
edges = []
def get_edges(treedict, parent=None):
name = next(iter(treedict.keys()))
if parent is not None:
edges.append((parent, name))
for item in treedict[name]:
if isinstance(item, dict):
get_edges(item, parent=name)
elif isinstance(item, list):
for el in item:
if isinstance(item, dict):
edges.append((parent, item.keys()[0]))
get_edges(item[item.keys()[0]])
else:
edges.append((parent, el))
else:
edges.append((name, item))
get_edges(data)
return edges
def main(json_filepath, out_dot_path, lr=False, verbose=True):
"""IO."""
# Read JSON
with open(json_filepath) as data_file:
data = json.load(data_file)
if verbose:
# Convert back to JSON & print to stderr so we can verfiy that the tree
# is correct.
print(json.dumps(data, indent=4), file=sys.stderr)
# Get edges
edges = tree2graph(data, verbose)
# Dump edge list in Graphviz DOT format
with open(out_dot_path, 'w') as f:
f.write('strict digraph tree {\n')
if lr:
f.write('rankdir="LR";\n')
for row in edges:
f.write(' "{0}" -> "{1}";\n'.format(*row))
f.write('}\n')
def get_parser():
"""Get parser object for tree2graph.py."""
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
parser = ArgumentParser(description=__doc__,
formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("-i", "--input",
dest="json_filepath",
help="JSON FILE to read",
metavar="FILE",
required=True)
parser.add_argument("-o", "--output",
dest="out_dot_path",
help="DOT FILE to write",
metavar="FILE",
required=True)
return parser
if __name__ == "__main__":
import doctest
doctest.testmod()
args = get_parser().parse_args()
main(args.json_filepath, args.out_dot_path, verbose=False)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12211 次 |
| 最近记录: |