jdotjdot
jdotjdot

Reputation: 17072

Python library for creating tree graphs out of nested Python objects (dicts)

Does anyone know of any Python libraries that allow you to simply and quickly feed it an object nested to arbitrary levels, like for example a dict tree along the lines of what you'd find in this gist, and it can spit out a workable tree graph file?

Simplicity is key, here, since I have to be able to work with people who are not technically minded.

What I mean by "graph tree" is something along the lines of the following, where I could feed it a nested dictionary of values and it would then create the tree structure:


(source: rubyforge.org)

Upvotes: 16

Views: 28671

Answers (5)

bruha
bruha

Reputation: 11

D3.js is a library for visualization mostly...

As far as I know, it does not provide a convenient tools for traversing graphs and mathematical operations with them. But they are in Python networkX package:

import networkx as nx

graph_data = {
    'id': 'root',
    'children': [
        {
            'id': 'A',
            'children': [
                {
                    'id': 'B', 'children': [{'id': 'B1'}, {'id': 'B2'}]
                },
                {
                    'id': 'C'
                }
            ]
        }
    ]
}

G = nx.readwrite.json_graph.tree_graph(graph_data)

print('EDGES: ', G.edges())
# EDGES: [('root', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B1'), ('B', 'B2')]
print('NODES: ', G.nodes())
# NODES: ['root', 'A', 'B', 'B1', 'B2', 'C']

This is an example of creating from structure an identical D3.js (with id and children fields).

Graph can also be created iteratively with recursion, as well as read and write with a variety of formats: https://networkx.github.io/documentation/stable/reference/readwrite/index.html

Upvotes: 1

Marc
Marc

Reputation: 734

I was looking for a similar problem: printing keys of a dict with nested dicts, where the key structure is very periodic. Therefore I wrote a recursive function that prints the keys of every dict and nested dicts, but for a single branch.

Hope the following code snippet helps other people:

from itertools import zip_longest

def dictPrintKeysTopBranch(dic):
    #track recursive depth
    depth=dictPrintKeysTopBranch.data.get('depth',-1)+1;
    dictPrintKeysTopBranch.data['depth']=depth;

    #accumalte keys from nested dicts
    if type(dic) is type(dict()):
        listKeys=sorted(list(dic.keys()));

        #save keys of current depth
        dictPrintKeysTopBranch.data['listKeysDepth{}'.format(depth)]=listKeys;

        #repeat for top branch
        dictPrintKeysTopBranch(dic[listKeys[0]]);

    #print accumalated list of keys
    else: 
        #pad lists 
        lists=[];
        maxlen=[];
        for d in range(depth):
            l=dictPrintKeysTopBranch.data['listKeysDepth{}'.format(d)];
            lists.append(l);

            lens = [len(s) for s in l];
            maxlen.append(max(lens)+1);

        i=-1;
        for zipped in zip_longest(*lists, fillvalue=' '):
            i=i+1;
            #print(x)
            row = '';
            j=-1;
            for z in zipped:
                j=j+1;
                if i==0:
                    row = row+ ((' {: <'+str(maxlen[j])+'} -->\\').format(z));
                else :
                    row = row+ ((' {: <'+str(maxlen[j])+'}    |').format(z));
            print(row.strip('\\|->'));

    dictPrintKeysTopBranch.data={};
dictPrintKeysTopBranch.data={};

Here an example:

mydict = { 'topLv':{'secLv':{'thirdLv':{'item1':42,'item2':'foo'}}}, 
           'topLvItem':[1,2,3], 
           'topLvOther':{'notPrinted':':('}
         }
dictPrintKeysTopBranch(mydict)

Output:

topLv       -->\ secLv  -->\ thirdLv  -->\ item1  
topLvItem      |           |             | item2     
topLvOther     |           |             |         

Upvotes: 0

doug
doug

Reputation: 70068

so the library i recommend and use for my code snippet in this answer is not a python library, but it is a python-friendly library, by which i mean that code using this library can be inserted into a python module for processing data and this foreign code will connect to extant python code on both ends, ie, both input and output, and i suspect, although of course i don't know, that's all that's really meant by the "python library" criterion. So if you are writing a web app, this code would be client-side. In other words, this library is not python, but it works with python.

  1. its input is (nearly) raw python dicts, more specifically, json.load(a_python_dict) returns a json array or object, a format which this javascript library can of course recognize; and

  2. the output format is either HTML or SVG, not objects in some language-specific format

You can use d3.js. It has a class specifically for rendering trees:

var tree = d3.layout.tree().size([h, w]);

There is also a couple of examples of trees (working code) in the example folder in the d3 source, which you can clone/download form the link i provided above.

Because d3 is a javascript library, its native data format is JSON.

The basic structure is a nested dictionary, each dictionary representing a single node with two values, the node's name and its children (stored in an array), keyed to names and children, respectively:

{"name": "a_root_node", "children": ["B", "C"]}

and of course it's simple to convert between python dictionaries and JSON:

>>> d = {"name": 'A', "children": ['B', 'C']}
>>> import json as JSON
>>> dj = JSON.dumps(d)
>>> dj
    '{"name": "A", "children": ["B", "C"]}'

here's a python dictionary representation of a larger tree (a dozen or so nodes) which i converted to json as above, and then rendered in d3 as the tree shown in the image below:

tree = {'name': 'root', 'children': [{'name': 'node 2', 'children': 
       [{'name': 'node 4', 'children': [{'name': 'node 10', 'size': 7500}, 
       {'name': 'node 11', 'size': 12000}]}, {'name': 'node 5', 'children': 
       [{'name': 'node 12', 'children': [{'name': 'node 16', 'size': 10000}, 
       {'name': 'node 17', 'size': 12000}]}, {'name': 'node 13', 'size': 5000}]}]}, 
       {'name': 'node 3', 'children': [{'name': 'node 6', 'children': 
       [{'name': 'node 14', 'size': 8000}, {'name': 'node 15', 'size': 9000}]}, 
       {'name': 'node 7', 'children': [{'name': 'node 8', 'size': 10000}, 
       {'name': 'node 9', 'size': 12000}]}]}]}

tree represented as python dictionary rendered in d3:

note: d3 renders in the browser; the image above is just a screen shot of my browser window.

Upvotes: 4

user2255012
user2255012

Reputation: 1

Depending on what you need, but if you only need it to view the content easily, you can use the online tool for that - "Python dict formatter and viewer" can render a dict as a tree.

Upvotes: -2

neirbowj
neirbowj

Reputation: 645

I'm not sure that this is quite what you have in mind, but it's the first thing that comes to mind.

blockdiag is mainly intended to be used as a standalone file processor similar to Graphviz (for which there exists a Python interface). It accepts a text file as input with a drop-dead simple syntax, and produces images as output.

You should be able to write a simple shim to output your recursive dict structure formatted for input to the standalone blockdiag script, or import the necessary innards of the blockdiag package and drive the output directly.

If this sounds promising, I'll see if I can whip up some example code.

EDIT Example code:

def print_blockdiag(tree, parent=None):
    if not parent: print('blockdiag { orientation = portrait')
    for key in tree:
        if parent: print('   {} -> {};'.format(parent, key))
        print_blockdiag(tree[key], key)
    if not parent: print('}')

This will output a file that blockdiag can read.

Upvotes: 5

Related Questions