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.
# 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))
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.
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 (
destiny) and the node labels (
names) to visualize the graphs.
destinylists, with optional
weightsfor 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:
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:
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
- 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 )
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.