CONSORT using graphviz

visaulisation
Author

maj

Published

October 21, 2024

Modified

October 22, 2024

Graphviz (short for Graph Visualization Software) is a package of open-source tools initiated by AT&T Labs Research for drawing graphs specified in DOT language scripts. Three main kinds of objects appear in the DOT language: graphs, nodes, edges. The outer most (main) graph can be directed (digraph) or undirected (graph). Within the main graph, a subgraph defines a subset of nodes and edges.

The following provides some basic reference examples, mostly taken from [1].

library(DiagrammeR)

Initial Examples

The specification of a simple graph is shown in Figure 1. Nodes are created when the name appears. Edges are created when nodes are joined by the edge operator ->.

Unlike other types of quarto blocks, it is necessary to enter the control elements in the form //| echo: true rather than the usual #| echo: FALSE.

digraph G {
  main -> parse -> execute;
  main -> init;
  main -> cleanup;
  execute -> make_string;
  execute -> printf
  init -> make_string;
  main -> printf;
  execute -> compare;
}
G main main parse parse main->parse init init main->init cleanup cleanup main->cleanup printf printf main->printf execute execute parse->execute make_string make_string execute->make_string execute->printf compare compare execute->compare init->make_string
Figure 1: Example dot graph

Nodes and edges can be given attributes to control their representation and placement. The size of the graph can be controlled with the size operator. If the drawing is too large, it is scaled uniformly as necessary to fit.

digraph G {
  size ="4,4";
  main [shape=box];   /* this is a comment */
  main -> parse [weight=8];
  parse -> execute;
  main -> init [style=dotted];
  main -> cleanup;
  execute -> { make_string; printf}
  init -> make_string;
  edge [color=red];   // so is this
  main -> printf [style=bold,label="100 times"];
  make_string [label="make a\nstring"];
  node [shape=box,style=filled,color=".7 .3 1.0"];
  execute -> compare;
}
G main main parse parse main->parse init init main->init cleanup cleanup main->cleanup printf printf main->printf 100 times execute execute parse->execute make_string make a string execute->make_string execute->printf compare compare execute->compare init->make_string
Figure 2: Example dot graph with attributes and comments

Attributes

Some common node shapes available include box, circle, record and plaintext. A complete list can be found at [www.graphviz.org/doc/info/shapes.html]. The modifier fixedsize=true ensures that the node’s actual size is aligned with width and height. Linework can be doubled up using peripheries=2 and orientation directed with orientation measured in degrees.

While the default name of a node is its name, this can be modified with the label attribute.

digraph structs {
  size ="2,2"; ratio=fill;
node [shape=record,fontsize=5];
  struct1 [shape=record,label="<f0> left|<f1> mid\ dle|<f2> right"];
  struct2 [shape=record,label="<f0> one|<f1> two"];
  struct3 [shape=record,label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h"];
  struct1 -> struct2;
  struct1 -> struct3;
}
structs struct1 left mid dle right struct2 one two struct1->struct2 struct3 hello world b c d e f g h struct1->struct3
Figure 3: Example of dot graph with nested attributes and comments

To specify the minimum distance between two adjacent nodes, use nodesep and to set the minimum vertical space use ranksep.

R Integration

There are a number of ways to incorporate DOT within R. One approach is to use the DiagrammeR package, specifically the grViz() function. This allows you to encode the graph using DOT notation but also pass in arguments as defined in R. Figure 4 provides a simple example.

N <- 24
N_A <- 12
N_A_excl <- 2
N_A_analy <- 10
N_B <- 12
N_B_analy <- 12

fig <- grViz("digraph consort {

  node [fontname = Helvetica, shape = box, width = 1];
  
  enrolled [label = 'Enrolled \n(n = @@1)'];  
  allocA [label = 'Assigned A \n(n = @@2)'];
  allocB [label = 'Assigned B \n(n = @@3)'];
  
  { rank = same; allocA allocB } 
  
  exclA[label = 'Excl A \n(n = @@6)'];
  
  analyA [label = 'Incl in analysis \n(n = @@4)'];
  analyB [label = 'Incl in analysis \n(n = @@5)'];
  
  { rank = same; analyA analyB } 
  
  blank1[label = '', width = 0.01, height = 0.01];

  blank2[label = '', width = 0.01, height = 0.01];
  blank3[label = '', width = 0.01, height = 0.01];
  blank4[label = '', width = 0.01, height = 0.01];
  /* all have same vertical position */
  { rank = same; blank2 blank3 blank4 }

  blank2 -> blank3 [arrowhead=none, minlen = 5];
  blank3 -> blank4 [arrowhead=none, minlen = 5];
  
  blank5[label = '', width = 0.01, height = 0.01]; 
  { rank = same; blank5 exclA } 

  enrolled -> blank1[dir = none];
  blank1 -> blank3[arrowhead=none];
  blank2 -> allocA;
  blank4 -> allocB;
  allocA -> blank5[arrowhead=none];
  blank5 -> exclA;
  blank5 -> analyA;
  allocB -> analyB;
  
}

  [1]: N
  [2]: N_A
  [3]: N_B
  [4]: N_A_analy
  [5]: N_B_analy
  [6]: N_A_excl
")
fig
Figure 4: Example of dot graph that uses arguments defined in R

References