Search code examples
rgraphvizdiagrammerpatchwork

Graphs with mixed splines OR merge layers with different splines


I want to create a graph with both splines=line and splines=splines, similar to the image below.

However, to achieve this, I had to create two separate graphs via the R package DiagrammeR and combine them using the figpatch and patchwork packages.

Is this possible just with graphviz? I'm not 100% certain, but I don't think it is… But just want to cover all bases to make sure.

Can someone please confirm / deny my hunch. If the latter, some examples or resources on how to do so would be greatly appreciated.

Please let me know if you need more info about my questions.

enter image description here

Graphs

Below is the code used to generate the graphs.

Graph 1: The one on the left.

digraph dot {

    // splines = curved;
    node[fontname = arial, width=1.5, height=1.5, shape=circle, fontsize=50, style = filled, penwidth = 3];
    edge[fontname = arial, penwidth = 3.6, fontsize = 50]

    RItb0  [pos = "000,0", style = invis, fillcolor = palevioletred];
    RIpb0  [pos = "100,0", style = invis, fillcolor = darkseagreen3];
    RIsi0  [pos = "200,0", style = invis, fillcolor = white];
    RIbhs0 [pos = "300,0", style = invis, fillcolor = mediumorchid3];
    RIdep0 [pos = "400,0", style = invis, fillcolor = cadetblue3];
    RItb   [pos = "000,100", width = 3, height = 3, fillcolor = palevioletred];
    RIpb   [pos = "100,100", width = 3, height = 3, fillcolor = darkseagreen3];
    RIsi   [pos = "200,100", width = 3, height = 3, fillcolor = white];
    RIbhs  [pos = "300,100", width = 3, height = 3, fillcolor = mediumorchid3];
    RIdep  [pos = "400,100", width = 3, height = 3, fillcolor = cadetblue3];
    tb1    [pos = "000,200", style = invis, fillcolor = palevioletred];
    pb1    [pos = "100,200", style = invis, fillcolor = darkseagreen3];
    si1    [pos = "200,200", style = invis, fillcolor = white];
    bhs1   [pos = "300,200", style = invis, fillcolor = mediumorchid3];
    dep1   [pos = "400,200", style = invis, fillcolor = cadetblue3];

    {   edge [weight = 10, minlen = 4.0; style = invis, penwidth = 3];
        // Random Intercepts
        RItb0  -> RIpb0  ; // [minlen = 4.0];
        RIpb0  -> RIsi0  ; // [minlen = 4.0];
        RIsi0  -> RIbhs0 ; // [minlen = 4.0];
        RIbhs0 -> RIdep0 ; // [minlen = 4.0];
        RItb   -> RIpb   ; // [minlen = 4.0];
        RIpb   -> RIsi   ; // [minlen = 4.0];
        RIsi   -> RIbhs  ; // [minlen = 4.0];
        RIbhs  -> RIdep  ; // [minlen = 4.0];
        tb1    -> pb1  ;
        pb1    -> si1  ;
        si1    -> bhs1 ;
        bhs1   -> dep1 ;
    }

    // autogregrssive paths
    {   rank = same; RItb0  -> RItb  [style = invis, labeldistance = 4.3, minlen = 6, color = palevioletred, style = invis]; }
    {   rank = same; RItb   -> tb1   [style = invis, labeldistance = 4.3, minlen = 6, color = palevioletred, style = invis]; }
    {   rank = same; RIpb0  -> RIpb  [style = invis, labeldistance = 4.3, minlen = 6, color = darkseagreen3, headlabel = ".28"]; }
    {   rank = same; RIpb   -> pb1   [style = invis, labeldistance = 4.3, minlen = 6, color = darkseagreen3, headlabel = ".28"]; }
    {   rank = same; RIsi0  -> RIsi  [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, headlabel = ".35"]; }
    {   rank = same; RIsi   -> si1   [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, headlabel = ".35"]; }
    {   rank = same; RIbhs0 -> RIbhs [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, color = mediumorchid3, headlabel = ".43"];}
    {   rank = same; RIbhs  -> bhs1  [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, color = mediumorchid3, headlabel = ".43"];}
    {   rank = same; RIdep0 -> RIdep [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, color = cadetblue3; style = invis]; }
    {   rank = same; RIdep  -> dep1  [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, color = cadetblue3; style = invis]; }

    {
    // cross-lagged paths
        edge[style = dashed, labeldistance = 4.3, labelfloat = false, constraint = true, minlen = 5]
        RIpb  -> RItb  [penwidth = 5, dir = both, label = ".47"];
        RIpb  -> RIbhs [penwidth = 5, dir = both, label = ".55"];
        RIpb  -> RIdep [penwidth = 5, dir = both, label = ".67"];
        RItb  -> RIbhs [penwidth = 5, dir = both, label = ".60"];
        RItb  -> RIdep [penwidth = 5, dir = both, label = ".58"];
        RItb  -> RIsi  [penwidth = 9.5, dir = both, style = dashed, label = ".27"];
        RIbhs -> RIsi  [penwidth = 9.5, dir = both, style = dashed, label = ".31"];
        RIdep -> RIsi  [penwidth = 9.5, dir = both, style = dashed, label = ".37"];
        RIbhs -> RIdep [penwidth = 5, dir = both, label = ".62"];
    }
}

