gforge / Gmisc

An R package for creating tables, some plots and other useful utilities too small to merit their own package
49 stars 14 forks source link

Mapping error connectGrob when spreadHorizontal is used #68

Open warrenfm opened 2 months ago

warrenfm commented 2 months ago

Hi Gmisc,

Thanks for coming up with this fantastic package. I had a great time with it.

I noticed that when connecting boxGrobs that have been spreadHorizontal, the package maps the initial position of the boxGrob instead of the new position after being evenly spread. I've attached the initial flow analysis diagram with the error I've noted above and a second script where I had to create transparent dummy boxGrox with alpha = 0.01. This helped with mapping the new/correct positions of the boxGrobs after spreadHorizontal function.

Also to mention that we used this package for this analysis: https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0306131

I couldn't attached the R script, the code is below:

# 1. Clear console and environment -------------------------------------------

  rm(list=ls(all=T)) #clear environment
  cat("\f") #clear console

# 2. Install and load packages -----------------------------------------------

pacman::p_load(survey,      # Contains survey analysis specific functions
               dplyr,       # Package for data manipulation
               rio,         # Allows user to import files from Stata and SPSS, optional
               haven,       # Allows import of stata dta
               gmodels,     # Unweighted tables of proportions and frequencies
               expss,       # Apply labels
               tidyverse,
               janitor,
               haven,
               visdat,
               readr,
               gtsummary)

# 3. Create a flowchart --------------------------------------------------

pacman::p_load(Gmisc,
               glue,
               htmlTable,
               grid,
               magrittr)

# browseURL("https://cran.r-project.org/web/packages/Gmisc/vignettes/Grid-based_flowcharts.html")

# 4. Original flow diagram with the noted error ---------------------------

grid.newpage()
txt1 <- "1,359 patients who attended PAC-19\n clinics from Aug-2020 to Jan-2023\n had their data entered in REDCap\n electronic database "
org_cohort <- boxGrob(txt1,
                      x = 0.5, y = .91,
                      just = "center",
                      txt_gp = gpar(col = "black", cex = 1),
                      box_gp = gpar(fill = "white", col = "black"))
org_cohort

txt2 <-  "\n 1,359 (100%) PAC-19 clinic patients \n in REDCap electronic database"
xsec_study1 <- boxGrob(txt2, 
                       x = 0.5, 
                       y = coords(org_cohort)$bottom - distance(org_cohort,
                                                                org_cohort,
                                                                type = "h",
                                                                half = T) +
                         unit(5, "mm"), 
                       just = "center",
                       txt_gp = gpar(col = "black", cex = 1),
                       box_gp = gpar(fill = "white", col = "black"))
xsec_study1

# Connect the boxes and print/plot them
connectGrob(org_cohort, xsec_study1, "vertical")

xsec_study2 <- boxGrob(expression(paste(bold("Cross-sectional analysis:"))), 
                       x = 0.5, 
                       y = coords(xsec_study1)$top - unit(5, "mm"),
                       just = "center",
                       txt_gp = gpar(col = "black", cex = 1),
                       box_gp = gpar(col = "white", fill = "white", alpha = 0.1))
xsec_study2

txt3 <- "811 (59.7%) had only one \nPAC-19 clinic visit"
exclude_cohort <- boxGrob(txt3,
                          x = 0.8, 
                          y = coords(xsec_study1)$top - distance(xsec_study1,
                                                                 xsec_study1,
                                                                 type = "h",
                                                                 half = T) +
                            unit(5, "mm"),
                          just = "center",
                          txt_gp = gpar(col = "black", cex = 1),
                          box_gp = gpar(fill = "white",
                                        col = "black"))
exclude_cohort

# Connect the boxes and print/plot them
connectGrob(xsec_study1, exclude_cohort, "L")

