jgraph in the IPython notebook

I wrote jgraph to visualize graphs in 3D purely out of curiosity. I couldn't find any 3D force-directed graph libraries when I wrote it, so this happened.

It can be used with the notebook to interactively view graph data. Some graphs just look nicer in 3D. To use, send it Python objects. A simple data structure is the adjacency list.

In [1]:
import jgraph
jgraph.draw([(1, 2), (2, 3), (3, 4), (4, 1), (4, 5), (5, 2)])

That works, but the graph is boring. More complex graph visualizations use node size and color to show multiple dimensions. To do this with jgraph, you can use a more expressive Python data structure. Each node takes three optional parameters: color, size, and location.

In [2]:
graph = {
    'nodes': {
        'ross': {'color': 0xffaaaa, 'size': 2.0},
        'joey': {'size': 0.5},
        'chandler': {'color': 0x2222ff, 'size': 1.25},
        'phoebe': {'color': 0x22ff22},
        'rachel': {},
        'monica': {},
        'jack': {},
        'judy': {},
    },
    'edges': [
        {'source': 'chandler', 'target': 'ross'},
        {'source': 'monica', 'target': 'ross'},
        {'source': 'ross', 'target': 'rachel', 'size': 3, 'color': 0xffaaaa},
        {'source': 'ross', 'target': 'joey'},
        {'source': 'ross', 'target': 'phoebe'},
        {'source': 'ross', 'target': 'judy'},
        {'source': 'monica', 'target': 'rachel'},
        {'source': 'rachel', 'target': 'jack'},
        {'source': 'chandler', 'target': 'phoebe'}
    ]
}

jgraph.draw(graph)

The draw method can take python objects, strings, or files.

In [3]:
jgraph.draw('miserables.json', directed=False)

Because the input is a pure Python data structure, you can programmatically create and edit graphs without learning a new API. This lets you mess around with graph data structures and algorithms without thinking about library-specific semantics.

In [4]:
number_of_steps = 5

b_tree = [(1, 2), (1, 3)]
index = 3
for _ in range(number_of_steps):
    leaves = [edge[1] for edge in b_tree if all(edge[1] != other_edge[0] for other_edge in b_tree)]
    for leaf in leaves:
        for __ in range(2):
            index += 1
            b_tree.append((leaf, index))
jgraph.draw(b_tree, shader="lambert", default_node_color=0x383294, z=200, size=(800, 600))

You may have noticed some extra arguments used in that last example. There are options to change default colors and sizing, renderers, and more. For a full breakdown, read the docs.

In [5]:
help(jgraph.draw)
Help on function draw in module jgraph.notebook:

draw(data, size=(600, 400), node_size=2.0, edge_size=0.25, default_node_color=6013150, default_edge_color=11184810, z=100, shader='basic', optimize=True, directed=True)
    Draws an interactive 3D visualization of the inputted graph.
    
    Args:
        data: Either an adjacency list of tuples (ie. [(1,2),...]) or object
        size: (Optional) Dimensions of visualization, in pixels
        node_size: (Optional) Defaults to 2.0
        edge_size: (Optional) Defaults to 0.25
        default_node_color: (Optional) If loading data without specified
            'color' properties, this will be used. Default is 0x5bc0de
        default_edge_color: (Optional) If loading data without specified
            'color' properties, this will be used. Default is 0xaaaaaa
        z: (Optional) Starting z position of the camera. Default is 100.
        shader: (Optional) Specifies shading algorithm to use. Can be 'toon',
            'basic', 'phong', or 'lambert'. Default is 'basic'.
        optimize: (Optional) Runs a force-directed layout algorithm on the
            graph. Default True.
        directed: (Optional) Includes arrows on edges to indicate direction.
            Default True.
    
    Inputting an adjacency list into `data` results in a 'default' graph type.
    For more customization, use the more expressive object format.

You may also want more control over generating the force-directed graph from an adjacency list. For that use jgraph.generate.

In [6]:
help(jgraph.generate)
Help on function generate in module jgraph.notebook:

generate(data, iterations=1000, force_strength=5.0, dampening=0.01, max_velocity=2.0, max_distance=50, is_3d=True)
    Runs a force-directed algorithm on a graph, returning a data structure.
    
    Args:
        data: An adjacency list of tuples (ie. [(1,2),...])
        iterations: (Optional) Number of FDL iterations to run in coordinate
            generation
        force_strength: (Optional) Strength of Coulomb and Hooke forces
            (edit this to scale the distance between nodes)
        dampening: (Optional) Multiplier to reduce force applied to nodes
        max_velocity: (Optional) Maximum distance a node can move in one step
        max_distance: (Optional) The maximum inter-node distance considered
        is_3d: (Optional) Generates three-dimensional coordinates
    
    Outputs a json-serializable Python object. To visualize, pass the output to
    `jgraph.draw(...)`.