简体   繁体   中英

Drawing simple mediation diagram in R

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:

来源:http://my.ilstu.edu/~jhkahn/medmod.html

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)

您可以使用“心理”包来测试您的节制/调解,它也会为您提供一个情节。

mediate(Output ~ Independent1 + (Mediator), data =mydata)

I don't know anything about ggraph but it's probably a possible route. Here's what 5 minutes of googling lead me to:

library(ggraph)
require(igraph)

test <- data.frame(from = c(1,2,3), to=c(2,3,1), coef = letters[1:3])
simple <- graph_from_data_frame(test)
V(simple)$name <- LETTERS[1:3]

ggraph(simple, layout = 'auto') + 
  geom_edge_link(aes(label = coef), 
                 angle_calc = 'along',
                 label_dodge = unit(2.5, 'mm'),
                 arrow = arrow(length = unit(4, 'mm')), 
                 end_cap = circle(3, 'mm')) + 
  geom_node_label(aes(label = name),size = 5) +
  theme_graph()

在此处输入图片说明

I have no idea how to enforce the geometry (if possible), but the nice thing is that the layout can generalise automatically.

Here are two versions of a mediation diagram built with DiagrammeR and a third built with TikZ. Each has strengths and weaknesses:

  • DiagrammeR + Graphviz:

    • Pros: easy to create diagrams with provided function, lots of options to adjust design of nodes.
    • Cons: few options to adjust text on edges, output is an htmlwidget object that can print to pdf but may require conversion under specific circumstances such as within a for-loop.
  • TikZ

    • Pros: highly customizable, including text on edges; easy to generate diagram with provided function
    • Cons: if you need HTML output, it's designed for LaTeX; syntax can be confusing
  • DiagrammeR native syntax (offered just for completeness)

    • Pros: code very easy to read, edit for R users
    • Cons: even fewer apparent ways to adjust text on edges

I'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.

Diagrammer + Graphviz

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

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")

```

TikZ绘制的中介图

Diagrammer native syntax

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()

显示边缘标签文本未正确排列的示例中介图

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM