Ten en cuenta que en la informática cualquier problema puede tener diversas resoluciones, así que nosotros aquí te enseñaremos lo más óptimo y eficiente.
Solución:
Para un árbol como este, no es necesario usar una biblioteca: puede generar las instrucciones del lenguaje Graphviz DOT directamente. La única parte complicada es extraer los bordes del árbol de los datos JSON. Para hacer eso, primero convertimos el JSON string volver a Python dict
y luego analizar eso dict
recursivamente.
Si un nombre en el dictado del árbol no tiene hijos, es un simple stringde lo contrario, es un dict y necesitamos escanear los elementos en su "children"
lista. Cada par (padre, hijo) que encontramos se agrega a una lista global edges
.
Esta línea algo críptica:
name = next(iter(treedict.keys()))
obtiene un solo key desde treedict
. Esto nos da el nombre de la persona, ya que ese es el único key en treedict
. En Python 2 podríamos hacer
name = treedict.keys()[0]
pero el código anterior funciona tanto en Python 2 como en 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('')
salida estándar
"Harry":
"children": [
"Bill",
"Jane":
"children": [
"Diane":
"children": [
"Mary"
]
,
"Mark"
]
]
salida estándar
strict digraph tree
Harry -> Bill;
Harry -> Jane;
Jane -> Diane;
Diane -> Mary;
Jane -> Mark;
El código anterior se ejecuta en Python 2 y Python 3. Imprime los datos JSON en stderr para que podamos verificar que sea correcto. Luego imprime los datos de Graphviz en la salida estándar para que podamos capturarlos en un archivo o canalizarlos directamente a un programa de Graphviz. Por ejemplo, si el script se llama “tree_to_graph.py”, entonces puede hacer esto en la línea de comando para guardar el gráfico como un archivo PNG llamado “tree.png”:
python tree_to_graph.py | dot -Tpng -otree.png
Y aquí está la salida PNG:
Basado en la respuesta de PM 2Ring, creo un script que se puede usar a través de la línea de comando:
#!/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)
Si tienes alguna desconfianza y capacidad de avanzar nuestro ensayo puedes añadir un exégesis y con deseo lo estudiaremos.