Visualizing Graphs in Julia using Plots and PlotRecipes

By: Tom Breloff

Re-posted from: http://www.breloff.com/Graphs/

In this short post, I hope to introduce you to basic visualization of graphs (nodes connected by edges) when using Plots in Julia. The intention is that Visualizing a graph is as simple as inputting the connectivity structure, and optionally setting a ton of attributes that define the layout, labeling, colors, and more. Nodes are markers, and edges are lines. With this understanding, we can apply common Plots attributes as we see fit.


Setup

First, you’ll want to get a working setup of Plots and PlotRecipes:

# for pkg in ("Plots","PlotRecipes")
#     Pkg.add(pkg)
#     Pkg.checkout(pkg)
# end
using PlotRecipes

# we'll use the PyPlot backend, and set a couple defaults
pyplot(alpha=0.5, size=(800,400))

Type Trees

For our example, we’re going to build a graph of the type hierarchy for a Julia abstract type. We will look at the Integer abstraction, and view it at the center of all supertypes and subtypes of Integer. You can view this demo as a Jupyter notebook.

First, we’ll create a vector of our chosen type (Integer) and all its supertypes (Real, Number, and Any):

T = Integer
sups = [T]
sup = T
while sup != Any
    sup = supertype(sup)
    unshift!(sups,sup)
end

Next we will build the graph connectivity and node labels. source and destiny are lists of integers representing the indices in the edge connections of “source” to “destination”.

n = length(sups)
nodes, source, destiny = copy(sups), collect(1:n-1), collect(2:n)
function add_subs!(T, supidx)
    for sub in subtypes(T)
        push!(nodes, sub)
        subidx = length(nodes)
        push!(source, supidx)
        push!(destiny, subidx)
        add_subs!(sub, subidx)
    end
end
add_subs!(T, n)
names = map(string, nodes)

Now we will use the connectivity (source and destiny) and the node labels (names) to visualize the graphs.

graphplot

The graphplot method is a user recipe defined in PlotRecipes. It accepts many different inputs to describe the graph structure:

  • source and destiny lists, with optional weights for weighted edges
  • adjlist: a vector of int-vectors, describing the connectivity from each node
  • adjmat: an adjacency matrix
  • LightGraphs.Graph: if LightGraphs is installed

We will use the source/destiny/weights method in this post. Lets see what it looks like without overriding default settings:

graphplot(source, destiny)


Cool. Now lets add names to the nodes. Notice that the nodes take on a hexagonal shape and expand to fix the text. There are a few additional attributes you can try: fontsize, nodeshape, and nodesize.

graphplot(source, destiny, names=names)


We can also change the layout of the nodes by:

  • using one of the built-in algorithms (spectral, stress, or tree)
  • extending with NetworkLayout
  • passing an arbitrary layout function to the func keyword
  • overriding the x/y/z coordinates yourself (graphplot(..., x=x, y=y)).

As there’s a clear hierarchy to our graph, lets use the tree method:

graphplot(source, destiny, names=names, method=:tree)


The tree layout allows the additional setting of the root of the tree. Lets make the graph flow from left to right:

graphplot(source, destiny, names=names, method=:tree, root=:left)


All too easy. Finally, lets give it some color. Remember that we’re building a generic Plots visualization, where nodes are markers and edges are line segments. For more info on Plots, please read through the documentation.

weights = linspace(1,2,length(source))
graphplot(source, destiny, weights,
    names = names, method = :tree,
    l = (2, cgrad()),  # apply the default color gradient to the line (line_z values taken from edge weights)
    m = [node==T ? :orange : :steelblue for node in nodes]     # node colors
)

Summary

Visualizing graphs with PlotRecipes is pretty simple, and it’s easy to customize to your hearts content, thanks to the flexibility of Plots. In a future post, I’ll use this functionality to view and visualize neural networks using my (work in progress) efforts within JuliaML and other projects.