long_study <- boxGrob(expression(paste(bold("Longitudinal analysis"))), 
                      x = 0.5, 
                      y = coords(exclude_cohort)$top - distance(exclude_cohort,
                                                                exclude_cohort,
                                                                type = "h",
                                                                half = T) -
                        unit(5, "mm"),
                      just = "center",
                      txt_gp = gpar(col = "black", cex = 1),
                      box_gp = gpar(col = "white", fill = "white", alpha = 0.1))
long_study 

# Connect the boxes and print/plot them
connectGrob(xsec_study1, long_study, "vertical")

txt4 <- "Visit 1:"
txt5 <- "548 (100%)"

visit1_a <- boxGrob(paste(c(txt4, txt5), collapse = "\n"),
                    x = 0.05, 
                    y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                    just = "center",
                    txt_gp = gpar(col = "black", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "black"))

txt6 <- "Visit 5:"
txt7 <- "50 (43.5%)"
visit5 <- boxGrob(paste(c(txt6, txt7), collapse = "\n"),
                  x = 0.95, 
                  y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))

txt8 <- "Visit 2:"
txt9 <- "548 (100%)"

visit2_a <- boxGrob(paste(c(txt8, txt9), collapse = "\n"),
                    x = coords(visit1_a)$right + 7.4*distance(visit1_a,
                                                              visit1_a,
                                                              type = "v",
                                                              half = T), 
                    y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                    just = "center",
                    txt_gp = gpar(col = "black", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "black"))

txt10 <- "Visit 3:"
txt11 <- "234 (42.7%)"
visit3_a <- boxGrob(paste(c(txt10, txt11), collapse = "\n"),
                    x = 0.5, 
                    y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                    just = "center",
                    txt_gp = gpar(col = "black", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "black"))

txt12 <- "Visit 4:"
txt13 <- "115 (49.1%)"
visit4_a <- boxGrob(paste(c(txt12, txt13), collapse = "\n"),
                    x = 0.7, 
                    y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                    just = "center",
                    txt_gp = gpar(col = "black", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "black"))

visit1_a
visit5
alignVertical(reference = visit1_a,
              visit2_a, visit3_a, visit4_a,
              .position = "top") %>%
  spreadHorizontal(.from = visit1_a,
                   .to = visit5)

# Connect the boxes and print/plot them
connectGrob(visit1_a, visit2_a, "horizontal") #visit2_a is mapped to the original position
connectGrob(visit2_a, visit3_a, "horizontal") #visit3_a is mapped to the original position
connectGrob(visit3_a, visit4_a, "horizontal") #visit4_a is mapped to the original position
connectGrob(visit4_a, visit5, "horizontal") 

txt14 <- "Discharged"
txt15 <- "216 (39.4%)"
txt16 <- "Lost to follow up"
txt17 <- "98 (17.9%)"

dchrg1 <- boxGrob(paste(c(txt14, txt15, txt16, txt17), collapse = "\n"),
                  x = coords(visit2_a)$right + unit(20, "mm"), 
                  y = coords(visit2_a)$bottom - 1.7*distance(visit2_a,
                                                             visit2_a,
                                                             type = "h",
                                                             half = T),
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))
dchrg1

# Connect the boxes and print/plot them
connectGrob(visit2_a, dchrg1, "L") 

txt18 <- "78 (33.3%)"
txt19 <- "41 (17.6%)"

dchrg3 <- boxGrob(paste(c(txt14, txt18, txt16, txt19), collapse = "\n"),
                  x = coords(visit3_a)$right + unit(20, "mm"), 
                  y = coords(visit2_a)$bottom - 1.7*distance(visit2_a,
                                                             visit2_a,
                                                             type = "h",
                                                             half = T),
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))
dchrg3

# Connect the boxes and print/plot them
connectGrob(visit3_a, dchrg3, "L") 

dchrg4 <- boxGrob(paste(c(txt14)),
                  x = coords(visit3_a)$right + unit(20, "mm"), 
                  y = coords(visit3_a)$top - unit(3.1, "mm"),
                  just = "center",
                  txt_gp = gpar(col = "white", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "white",
                                alpha = 0.01))
dchrg4

txt20 <- "49 (42.6%)"
txt21 <- "16 (13.9%)"

dchrg5 <- boxGrob(paste(c(txt14, txt20, txt16, txt21), collapse = "\n"),
                  x = coords(visit4_a)$right + unit(31, "mm"), 
                  y = coords(visit2_a)$bottom - 1.7*distance(visit2_a,
                                                             visit2_a,
                                                             type = "h",
                                                             half = T),
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))
dchrg5

# Connect the boxes and print/plot them
connectGrob(visit4_a, dchrg5, "L") 

# 5. Updated flow analysis with dummy transparent boxGrob -----------------

grid.newpage()
txt1 <- "1,359 patients who attended PAC-19\n clinics from Aug-2020 to Jan-2023\n had their data entered in REDCap\n electronic database "
org_cohort <- boxGrob(txt1,
                      x = 0.5, y = .91,
                      just = "center",
                      txt_gp = gpar(col = "black", cex = 1),
                      box_gp = gpar(fill = "white", col = "black"))
org_cohort

txt2 <-  "\n 1,359 (100%) PAC-19 clinic patients \n in REDCap electronic database"
xsec_study1 <- boxGrob(txt2, 
                       x = 0.5, 
                       y = coords(org_cohort)$bottom - distance(org_cohort,
                                                            org_cohort,
                                                            type = "h",
                                                            half = T) +
                         unit(5, "mm"), 
                       just = "center",
                       txt_gp = gpar(col = "black", cex = 1),
                       box_gp = gpar(fill = "white", col = "black"))
xsec_study1

# Connect the boxes and print/plot them
connectGrob(org_cohort, xsec_study1, "vertical")

xsec_study2 <- boxGrob(expression(paste(bold("Cross-sectional analysis:"))), 
                       x = 0.5, 
                       y = coords(xsec_study1)$top - unit(5, "mm"),
                       just = "center",
                       txt_gp = gpar(col = "black", cex = 1),
                       box_gp = gpar(col = "white", fill = "white", alpha = 0.1))
xsec_study2

txt3 <- "811 (59.7%) had only one \nPAC-19 clinic visit"
exclude_cohort <- boxGrob(txt3,
                           x = 0.8, 
                           y = coords(xsec_study1)$top - distance(xsec_study1,
                                                                  xsec_study1,
                                                                  type = "h",
                                                                  half = T) +
                             unit(5, "mm"),
                           just = "center",
                           txt_gp = gpar(col = "black", cex = 1),
                           box_gp = gpar(fill = "white",
                                         col = "black"))
exclude_cohort

# Connect the boxes and print/plot them
connectGrob(xsec_study1, exclude_cohort, "L")

long_study <- boxGrob(expression(paste(bold("Longitudinal analysis"))), 
                       x = 0.5, 
                       y = coords(exclude_cohort)$top - distance(exclude_cohort,
                                                                 exclude_cohort,
                                                                 type = "h",
                                                                 half = T) -
                        unit(5, "mm"),
                       just = "center",
                       txt_gp = gpar(col = "black", cex = 1),
                       box_gp = gpar(col = "white", fill = "white", alpha = 0.1))
long_study 

# Connect the boxes and print/plot them
connectGrob(xsec_study1, long_study, "vertical")

txt4 <- "Visit 1:"
txt5 <- "548 (100%)"

visit1_a <- boxGrob(paste(c(txt4, txt5), collapse = "\n"),
                  x = 0.05, 
                  y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))

# transparent dummy box
visit1_b <- boxGrob(paste(c(txt4, txt5), collapse = "\n"),
                x = 0.05, 
                y = coords(visit1_a)$top + coords(visit1_a)$half_height,
                just = "center",
                txt_gp = gpar(col = "white", cex = 1),
                box_gp = gpar(fill = "white",
                              col = "black", alpha = 0.01))
visit1_b

txt6 <- "Visit 5:"
txt7 <- "50 (43.5%)"
visit5 <- boxGrob(paste(c(txt6, txt7), collapse = "\n"),
                  x = 0.95, 
                  y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))

txt8 <- "Visit 2:"
txt9 <- "548 (100%)"

visit2_a <- boxGrob(paste(c(txt8, txt9), collapse = "\n"),
                    x = coords(visit1_a)$right + 7.4*distance(visit1_a,
                                                              visit1_a,
                                                              type = "v",
                                                              half = T), 
                    y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                    just = "center",
                    txt_gp = gpar(col = "black", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "black"))

# transparent dummy box
visit2_b <- boxGrob(paste(c(txt4, txt5), collapse = "\n"),
                    x = coords(visit2_a)$right - coords(visit1_a)$half_width -
                      unit(4.5, "mm"), 
                    y = coords(visit1_a)$top + coords(visit1_a)$half_height,
                    just = "center",
                    txt_gp = gpar(col = "white", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "white", 
                                  alpha = 0.01))
visit2_b

# transparent dummy box
visit2_c <- boxGrob(paste(c(txt4, txt5), collapse = "\n"),
                    x = coords(visit2_a)$right - coords(visit1_a)$half_width -
                      unit(4.5, "mm"), 
                    y = coords(visit1_a)$top - coords(visit1_a)$half_height,
                    just = "center",
                    txt_gp = gpar(col = "white", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "white",
                                  alpha = 0.01))
visit2_c 
# 
# Connect the boxes and print/plot them
connectGrob(visit1_b, visit2_c, "L")

txt10 <- "Visit 3:"
txt11 <- "234 (42.7%)"
visit3_a <- boxGrob(paste(c(txt10, txt11), collapse = "\n"),
                  x = 0.5, 
                  y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))

# transparent dummy box
visit3_b <- boxGrob(paste(c(txt10, txt11), collapse = "\n"),
                    x = coords(visit3_a)$right - coords(visit3_a)$half_width - 
                      unit(1.5, "mm"), 
                    y = coords(visit3_a)$top + coords(visit3_a)$half_height,
                    just = "center",
                    txt_gp = gpar(col = "white", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "white",
                                  alpha = 0.01
                                  ))
visit3_b
long_study 
# Connect the boxes and print/plot them
connectGrob(xsec_study1, long_study, "vertical")

# transparent dummy box
visit3_c <- boxGrob(paste(c(txt10, txt11), collapse = "\n"),
                    x = coords(visit3_a)$right - 1.5*coords(visit3_a)$width - 
                      unit(1.5, "mm"), 
                    y = coords(visit3_a)$top - coords(visit3_a)$half_height,
                    just = "center",
                    txt_gp = gpar(col = "white", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "white",
                                  alpha = 0.01
                                  ))
visit3_c

# Connect the boxes and print/plot them
connectGrob(visit2_b, visit3_c, "L")

txt12 <- "Visit 4:"
txt13 <- "115 (49.1%)"
visit4_a <- boxGrob(paste(c(txt12, txt13), collapse = "\n"),
                  x = 0.7, 
                  y = coords(long_study)$bottom - 0.6*coords(long_study)$height,
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))

# transparent dummy box
visit4_b <- boxGrob(paste(c(txt12, txt13), collapse = "\n"),
                    x = coords(visit4_a)$right - 0.5*coords(visit4_a)$half_width +
                      unit(2, "mm"), 
                    y = coords(visit4_a)$top + coords(visit4_a)$half_height,
                    just = "center",
                    txt_gp = gpar(col = "white", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "white",
                                  alpha = 0.01))
visit4_b

# transparent dummy box
visit4_c <- boxGrob(paste(c(txt12, txt13), collapse = "\n"),
                    x = coords(visit4_a)$right - 1.18*coords(visit4_a)$width, 
                    y = coords(visit4_a)$top - coords(visit4_a)$half_height,
                    just = "center",
                    txt_gp = gpar(col = "white", cex = 1),
                    box_gp = gpar(fill = "white",
                                  col = "white",
                                  alpha = 0.01
                    ))
visit4_c

# Connect the boxes and print/plot them
connectGrob(visit3_b, visit4_c, "L")

