Mediation model

I am trying to draw a longitudinal model as attached. I tried in many ways, but did not work. Could some one help.

Regards,

Please attach your current “best try”. It will help with recreating all the edges.

Hi,
Thanks a lot for your help. I attached the file so far.

Regards,

Holendro

(Attachment med.R is missing)

Here is the code

med_data ←
data.frame(
lab_wd06 = “WD06”,
lab_wd08 = “WD08”,
lab_wd10 = “WD10”,
lab_wd12 = “WD12”,
lab_wd14 = “WD14”,
lab_wd16 = “WD16”,
lab_dp06 = “DP06”,
lab_dp08 = “DP08”,
lab_dp10 = “DP10”,
lab_dp12 = “DP12”,
lab_dp14 = “DP14”,
lab_dp16 = “DP16”
)

med_diagram ← function(data, height = .8, width = 0.8, graph_label = NA, node_text_size = 10, edge_text_size = 3, color = “black”, ranksep = .3, minlen = 2.8){

require(glue)
require(DiagrammeR)

data$height ← height # node height
data$width ← width # node width
data$color ← color # node + edge border color
data$ranksep ← ranksep # separation btwn mediator row and x->y row
data$minlen ← minlen # minimum edge length

data$node_text_size ← node_text_size
data$edge_text_size ← edge_text_size

data$graph_label ← ifelse(is.na(graph_label), “”, paste0(“label = '”, graph_label, “’”))

diagram_out ← glue::glue_data(data,
"digraph flowchart {
fontname = Helvetica
<<graph_label>>
graph [ranksep = <>, rankdir = LR]

  # node definitions with substituted label text
  node [fontname = Helvetica, shape = circle, fixedsize = TRUE, width = <<width>>, height = <<height>>, fontsize = <<node_text_size>>, color = <<color>>]        
    wd0606 [pos = '-1,1!', label = '<<lab_wd06>>']
    wd0808 [pos = '1,1!', label = '<<lab_wd08>>']
    wd1010 [pos = '3,1!', label = '<<lab_wd10>>']
    wd1212 [pos = '5,1!', label = '<<lab_wd12>>']
    wd1414 [pos = '7,1!', label = '<<lab_wd14>>']
    wd1616 [pos = '9,1!', label = '<<lab_wd16>>']

    dp0606 [pos = '-1,-1!', label = '<<lab_dp06>>']
    dp0808 [pos = '1,-1!', label = '<<lab_dp08>>']
    dp1010 [pos = '3,-1!', label = '<<lab_dp10>>']
    dp1212 [pos = '5,-1!', label = '<<lab_dp12>>']
    dp1414 [pos = '7,-1!', label = '<<lab_dp14>>']
    dp1616 [pos = '9,-1!', label = '<<lab_dp16>>']


  # edge definitions with the node IDs
  edge [minlen = <<minlen>>, fontname = Helvetica, fontsize = <<edge_text_size>>, color = <<color>>, arrow = vee, arrowhead = vee, arrowtail = vee, arrowsize = 0.2]
    wd0606 -> wd0808 [color = forestgreen, penwidth = 0.5];
    wd0808 -> wd1010 [color = purple, penwidth = 0.5];
    wd1010 -> wd1212 [color = purple, penwidth = 0.5];
    wd1212 -> wd1414 [color = purple, penwidth = 0.5];
    wd1616 -> wd1414 [color = purple, penwidth = 0.5];

    dp0606 -> dp0808 [color = purple, penwidth = 0.5];
    dp0808 -> dp1010 [color = purple, penwidth = 0.5];
    dp1010 -> dp1212 [color = forestgreen, penwidth = 0.5];
    dp1212 -> dp1414 [color = purple, penwidth = 0.5];
    dp1414 -> dp1616 [color = forestgreen, penwidth = 0.5];

    {rank = same; wd0606 -> dp0606[penwidth = 0.5]};
    {rank = same; wd0808 -> dp0808[penwidth = 0.5]};
    {rank = same; wd1010 -> dp1010[penwidth = 0.5]};
    {rank = same; wd1212 -> dp1212[penwidth = 0.5]};
    {rank = same; wd1414 -> dp1414[penwidth = 0.5]};
    {rank = same; wd1616 -> dp1616[penwidth = 0.5]};

    {wd0606 -> dp0808[penwidth = 0.5]};
    {wd0606 -> dp1010[penwidth = 0.5]};
    {wd0606 -> dp1212[penwidth = 0.5]};
    {wd0606 -> dp1414[penwidth = 0.5]};
    {wd0606 -> dp1616[penwidth = 0.5]};
    {wd0808 -> dp1010[penwidth = 0.5]};
    {wd0808 -> dp1212[penwidth = 0.5]};
    {wd0808 -> dp1414[penwidth = 0.5]};
    {wd0808 -> dp1616[penwidth = 0.5]};
    {wd1010 -> dp1212[penwidth = 0.5]};
    {wd1010 -> dp1414[penwidth = 0.5]};
    {wd1010 -> dp1616[penwidth = 0.5]};
    {wd1212 -> dp1414[penwidth = 0.5]};
    {wd1212 -> dp1616[penwidth = 0.5]};
    {wd1414 -> dp1616[penwidth = 0.5]};

    {dp0606 -> wd0808[penwidth = 0.5]};
    {dp0606 -> wd1010[penwidth = 0.5]};
    {dp0606 -> wd1212[penwidth = 0.5]};
    {dp0606 -> wd1414[penwidth = 0.5]};
    {dp0808 -> wd1010[penwidth = 0.5]};
    {dp0808 -> wd1212[penwidth = 0.5]};
    {dp0808 -> wd1414[penwidth = 0.5]};
    {dp1010 -> wd1212[penwidth = 0.5]};
    {dp1010 -> wd1414[penwidth = 0.5]};
    {dp1212 -> wd1414[penwidth = 0.5]};
    {dp1212 -> wd1616[penwidth = 0.5]};

    {wd0606 -> wd1010[penwidth = 0.5]};
    {wd0606 -> wd1212[penwidth = 0.5]};
    {wd0606 -> wd1414[penwidth = 0.5]};

    {wd1616 -> wd1212[penwidth = 0.5]};
    {wd1616 -> wd1010[penwidth = 0.5]};
    {wd1616 -> wd0808[penwidth = 0.5]};
    {wd1616 -> wd0606[penwidth = 0.5]};

    {dp0606 -> dp1010[penwidth = 0.5]};
    {dp0606 -> dp1212[penwidth = 0.5]};
    {dp0606 -> dp1414[penwidth = 0.5]};
    {dp0606 -> dp1616[penwidth = 0.5]};

    {dp0808 -> dp1616[penwidth = 0.5]};
    {dp1010 -> dp1616[penwidth = 0.5]};
    {dp1212 -> dp1616[penwidth = 0.5]};

    { rank = same; wd0606; dp0606 }
    { rank = same; wd0808; dp0808 }
    { rank = same; wd1010; dp1010 }
  { rank = same; wd1212; dp1212 }
    { rank = same; wd1414; dp1414 }
  { rank = same; wd1616; dp1616 }
  
   }

  ", .open = "<<", .close = ">>")  

DiagrammeR::grViz(diagram_out)
}

med_diagram(med_data)

Regards

My monthly PSA: the forum supports code blocks with Graphviz syntax highlighting:

```dot
digraph {
  like -> so[with="a label"];
}
```

which produces:

digraph {
  like -> so[with="a label"];
}

Got the nodes positioned, but the edges are a challenge.

  1. Is Graphviz a requirement?
  2. Is this a one-time diagram or do you plan on making multiple variations?

Hi,
Thanks a lot. Yes, this is for a longitudinal data i.e. over time. Here, I am just showing for 6 time points. It will be nice if somehow control for edges. Once again thanks a lot.

Basically I did in R using the package DiagrammeR which uses internally graphViz. If it is not required I guess it will be ok.

I tried a simplified version of your first picture.
I didn’t check deeply if all the edges are there in the right direction, but it will not be hard to rectify them, I think.

If someone could explain how to get better results with the splines, it would be great !
I am really new to Graphviz so I am not even sure if is the right strategy.
What I so far understood is that when you want a better control to splines trajectory the neato layout is better because you can edit the pos attribute that is valid on Edges and Nodes for positioning nodes, or spline control points.

Here an interesting post about splines on this forum by scnorth
It says about the spline fitter :

There’s a top level, that knows about the graph layout. It computes a “feasible region” or constraint polygon for the spline, which is a simple polygon that must contain the endpoints. In dot, the polygon is obtained by first computing a “box path” (a list of boxes that touch sequentially on sides but the direction can change) because it is easy to generate boxes from the levels of the ranked graph. Then there is a phase to walk the boxes and make them into a simple polygon

I am wondering if it’s possible to specify this polygon…

anyway here is the code :

digraph {
concentrate=true
ranksep =1.7 nodesep=2
edge[arrowsize=0.5]
node[shape=“circle”]
a b c d e f
g h i j k l
{a b c d e f rank=“same”}
{g h i j k l rank=“same”}
a → g
b → h
c → i
d → j
e → k
f → l
edge[constraint=false]
a → b → c → d → e
g → h → i → j → k → l
f → e
a → h
a → i
a → j
a → k
a → l
b → i
b → j
b → k
b → l
c → j
c → k
c → l
a → c
a → d
a → e
f → d
f → c
f → b
f → a
g → e
h → e
i → e
j → e
e → l
i → d
g → b
g → c
d → k
d → l
g → d
g:se → i:s
g:se → j:s
g:se → k:s
g:se → l:s
h → c
h → d
h:s → l:s
i:s → l:s
j → l:s
}

Best I could do with Graphviz. I use dot to position the nodes, then neato -n to place the edges. Dot on its own did a terrible job with the edges.
These edges are still too close to the nodes and to each other, but …

command: dot myfile -Tdot |neato -n -Tpng >myfile.png

digraph flowchart {
fontname = Helvetica
//   graph [ rankdir = LR]
  ranksep=1.5  // inches
  nodesep=1
  splines=true
  
  # node definitions with substituted label text
  node [fontname = Helvetica, shape = circle] 
  // remove pos attributes - dot ignores, anyway
    {rank=same
    wd0606 [ label = "lab_wd06" group=g1]
    wd0808 [ label = "lab_wd08" group=g2]
    wd1010 [ label = "lab_wd10" group=g3]
    wd1212 [ label = "lab_wd12" group=g4]
    wd1414 [ label = "lab_wd14" group=g5]
    wd1616 [ label = "lab_wd16" group=g6]
    }
    
    {rank=same
    dp0606 [label = "lab_dp06" group=g1]
    dp0808 [label = "lab_dp08" group=g2]
    dp1010 [label = "lab_dp10" group=g3]
    dp1212 [label = "lab_dp12" group=g4]
    dp1414 [label = "lab_dp14" group=g5]
    dp1616 [label = "lab_dp16" group=g6]
    }
  // edge definitions with the node IDs
  // edge [ fontname = Helvetica,  arrow = vee, arrowhead = vee, arrowtail = vee, arrowsize = 0.2]
  // penwidth is in points,  .5 is too small??

    wd0606 -> wd0808 [color = forestgreen, ] 
    wd0808 -> wd1010 [color = purple, ]  
    wd1010 -> wd1212 [color = purple, ]  
    wd1212 -> wd1414 [color = purple, ]  
    //wd1616 -> wd1414 [color = purple, ]
    wd1414 -> wd1616 [color = purple,  dir=back ]

    dp0606 -> dp0808 [color = purple, ]  
    dp0808 -> dp1010 [color = purple, ]  
    dp1010 -> dp1212 [color = forestgreen, ]
    dp1212 -> dp1414 [color = purple, ]  
    dp1414 -> dp1616 [color = forestgreen, ]

    // curves, within same rank
    // get dp-to-dp edges under the nodes - southern ports
    dp0606:se -> dp1010:sw
    dp0606:se -> dp1212:sw  
    dp0606:se -> dp1414:sw  
    dp0606:se -> dp1616:sw  

    dp0808:se -> dp1616:sw  
    dp1010:se -> dp1616:sw
    dp1212:se -> dp1616:sw  

    wd0606 -> dp0606  
    wd0808 -> dp0808  
    wd1010 -> dp1010  
    wd1212 -> dp1212  
    wd1414 -> dp1414  
    wd1616 -> dp1616  

    wd0606 -> dp0808  
    wd0606 -> dp1010  
    wd0606 -> dp1212  
    wd0606 -> dp1414  
    wd0606 -> dp1616  
    wd0808 -> dp1010  
    wd0808 -> dp1212  
    wd0808 -> dp1414  
    wd0808 -> dp1616  
    wd1010 -> dp1212  
    wd1010 -> dp1414  
    wd1010 -> dp1616  
    wd1212 -> dp1414  
    wd1212 -> dp1616  
    wd1414 -> dp1616  

    dp0606 -> wd0808  
    dp0606 -> wd1010  
    dp0606 -> wd1212  
    dp0606 -> wd1414  
    dp0808 -> wd1010  
    dp0808 -> wd1212  
    dp0808 -> wd1414  
    dp1010 -> wd1212  
    dp1010 -> wd1414  
    dp1212 -> wd1414  
    dp1212 -> wd1616  

    wd0606 -> wd1010  
    wd0606 -> wd1212  
    wd0606 -> wd1414  
/*
    wd1616 -> wd1212  
    wd1616 -> wd1010  
    wd1616 -> wd0808  
    wd1616 -> wd0606
*/
     // wd1616 was "incorrectly" positioned maybe because of backward edges
     //  so, rewrite as forward edges with backward arrows
     wd1212 ->  wd1616 [dir=back]
     wd1010 ->  wd1616 [dir=back]     
     wd0808 ->  wd1616 [dir=back]
     wd0606 ->  wd1616 [dir=back]
   }

Giving:

In comparison, here is an hour or two with the pikchr (Pikchr: Documentation) variant of the pic language:

## ``` pikchr

circlerad = .4

    # first, position the nodes (and label and name them)
    right
    N_wd0606: circle "lab_wd06"
    move right
    N_wd0808: circle "lab_wd08"
    move right  
    N_wd1010: circle "lab_wd10"
    move right  
    N_wd1212: circle "lab_wd12"
    move right  
    N_wd1414: circle "lab_wd14"
    move right  
    N_wd1616: circle "lab_wd16"

    move to N_wd0606.w
    move down; move down; move down; move down
    right
    N_dp0606: circle "lab_dp06"
    move right  
    N_dp0808: circle  "lab_dp08"
    move right  
    N_dp1010: circle  "lab_dp10"
    move right  
    N_dp1212: circle  "lab_dp12"
    move right  
    N_dp1414: circle  "lab_dp14"
    move right  
    N_dp1616: circle "lab_dp16"

    #  time to place edges
    #  chop is used to draw from edge-to-edge, not center-to-center
    #  straight edges are easy
    #  first straight, horizontal edges
    arrow from N_wd0606 to N_wd0808 color  forestgreen  chop
    arrow from N_wd0808 to N_wd1010 color  purple  chop
    arrow from N_wd1010 to N_wd1212 color  purple  chop
    arrow from N_wd1212 to N_wd1414 color  purple  chop
    arrow from N_wd1616 to N_wd1414 color  purple  chop

    arrow from N_dp0606 to N_dp0808 color  purple  chop
    arrow from N_dp0808 to N_dp1010 color  purple  chop
    arrow from N_dp1010 to N_dp1212 color  forestgreen  chop
    arrow from N_dp1212 to N_dp1414 color  purple  chop
    arrow from N_dp1414 to N_dp1616 color  forestgreen  chop

  # now, straight, diagonal edges "rank-to-rank" (in dot-speak)
  arrow from N_wd0606  to N_dp0606 chop
  arrow from N_wd0808  to N_dp0808 chop
  arrow from N_wd1010  to N_dp1010 chop
  arrow from N_wd1212  to N_dp1212 chop
  arrow from N_wd1414  to N_dp1414 chop
  arrow from N_wd1616  to N_dp1616 chop
  arrow from N_wd0606  to N_dp0808 chop
  arrow from N_wd0606  to N_dp1010 chop
  arrow from N_wd0606  to N_dp1212 chop
  arrow from N_wd0606  to N_dp1414 chop
  arrow from N_wd0606  to N_dp1616 chop
  arrow from N_wd0808  to N_dp1010 chop
  arrow from N_wd0808  to N_dp1212 chop
  arrow from N_wd0808  to N_dp1414 chop
  arrow from N_wd0808  to N_dp1616 chop
  arrow from N_wd1010  to N_dp1212 chop
  arrow from N_wd1010  to N_dp1414 chop
  arrow from N_wd1010  to N_dp1616 chop
  arrow from N_wd1212  to N_dp1414 chop
  arrow from N_wd1212  to N_dp1616 chop
  arrow from N_wd1414  to N_dp1616 chop
  
  arrow from N_dp0606  to N_wd0808 chop
  arrow from N_dp0606  to N_wd1010 chop
  arrow from N_dp0606  to N_wd1212 chop
  arrow from N_dp0606  to N_wd1414 chop
  arrow from N_dp0808  to N_wd1010 chop
  arrow from N_dp0808  to N_wd1212 chop
  arrow from N_dp0808  to N_wd1414 chop
  arrow from N_dp1010  to N_wd1212 chop
  arrow from N_dp1010  to N_wd1414 chop
  arrow from N_dp1212  to N_wd1414 chop
  arrow from N_dp1212  to N_wd1616 chop

  #  arcs (splines with a single curve) are a bit harder
  #    in this case, we want them above the "wd" "rank"
  #    and below the "dp" "rank"
  #  so, we have to factor in which rank
  #     and which direction (left-to-right" or "right-to-left)
  #  cw means clockwise, ccw means counterclockwise (default(
  #  wd rank, arcs above

  arcrad = 2  #.4
  arc cw  -> from N_wd0606.ne  to N_wd1010.nw  #  chop
  arc cw  -> from N_wd0606.ne  to N_wd1212.nw  #  chop
  arc cw  -> from N_wd0606.ne  to N_wd1414.nw  #  chop
  arc ccw -> from N_wd1616.nw  to N_wd1212.ne  #  chop
  arc ccw -> from N_wd1616.nw  to N_wd1010.ne  #  chop
  arc ccw -> from N_wd1616.nw  to N_wd0808.ne  #  chop
  arc ccw -> from N_wd1616.nw  to N_wd0606.ne  #  chop
  #  dp rank, arcs below
  arc ccw -> from N_dp0606.se  to N_dp1010.sw  #  chop
  arc ccw -> from N_dp0606.se  to N_dp1212.sw  #  chop
  arc ccw -> from N_dp0606.se  to N_dp1414.sw  #  chop
  arc ccw -> from N_dp0606.se  to N_dp1616.sw  #  chop
  arc ccw -> from N_dp0808.se  to N_dp1616.sw  #  chop
  arc ccw -> from N_dp1010.se  to N_dp1616.sw  #  chop
  arc ccw -> from N_dp1212.se  to N_dp1616.sw  #  chop

##```