2016-10-18 2 views
2

Ich verwende treelib, um Bäume zu generieren, jetzt brauche ich einfach zu lesende Version von Bäumen, also möchte ich sie in Bilder konvertieren. Zum Beispiel: enter image description hereWie konvertiert man JSON-Daten in ein Baumbild?

Die Probe JSON-Daten für den folgenden Baum:

enter image description here

Mit Daten:

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

Ohne Daten:

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

Gibt es sowieso zu dir se graphviz oder d3.js oder eine andere Python-Bibliothek zum Erstellen von Baum mit diesen JSON-Daten?

+0

Haben Sie plotly angeschaut https://plot.ly/python/tree-plots/ – corn3lius

+0

@ corn3lius, Nein, habe es noch nicht gesehen, können Sie mir dabei helfen? Ich bin neu in JSON! – Grimlock

+0

BTW, die 'with_data = False'-Version von JSON stimmt mit dem Baum in Ihrem Bild überein, aber die' with_data = True'-Version nicht. –

Antwort

4

Für einen Baum wie diesen ist es nicht notwendig, eine Bibliothek zu verwenden: Sie können die Sprachanweisungen von Graphviz DOT direkt generieren. Der einzige knifflige Teil ist das Extrahieren der Baumkanten aus den JSON-Daten. Um dies zu tun, konvertieren wir zuerst die JSON-Zeichenfolge zurück in eine Python dict, und dann rekursiv diese dict.

Wenn ein Name in der Struktur dict keine Kinder hat, ist es eine einfache Zeichenfolge, ansonsten ist es ein Diktat und wir müssen die Elemente in seiner "children" Liste scannen. Jedes (Eltern-, Kind-) Paar, das wir finden, wird an eine globale Liste angehängt edges.

Diese etwas kryptische Zeile:

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

bekommt einen Schlüssel aus . Dies gibt uns den Namen der Person, da dies der einzige Schlüssel in ist. In Python 2 konnten wir

name = treedict.keys()[0] 

tun, aber der vorherige Code funktioniert in beide Python 2 und 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 Ausgabe

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

stdout Ausgabe

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

Der obige Code läuft auf Python 2 & Python 3. Es druckt die JSON-Daten auf stderr, damit wir überprüfen können, ob es korrekt ist. Anschließend werden die Graphviz-Daten in stdout ausgedruckt, sodass wir sie in einer Datei erfassen oder direkt an ein Graphviz-Programm leiten können. Wenn das Skript beispielsweise den Namen "tree_to_graph.py" hat, können Sie dies in der Befehlszeile tun, um das Diagramm als PNG-Datei mit dem Namen "tree" zu speichern.png ":

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

Und hier ist das PNG-Ausgang:

Tree made by Graphviz

+0

Dies ist genau das, was ich brauche, aber ich bekomme immer Fehler: 'Warnung: : Syntaxfehler in Zeile 1 in der Nähe '. Irgendeine Idee, was die Ursache für diesen Fehler ist? – Grimlock

+0

Vielleicht liegt es daran, dass mein Baum aus ganzen Zahlen anstatt aus Strings besteht. Wann immer ich diesen Befehl in der Konsole ausfühle, bekomme ich einen Fehler: 'Warnung: : Syntaxfehler in Zeile 1 in der Nähe von 114', wobei 114 der Wurzelknoten ist. – Grimlock

+0

Getestet alles von Ihrem Code, es funktioniert. Das Problem ist, ich hole Daten als 's = tree.to_json (with_data = False)', was den oben erwähnten Fehler verursacht, irgendeine Idee, wie kann ich es lösen? @ PM2Ring – Grimlock

1

Basierend auf der Antwort von PM 2Ring erstelle ich ein Skript, das über die Kommandozeile verwendet werden können:

#!/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) 
Verwandte Themen