Saltar al contenido

¿Cómo convertir datos JSON en una imagen de árbol?

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 dicty 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:

Árbol hecho por Graphviz

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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *