Plotting a weighted tree graph

I am trying to plot a weighted graph using SimpleWeightedGraphs using GraphPlot.

g = SimpleWeightedGraph(5)
add_edge!(g, 1, 2, 0.5)
add_edge!(g, 1, 3, 2.0)
add_edge!(g, 1, 4, 3.5)
add_edge!(g, 1, 5, 5.0)
gplot(g)


But, I would like to have it (a) as a tree with vertex 1 as the root, and (b) the edge’s thickness are as per the weights assigned to them. How can I do it?

gplot(g, nodelabel=vertices(g), edgelinewidth=weight.(edges(g)))

3 Likes

Thank you! Could you please also address the problem that I would like it to have a tree structure, rather than a star?

I don’t think GraphPlot.jl can do tree layout. But Home · NetworkLayout.jl can. It is best integrated with GraphMakie.jl though.

Yes, I don’t see any tree layout or layered layout, that could be a good addition to GraphPlot

Thank you, I will check them.

If you want to explore even more possibilities that are possibly still experimental (:slight_smile:) , you could try Karnak.jl:

using Graphs
using SimpleWeightedGraphs
using Karnak

g = SimpleWeightedGraph(5)
add_edge!(g, 1, 2, 0.5)
add_edge!(g, 1, 3, 2.0)
add_edge!(g, 1, 4, 3.5)
add_edge!(g, 1, 5, 5.0)

@drawsvg begin
    background("black")
    sethue("gold")
    drawgraph(g,
        layout=vcat(
            Point(0, -200), # point 1 is at the top
            between.(Point(-200, 150), Point(200, 150), range(0, 1, length=4))
            ),
        vertexshapesizes = 20,
        edgestrokeweights =
            (n, s, d, f, t) -> 2get_weight(g, s, d)
        )
end 

This is merely passing some calculated positions for the vertices.

If you want to use the buchheim algorithm from NetworkLayout, you can do it like this;

g = SimpleWeightedDiGraph(5)    # must be a Directed Graph
add_edge!(g, 1, 2, 0.5)
add_edge!(g, 1, 3, 2.0)
add_edge!(g, 1, 4, 3.5)
add_edge!(g, 1, 5, 5.0)

@drawsvg begin
    background("black")
    sethue("cyan")
    drawgraph(g,
        layout=buchheim,
        edgestrokeweights =
            (n, s, d, f, t) -> get_weight(g, s, d)
        )
end

The NetworkLayout.Buchheim() algorithm lets you also specify the vertex spacing:

      ...
      layout=Buchheim(nodesize=[1.0, 4.0, 8.0, 16.0]),
      ...

3 Likes

Thank you so much!

1 Like

As other people mentioned, you can also use GraphMakie.jl

Bucheim breaks with your graph because it assumes double edges and a node cannot have multiple parent nodes.
If this is not a deal breaker you can work it around with SimpleWeightedDiGraph:

using GraphMakie

g = SimpleWeightedDiGraph(5)    
add_edge!(g, 1, 2, 0.5)    
add_edge!(g, 1, 3, 2.0)    
add_edge!(g, 1, 4, 3.5)    
add_edge!(g, 1, 5, 5.0)    
     
edws = [get_weight(g, e.src, e.dst) for e in edges(g)]    
            
graphplot(g, layout=NetworkLayout.Buchheim(), edge_width=edws, arrow_size=5 .+ 5 .* edws)                

If SimpleWeightedDiGraph is a deal breaker, you can of course get around that by being a little bit more verbose, i.e. making a directed graph representation just for plotting:

(maybe there is an easier way, but that came on top of my head)

using SparseArrays
                                
g = SimpleWeightedGraph(5) 
add_edge!(g, 1, 2, 0.5)    
add_edge!(g, 1, 3, 2.0)    
add_edge!(g, 1, 4, 3.5)    
add_edge!(g, 1, 5, 5.0)
                                                          
# directed graph equivalent just for plotting
sdg = let                     
    adg = adjacency_matrix(g)
    [adg[i,j] = 0.0  for i in vertices(g) for j in vertices(g) if i > j];
    dropzeros!(adg);        
    SimpleDiGraph(adg)
end                       

edws = [get_weight(g, e.src, e.dst) for e in edges(sdg)] 

# same plotting as before with the directed representation
graphplot(sdg, layout=NetworkLayout.Buchheim(), edge_width=edws, arrow_size=5 .+ 5 .* edws)

Hi, thank you! I was wondering if there is any way to assign edge styles, like dash, dashdot etc.? I looked up the documentation, but not sure I see anything that helps! Also how to save the graph as a pdf?

You can save the graph as every figure in the Makie ecosystem.
For example when using the CairoMakie backend

using CairoMakie
f,a,p = graphplot(sdg, layout=NetworkLayout.Buchheim(), edge_width=edws, arrow_size=5 .+ 5 .* edws)
save("mygraphplot.pdf", f)

GraphMakie uses the linesegments function in order to plot edges. By using the edge_attr attribute, you can pass whatever namedtuple that will be forwarded to the linesegments. For example if you want dashed edges you can do:

f,a,p = graphplot(sdg, layout=NetworkLayout.Buchheim(), edge_width=edws, arrow_size=5 .+ 5     .* edws, edge_attr=(linestyle=:dash,))    

Be careful of the single-namedtuple ending with a comma.

Thank you! This helps. One final thing, is there any attribute by which I can control the edge length? I mean the distance between the nodes, the default seems too long.

If you want a different layout you need to modify the layout function or pass in different parameters as defined e.g. in Buchheim and demonstrated by cormullion.

But I think what you really want is a different aspect of your plotted data.
No matter how closely you will plot the children with the nodesize from Buchheim, Makie will always zoom, as a result always looking the same. But you can change the aspect ratio, e.g.:

f = Figure()
#  width of three times the height.
ax = Axis(f[1,1], aspect=AxisAspect(3))
# same plot command
graphplot!(ax, sdg, layout=NetworkLayout.Buchheim(), edge_width=edws, arrow_size=5 .+ 5 .* edws, edge_attr=(linestyle=:dash,))  

Screenshot from 2022-06-15 16-24-44

1 Like

Thank you! Also, specifying the width of the axis makes it look a little nicer!