# Connect the boxes and print/plot them
connectGrob(visit4_b, visit5, "L")

visit1_a
visit5
alignVertical(reference = visit1_a,
              visit2_a, visit3_a, visit4_a,
              .position = "top") %>%
  spreadHorizontal(.from = visit1_a,
                   .to = visit5)

txt14 <- "Discharged"
txt15 <- "216 (39.4%)"
txt16 <- "Lost to follow up"
txt17 <- "98 (17.9%)"

dchrg1 <- boxGrob(paste(c(txt14, txt15, txt16, txt17), collapse = "\n"),
                  x = coords(visit2_a)$right + unit(20, "mm"), 
                  y = coords(visit2_a)$bottom - 1.7*distance(visit2_a,
                                                    visit2_a,
                                                    type = "h",
                                                    half = T),
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))
dchrg1

# transparent dummy box
dchrg2 <- boxGrob(paste(c(txt14)),
                  x = coords(visit2_a)$right + unit(20, "mm"), 
                  y = coords(visit2_a)$top - unit(3.1, "mm"),
                  just = "center",
                  txt_gp = gpar(col = "white", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "white",
                                alpha = 0.01))
dchrg2

# Connect the boxes and print/plot them
connectGrob(dchrg2, dchrg1, "vertical")

txt18 <- "78 (33.3%)"
txt19 <- "41 (17.6%)"

dchrg3 <- boxGrob(paste(c(txt14, txt18, txt16, txt19), collapse = "\n"),
                  x = coords(visit3_a)$right + unit(20, "mm"), 
                  y = coords(visit2_a)$bottom - 1.7*distance(visit2_a,
                                                             visit2_a,
                                                             type = "h",
                                                             half = T),
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))
dchrg3

# transparent dummy box
dchrg4 <- boxGrob(paste(c(txt14)),
                  x = coords(visit3_a)$right + unit(20, "mm"), 
                  y = coords(visit3_a)$top - unit(3.1, "mm"),
                  just = "center",
                  txt_gp = gpar(col = "white", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "white",
                                alpha = 0.01))
dchrg4

# Connect the boxes and print/plot them
connectGrob(dchrg4, dchrg3, "vertical")

txt20 <- "49 (42.6%)"
txt21 <- "16 (13.9%)"

dchrg5 <- boxGrob(paste(c(txt14, txt20, txt16, txt21), collapse = "\n"),
                  x = coords(visit4_a)$right + unit(31, "mm"), 
                  y = coords(visit2_a)$bottom - 1.7*distance(visit2_a,
                                                             visit2_a,
                                                             type = "h",
                                                             half = T),
                  just = "center",
                  txt_gp = gpar(col = "black", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "black"))
dchrg5

# transparent dummy box
dchrg6 <- boxGrob(paste(c(txt14)),
                  x = coords(visit4_a)$right + unit(31, "mm"), 
                  y = coords(visit4_a)$top - unit(3.1, "mm"),
                  just = "center",
                  txt_gp = gpar(col = "white", cex = 1),
                  box_gp = gpar(fill = "white",
                                col = "white",
                                alpha = 0.01))
dchrg6

# Connect the boxes and print/plot them
connectGrob(dchrg6, dchrg5, "vertical")

Thanks

Warren

gforge commented 1 month ago

Hi Warren,

Thanks for the detailed bug description. It would be good if you could clean it up a little so that I know what to focus on and there are a lot of packages that seem to be non-essential for the bug.

I think your problem is that the spread returns a new object with all the boxes in the right position and your old values are no longer applicable. Here's an example that I think does what you want:

boxes <- alignVertical(reference = visit1_a,
                       visit1_a, visit2_a, visit3_a, visit4_a, visit5,
                       .position = "top") %>%
  spreadHorizontal()

for (i in 2:length(boxes)) {
  connectGrob(boxes[[i - 1]], boxes[[i]], type = "h") |> 
    print()
}

I like the layout features you've been using with the bold text. This could possibly be a nice new feature, can you suggest how you think an API for this should look?