2016-10-18 3 views
2

Я использую treelib для генерации деревьев, теперь мне нужна простая для чтения версия деревьев, поэтому я хочу преобразовать их в изображения. Например: enter image description hereКак преобразовать данные JSON в изображение дерева?

В пример JSON данных, для следующего дерева:

enter image description here

С данными:

>>> 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}}]}} 

Без данных:

>>> print(tree.to_json(with_data=False)) 
{"Harry": {"children": ["Bill", {"Jane": {"children": [{"Diane": {"children": ["Mary"]}}, "Mark"]}}]}} 

Есть в любом случае se graphviz или d3.js или какую-либо другую библиотеку python для генерации дерева с использованием данных JSON?

+0

вы посмотрели на сюжет https://plot.ly/python/tree-plots/ – corn3lius

+0

@ corn3lius, Нет, не видели этого раньше, можете ли вы мне помочь? Я новичок в JSON! – Grimlock

+0

BTW, версия JSON 'with_data = False' соответствует дереву вашего изображения, но версия' with_data = True' не имеет. –

ответ

4

Для такого дерева нет необходимости использовать библиотеку: вы можете напрямую создавать операторы языка Graphviz DOT. Единственная сложная часть - извлечение ребер дерева из данных JSON. Для этого мы сначала преобразуем строку JSON обратно в Python dict, а затем проанализируем, что dict рекурсивно.

Если имя в дереве dict не имеет детей, это простая строка, в противном случае это dict, и нам нужно сканировать элементы в своем списке "children". Каждая найденная (родительская, дочерняя) пара добавляется в глобальный список edges.

Это зашифрованное линия:

name = next(iter(treedict.keys())) 

получает один ключ от . Это дает нам имя человека, поскольку это единственный ключ в . В Python 2 мы могли бы сделать

name = treedict.keys()[0] 

но предыдущий код работает как 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 verfiy 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('}') 

выход STDERR

{ 
    "Harry": { 
     "children": [ 
      "Bill", 
      { 
       "Jane": { 
        "children": [ 
         { 
          "Diane": { 
           "children": [ 
            "Mary" 
           ] 
          } 
         }, 
         "Mark" 
        ] 
       } 
      } 
     ] 
    } 
} 

вывода STDOUT

strict digraph tree { 
    Harry -> Bill; 
    Harry -> Jane; 
    Jane -> Diane; 
    Diane -> Mary; 
    Jane -> Mark; 
} 

Код выше работает на Python 2 & Python 3. Он печатает данные JSON в stderr, поэтому мы можем проверить, что это правильно. Затем он печатает данные Graphviz в stdout, чтобы мы могли записать его в файл или напрямую подключить к программе Graphviz. Например, если сценарий имеет имя «tree_to_graph.py», вы можете сделать это в командной строке, чтобы сохранить график в виде PNG-файла с именем «tree».PNG ":

python tree_to_graph.py | dot -Tpng -otree.png 

А вот выход PNG:

Tree made by Graphviz

+0

Это именно то, что мне нужно, но я продолжаю получать ошибку: 'Предупреждение: : синтаксическая ошибка в строке 1 рядом'. Любая идея, в чем причина этой ошибки? – Grimlock

+0

Возможно, это потому, что мое дерево состоит из целых чисел вместо строк. Поэтому всякий раз, когда я выполняю эту команду в консоли, я получаю ошибку: «Предупреждение: : синтаксическая ошибка в строке 1 около 114', где 114 является корневым узлом. – Grimlock

+0

Протестировано все от вашего кода, оно работает. Проблема в том, что я получаю данные как 's = tree.to_json (with_data = False)', что вызывает вышеупомянутую ошибку, любую идею, как я могу ее решить? @ PM2Ring – Grimlock

1

На основании ответа 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)