library(DiagrammeR)
CONSORT using graphviz
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].
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;
}
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;
}
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;
}
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.
<- 24
N <- 12
N_A <- 2
N_A_excl <- 10
N_A_analy <- 12
N_B <- 12
N_B_analy
<- grViz("digraph consort {
fig
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