adayim / consort

Create CONSORT diagrams for clinical studies.
Other
31 stars 4 forks source link

Combining consort plots with gridExtra::grid.arrange fails #2

Closed EstherHerbert closed 2 years ago

EstherHerbert commented 2 years ago

Hello,

Thanks for the package! I'd like to be able to display two consort diagrams next to each other, specifically for situations like case-control designs where the data has come from two source populations. However, I'm struggling to find a way to do this successfully. I've tried using grid.arrange from gridExtra but that doesn't work:

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(consort)
library(gridExtra)
#> 
#> Attaching package: 'gridExtra'
#> The following object is masked from 'package:dplyr':
#> 
#>     combine

set.seed(500)

source1 <- data.frame(A = 1,
                      matchA = rbinom(100, 1, 0.4),
                      excA = sample(c("Sample not collected", "MRI not collected", 
                                      "Other", NA_character_), 100, replace = T,
                                    prob = c(0.03,0.03,0.03,0.9))) %>% 
  mutate(
    matchA = if_else(!is.na(excA), 0L, matchA),
    matchId = cumsum(matchA),
    matchId = if_else(matchA == 0, NA_integer_, matchId)
  )

source2 <- data.frame(B = 1,
                      matchB = rbinom(60, 1, 0.2),
                      excB = sample(c("Sample not collected", "MRI not collected", 
                                      "Other", NA_character_), 60, replace = T,
                                    prob = c(0.03,0.03,0.03,0.9))) %>% 
  mutate(
    matchB = if_else(!is.na(excB), 0L, matchB),
    matchId = cumsum(matchB),
    matchId = if_else(matchB == 0, NA_integer_, matchId)
  )

full_data <- full_join(source1, source2, by = "matchId", na_matches = "never")

(p1 <- consort_plot(full_data,
                    orders = c(A = "Source A",
                               excA = "Excluded",
                               matchA = "Match found"),
                    side_box = c("excA")))

(p2 <- consort_plot(full_data,
                    orders = c(B = "Source B",
                               excB = "Excluded",
                               matchB = "Match found"),
                    side_box = c("excB")))


grid.arrange(p1, p2, ncol = 2)
#> Error: $ operator is invalid for atomic vectors

Created on 2022-03-15 by the reprex package (v2.0.1)

Are there any plans to include capability for these situations into the package?

EstherHerbert commented 2 years ago

I forgot to include a simple example of what I'd like to eventually achieve:

Ideal-diagram

adayim commented 2 years ago

Hi,

There are two ways to combine two or more consort plots:

# Method 1: gridExtra package
grid.arrange(grobTree(p1), grobTree(p2), ncol = 2)

# Method 2: grid package
grid.newpage()
gf <- frameGrob(layout = grid.layout(1, 2))
gf <- packGrob(gf, grobTree(p1), row = 1, col = 1, dynamic = TRUE)
gf <- packGrob(gf, grobTree(p2), row = 1, col = 2, dynamic = TRUE)
grid.draw(gf)

# Connecting box
# List the objects
grid.ls(gf)
#> GRID.frame.107
#>   GRID.cellGrob.109
#>     GRID.gTree.108
#>       vertbox.1
#>       sidebox.2
#>       vertbox.1-sidebox.2
#>       vertbox.3
#>       vertbox.1-vertbox.3
#>   GRID.cellGrob.111
#>     GRID.gTree.110
#>       vertbox.4
#>       sidebox.5
#>       vertbox.4-sidebox.5
#>       vertbox.6
#>       vertbox.4-vertbox.6
# Draw arrow
# Adjust 0.5 below to your need, 0.5 is middle of Y axis
grid.segments(grobX(p1[[4]], 180) , .5,  
              grobX(p2[[4]], 0), .5, 
              arrow = arrow(length=unit(3, "mm"), type="closed"),
              gp=gpar(fill="black"))
# Or from the package function
ln <- connect_box(p1[[4]], p2[[4]], connect = "lr")
grid.draw(ln)

Created on 2022-03-15 by the reprex package (v2.0.1)

As you can see the output is not perfect, but you can change the x and y axis to meet your need. Also, the trick is finding the correct vertical box you want to connect. The gird.ls() function should be able to show you the names of the plot components. As you can see from the output, vertbox is the name of the vertical box and vertbox.4-vertbox.6 is the connection arrow from box 4 to box 6. Here, you want to connect vertbox.3 with vertbox.6. You can loop through name, as you should be able to see the name of the 4th element of p1. This is same for the p2

p1[[4]]$name
#>  [1] "vertbox.3"

Hope this solve your problem.

EstherHerbert commented 2 years ago

That's really helpful thank you!