Drawing simple mediation diagram in R

折月煮酒 提交于 2021-02-06 09:08:26

问题


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:


回答1:


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)



回答2:


You can use the 'psych' package to test your moderation/mediation and it will give you a plot as well.

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



回答3:


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.




回答4:


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

```

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



来源:https://stackoverflow.com/questions/46465752/drawing-simple-mediation-diagram-in-r

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!