Is there a (preferably easy and straightforward) way to draw simple path diagrams with mediation coefficients in R?
I've been looking to DiagrammeR
package but that looks like an overkill (and to be honest I failed to figure out how to make a plot).
Other options I know of are Dia or Inkscape but those require manual positioning, connecting paths etc.
mediation
package has a plot
option but that draws bootstrapped CIs and what I'd like to achieve is a simple path with coefficients like this one:
While @baptiste solution might work as well, I was looking for a publishable format.
Function plotmat
from library(diagram)
is the one that got me the closest to my example:
For a reproducible example use:
library(diagram)
data <- c(0, "'.47*'", 0,
0, 0, 0,
"'.36*'", "'.33* (.16)'", 0)
M<- matrix (nrow=3, ncol=3, byrow = TRUE, data=data)
plot<- plotmat (M, pos=c(1,2),
name= c( "Math self-efficacy","Math ability", "Interest in the math major"),
box.type = "rect", box.size = 0.12, box.prop=0.5, curve=0)
Here are two versions of a mediation diagram built with DiagrammeR
and a third built with TikZ. Each has strengths and weaknesses:
DiagrammeR + Graphviz:
TikZ
DiagrammeR native syntax (offered just for completeness)
R
usersI've coded the first and second examples with a function med_diagram
that uses the glue
package to assemble the relevant graphviz or TikZ code. The function requires a data.frame that expects one row with columns for relevant labels and coefficients. For the DiagrammeR-specific function, there are also arguments to adjust various elements of the design.
med_data <-
data.frame(
lab_x = "Math\\nAbility",
lab_m = "Math\\nself-efficacy",
lab_y = "Interest in the\\nmath major",
coef_xm = ".47*",
coef_my = ".36*",
coef_xy = "0.33* (.16)"
)
med_diagram <- function(data, height = .75, width = 2, graph_label = NA, node_text_size = 12, edge_text_size = 12, color = "black", ranksep = .2, minlen = 3){
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 = <<ranksep>>]
# node definitions with substituted label text
node [fontname = Helvetica, shape = rectangle, fixedsize = TRUE, width = <<width>>, height = <<height>>, fontsize = <<node_text_size>>, color = <<color>>]
mm [label = '<<lab_m>>']
xx [label = '<<lab_x>>']
yy [label = '<<lab_y>>']
# edge definitions with the node IDs
edge [minlen = <<minlen>>, fontname = Helvetica, fontsize = <<edge_text_size>>, color = <<color>>]
mm -> yy [label = '<<coef_my>>'];
xx -> mm [label = '<<coef_xm>>'];
xx -> yy [label = '<<coef_xy>>'];
{ rank = same; mm }
{ rank = same; xx; yy }
}
", .open = "<<", .close = ">>")
DiagrammeR::grViz(diagram_out)
}
med_diagram(med_data)
TikZ and PGF need to be loaded as LaTeX packages in the preamble. The sample code below includes both those packages and some additional commands that, for example, set a global specification for 'mynode' used in the diagram. I received a warning to include \pgfplotsset{compat=1.17}
in my preamble but that may not be necessary for others. Note, this code builds on the example provided here: https://tex.stackexchange.com/a/225940/34597
---
title: "Sample Rmd"
author: "Your name here"
output: pdf_document
header-includes:
- \usepackage{tikz}
- \usepackage{pgfplots}
- \pgfplotsset{compat=1.17}
- \tikzset{mynode/.style={draw,text width=1in,align=center} }
- \usetikzlibrary{positioning}
---
```{r, load_packages, include = FALSE}
library(glue)
```
```{r, load_function}
med_diagram_tikz <- function(data) {
glue::glue_data(med_data,
"
\\begin{figure}
\\begin{center}
\\begin{tikzpicture}[font=\\sffamily]
\\node[mynode] (m){<<lab_m>>};
\\node[mynode,below left=of m](x) {<<lab_x>>};
\\node[mynode,below right=of m](y) {<<lab_y>>};
\\draw[-latex] (x.north) -- node[auto] {<<coef_xm>>} (m.west);
\\draw[-latex] (m.east) -- node[auto] {<<coef_my>>} (y.north);
\\draw[-latex] (x.east) --
node[below=2mm, align=center] {<<coef_xy>>} (y.west);
\\end{tikzpicture}
\\end{center}
\\end{figure}
",
.open = "<<", .close = ">>"
)
}
```
```{r create_diagram, echo = FALSE, results = 'asis'}
med_data <-
data.frame(
lab_x = "Math\\\\Ability",
lab_m = "Math\\\\self-efficacy",
lab_y = "Interest in the\\\\math major",
coef_xm = ".47*",
coef_my = ".36*",
coef_xy = "0.33* (.16)"
)
tikz_diagram_out <- med_diagram_tikz(med_data)
# requires chunk header to be set to results = 'asis'
cat("\n", tikz_diagram_out, "\n")
```
This third version of a mediation diagram uses syntax that's more easily understood by R
users but I don't love how the edge label text gets placed oddly (hence the two alternative versions above).
library(DiagrammeR)
# Create a node data frame (ndf)
ndf <- create_node_df(
n = 3,
label = c( "Math\nself-efficacy","Math\nability", "Interest in\nthe math major"),
shape = rep("rectangle", 3),
style = "empty",
fontsize = 6,
fixedsize = TRUE,
height = .5,
width = .75,
color = "gray80",
x = c(1, 2, 3),
y = c(1, 2, 1)
)
# Create an edge data frame (edf)
edf <- create_edge_df(
from = c(1, 1, 2),
to = c(2, 3, 3),
label = c(".47*", ".33* (.16)", ".36*"),
fontsize = 6,
minlen = 1,
color = "gray80",
)
# Create a graph with the ndf and edf
graph <- create_graph(
nodes_df = ndf,
edges_df = edf
)
graph %>%
render_graph()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With