Graph 2: The one on the right.

digraph dot {
    splines = line;
    node[fontname = arial, width=1.5, height=1.5, shape=circle, fontsize=36, style = filled, penwidth = 3];
    edge[fontname = arial, penwidth = 3.6, fontsize = 28]

    tb1  [pos = "000,200", fillcolor = palevioletred];
    pb1  [pos = "100,200", fillcolor = darkseagreen3];
    si1  [pos = "200,200", fillcolor = white];
    bhs1 [pos = "300,200", fillcolor = mediumorchid3];
    dep1 [pos = "400,200", fillcolor = cadetblue3];
    tb2  [pos = "000,300", fillcolor = palevioletred];
    pb2  [pos = "100,300", fillcolor = darkseagreen3];
    si2  [pos = "200,300", fillcolor = white];
    bhs2 [pos = "300,300", fillcolor = mediumorchid3];
    dep2 [pos = "400,300", fillcolor = cadetblue3];
    tb3  [pos = "000,400", fillcolor = palevioletred];
    pb3  [pos = "100,400", fillcolor = darkseagreen3];
    si3  [pos = "200,400", fillcolor = white];
    bhs3 [pos = "300,400", fillcolor = mediumorchid3];
    dep3 [pos = "400,400", fillcolor = cadetblue3];
    tb4  [pos = "000,500", fillcolor = palevioletred];
    pb4  [pos = "100,500", fillcolor = darkseagreen3];
    si4  [pos = "200,500", fillcolor = white];
    bhs4 [pos = "300,500", fillcolor = mediumorchid3];
    dep4 [pos = "400,500", fillcolor = cadetblue3];
    tb5  [pos = "000,600", fillcolor = palevioletred];
    pb5  [pos = "100,600", fillcolor = darkseagreen3];
    si5  [pos = "200,600", fillcolor = white];
    bhs5 [pos = "300,600", fillcolor = mediumorchid3];
    dep5 [pos = "400,600", fillcolor = cadetblue3];

    {   edge [weight = 10, minlen = 4.0; style = invis, penwidth = 3];
        tb1   -> pb1  ;
        pb1   -> si1  ;
        si1   -> bhs1 ;
        bhs1  -> dep1 ;
        tb2   -> pb2  ;
        pb2   -> si2  ;
        si2   -> bhs2 ;
        bhs2  -> dep2 ;
        tb3   -> pb3  ;
        pb3   -> si3  ;
        si3   -> bhs3 ;
        bhs3  -> dep3 ;
        tb4   -> pb4  ;
        pb4   -> si4  ;
        si4   -> bhs4 ;
        bhs4  -> dep4 ;
        tb5   -> pb5  ;
        pb5   -> si5  ;
        si5   -> bhs5 ;
        bhs5  -> dep5 ;
    }

    // autogregrssive paths
    {   rank = same; tb1   -> tb2  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = palevioletred, style = invis]; }
    {   rank = same; tb2   -> tb3  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = palevioletred, headlabel = ".37"]; }
    {   rank = same; tb3   -> tb4  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = palevioletred, headlabel = ".28"]; }
    {   rank = same; tb4   -> tb5  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = palevioletred, headlabel = ".30"]; }
    {   rank = same; pb1   -> pb2  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = darkseagreen3, headlabel = ".28"]; }
    {   rank = same; pb2   -> pb3  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = darkseagreen3; style = invis]; }
    {   rank = same; pb3   -> pb4  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = darkseagreen3, headlabel = ".30"]; }
    {   rank = same; pb4   -> pb5  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = darkseagreen3, headlabel = ".45"]; }
    {   rank = same; si1   -> si2  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, style = bold, headlabel = ".35"]; }
    {   rank = same; si2   -> si3  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, style = bold, headlabel = ".25"]; }
    {   rank = same; si3   -> si4  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, style = bold; style = invis]; }
    {   rank = same; si4   -> si5  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, style = bold, headlabel = ".27"]; }
    {   rank = same; bhs1  -> bhs2 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = mediumorchid3, headlabel = ".43"];}
    {   rank = same; bhs2  -> bhs3 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = mediumorchid3, headlabel = ".49"]; }
    {   rank = same; bhs3  -> bhs4 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = mediumorchid3, headlabel = ".50"]; }
    {   rank = same; bhs4  -> bhs5 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = mediumorchid3, headlabel = ".48"]; }
    {   rank = same; dep1  -> dep2 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = cadetblue3; style = invis]; }
    {   rank = same; dep2  -> dep3 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = cadetblue3, headlabel = ".25"]; }
    {   rank = same; dep3  -> dep4 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = cadetblue3, headlabel = ".27"]; }
    {   rank = same; dep4  -> dep5 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = cadetblue3, headlabel = ".37"]; }

    {
    // cross-lagged paths
        edge[style = dashed, labelangle = -39.5, labeldistance = 3.6, labelfloat = true]
        ## [headlabel=<<table border="0" cellborder="0"><tr><td bgcolor="white">Head Label</td></tr></table>>,taillabel="Tail Label"]
        bhs1 -> pb2 [labelangle = 39.5, color = mediumorchid3, headlabel = ".39"];
        bhs2 -> pb3 [labelangle = 39.5, color = mediumorchid3, headlabel = ".25"];
        bhs3 -> pb4 [labelangle = 39.5, color = mediumorchid3, headlabel = ".31"];
        bhs3 -> si4 [color = mediumorchid3, headlabel = ".30"];
        bhs4 -> tb5 [color = mediumorchid3, headlabel = ".25"];
        dep4 -> pb5 [labelangle = 39.5, color = cadetblue3,    headlabel = ".26"];
        pb2 -> tb3  [color = darkseagreen3, headlabel = ".25"];
        pb3 -> si4  [color = darkseagreen3, headlabel = ".30"];
        pb3 -> dep4 [color = darkseagreen3, headlabel = ".35"];
        pb3 -> bhs4 [color = darkseagreen3, headlabel = ".28"];
        pb4 -> dep5 [color = darkseagreen3, headlabel = ".30"];
        pb4 -> bhs5 [color = darkseagreen3, headlabel = ".27"];
        si1 -> bhs2 [headlabel = ".19"];
        si1 -> dep2 [headlabel = ".17"];
        si1 -> pb2  [headlabel = ".14"];
        si2 -> pb3  [headlabel = ".16"];
        tb2 -> si3  [color = palevioletred, headlabel = ".15"];
    }
}


Solution

  • No - the splines attribute applies to the entire graph, so it can't be done with a single invocation / pass of a Graphviz tool

    Yes - Chop your graph into multiple graphs, each cluster into its own graph. Each (new) graph can have a different splines value. Run each (new) graph through dot -Tdot to determine layouts of each separately. Run those results through gvpack (https://www.graphviz.org/pdf/gvpack.1.pdf 2) to combine them into a single graph. And finally hand that to neato -n2 (https://graphviz.org/faq/#FaqDotWithNodeCoords) to produce an output file.
    Like so:

    S="";
    for f in sub[12].gv;  # sub1.gv and sub2.gv are the two created graphs
    do 
      T=dot; 
      F=`basename $f .gv`;
      dot -T$T $f >$F.$T; 
      S="$S  $F.$T"; 
    done; 
    gvpack -array_i3  $S |
      neato -n2 -Tpng >O.png
    

    Note - if necessary, it is possible to apply different splines values to individual edges by running a dot-formatted graph through neato -n2 multiple times, each time changing the splines value and removing individual edge pos values. Possible, but messy.