In addition to its numerous built-in genomic functions, plotgardener can size and place ggplots and other grid-based visualizations within a plotgardener layout. Rather than arranging these plots in a relative manner, plotgardener can make and place these plots in absolute sizes and locations. This makes it simple and intuitive to make complex and combined plotgardener,ggplot, and other grid plot arrangements beyond a basic grid-style layout.

ggplots

Let’s say we wanted to make a complex multi-panel ggplot about COVID-19 data consisting of the following plots:

  1. A United States map depicting COVID-19 cases:
library(ggplot2)
library(scales)
data("COVID_USA_cases")

US_map <- ggplot(COVID_USA_cases, aes(long, lat, group = group)) +
    theme_void() +
    geom_polygon(aes(fill = cases_100K), color = "white", size = 0.3) +
    scale_fill_distiller(
        palette = "YlGnBu", direction = 1,
        labels = label_number(suffix = "", scale = 1e-3, accuracy = 1)
    ) +
    theme(
        legend.position = "left",
        legend.justification = c(0.5, 0.95),
        legend.title = element_blank(),
        legend.text = element_text(size = 7),
        legend.key.width = unit(0.3, "cm"),
        legend.key.height = unit(0.4, "cm"),
        plot.title = element_text(
            hjust = 0, vjust = -1,
            family = "ProximaNova", face = "bold",
            size = 12
        ),
        plot.title.position = "plot"
    ) +
    labs(title = "Thousands of COVID-19 Cases per 100,000 People") +
    coord_fixed(1.3)

print(US_map)

  1. Line plots showing the accumulation of COVID-19 cases over time:
data("COVID_NY_FL_tracking")

# Format y-labels
ylabels <- seq(0, 2000000, by = 500000) / 1e6
ylabels[c(3, 5)] <- round(ylabels[c(3, 5)], digits = 0)
ylabels[c(2, 4)] <- round(ylabels[c(2, 4)], digits = 1)
ylabels[5] <- paste0(ylabels[5], "M cases")
ylabels[1] <- ""

casesNY <- COVID_NY_FL_tracking[COVID_NY_FL_tracking$state == "new york", ]
casesNYpoly <- rbind(
    casesNY,
    data.frame(
        "date" = as.Date("2021-03-07"),
        "state" = "new york",
        "caseIncrease" = -1 * sum(casesNY$caseIncrease)
    )
)

cases_NYline <- ggplot(
    casesNY,
    aes(x = date, y = cumsum(caseIncrease))
) +
    geom_polygon(data = casesNYpoly, fill = "#B8E6E6") +
    scale_x_date(
        labels = date_format("%b '%y"),
        breaks = as.Date(c("2020-05-01", "2020-09-01", "2021-01-01")),
        limits = as.Date(c("2020-01-29", "2021-03-07")),
        expand = c(0, 0)
    ) +
    scale_y_continuous(labels = ylabels, position = "right", expand = c(0, 0)) +
    geom_hline(
        yintercept = c(500000, 1000000, 1500000, 2000000),
        color = "white", linetype = "dashed", size = 0.3
    ) +
    coord_cartesian(ylim = c(0, 2000000)) +
    theme(
        panel.background = element_rect(fill = "transparent", color = NA),
        text = element_text(family = "ProximaNova"),
        panel.grid = element_blank(),
        panel.border = element_blank(),
        plot.background = element_rect(fill = "transparent", color = NA),
        axis.line.x.bottom = element_blank(),
        axis.line.y = element_line(size = 0.1, color = "#8F9BB3"),
        axis.text.x = element_text(
            size = 7, hjust = 0.5,
            margin = margin(t = -10), color = "black"
        ),
        axis.title.x = element_blank(),
        axis.ticks.x = element_line(size = 0.2, color = "black"),
        axis.title.y = element_blank(),
        axis.text.y = element_text(size = 7, color = "black"),
        axis.ticks.y = element_blank(),
        axis.ticks.length.x.bottom = unit(-0.1, "cm"),
        plot.title = element_text(size = 8, hjust = 1),
        plot.title.position = "plot"
    )

print(cases_NYline)

casesFL <- COVID_NY_FL_tracking[COVID_NY_FL_tracking$state == "florida", ]
casesFLpoly <- rbind(
    casesFL,
    data.frame(
        "date" = as.Date("2021-03-07"),
        "state" = "florida",
        "caseIncrease" = -1 * sum(casesFL$caseIncrease)
    )
)

cases_FLline <- ggplot(
    casesFL,
    aes(x = date, y = cumsum(caseIncrease))
) +
    geom_polygon(data = casesFLpoly, fill = "#B8E6E6") +
    scale_x_date(
        labels = date_format("%b '%y"),
        breaks = as.Date(c("2020-05-01", "2020-09-01", "2021-01-01")),
        limits = as.Date(c("2020-01-29", "2021-03-07")),
        expand = c(0, 0)
    ) +
    scale_y_continuous(labels = ylabels, position = "right", expand = c(0, 0)) +
    geom_hline(
        yintercept = c(500000, 1000000, 1500000, 2000000),
        color = "white", linetype = "dashed", size = 0.3
    ) +
    coord_cartesian(ylim = c(0, 2000000)) +
    theme(
        panel.background = element_rect(fill = "transparent", color = NA),
        plot.background = element_rect(fill = "transparent", color = NA),
        text = element_text(family = "ProximaNova"),
        panel.grid = element_blank(),
        panel.border = element_blank(),
        axis.line.x.bottom = element_blank(),
        axis.line.y = element_line(size = 0.1, color = "#8F9BB3"),
        axis.title = element_blank(),
        axis.text.y = element_text(size = 7, color = "black"),
        axis.text.x = element_text(
            size = 7, hjust = 0.5,
            margin = margin(t = -10), color = "black"
        ),
        axis.ticks = element_line(color = "black", size = 0.2),
        axis.ticks.y = element_blank(),
        axis.ticks.length.x.bottom = unit(-0.1, "cm"),
        plot.title = element_text(size = 8, hjust = 1),
        plot.title.position = "plot"
    )

print(cases_FLline)

  1. Pie charts of COVID-19 vaccination status:
data("COVID_NY_FL_vaccines")

vaccines_NYpie <- ggplot(
    COVID_NY_FL_vaccines[COVID_NY_FL_vaccines$state == "new york", ],
    aes(x = "", y = value, fill = vax_group)
) +
    geom_bar(width = 1, stat = "identity") +
    theme_void() +
    scale_fill_manual(values = c("#FBAA7E", "#F7EEBF", "#FBCB88")) +
    coord_polar(theta = "y", start = 2.125, clip = "off") +
    geom_text(aes(
        x = c(1.9, 2, 1.9),
        y = c(1.65e7, 1.3e6, 7.8e6),
        label = paste0(percent, "%")
    ),
    size = 2.25, color = "black"
    ) +
    theme(
        legend.position = "none",
        plot.title = element_text(
            hjust = 0.5, vjust = -3.5, size = 10,
            family = "ProximaNova", face = "bold"
        ),
        text = element_text(family = "ProximaNova")
    ) +
    labs(title = "New York")

print(vaccines_NYpie)

vaccines_FLpie <- ggplot(
    COVID_NY_FL_vaccines[COVID_NY_FL_vaccines$state == "florida", ],
    aes(x = "", y = value, fill = vax_group)
) +
    geom_bar(width = 1, stat = "identity") +
    scale_fill_manual(values = c("#FBAA7E", "#F7EEBF", "#FBCB88")) +
    theme_void() +
    coord_polar(theta = "y", start = pi / 1.78, clip = "off") +
    geom_text(aes(
        x = c(1.95, 2, 1.9),
        y = c(1.9e7, 1.83e6, 9.6e6),
        label = paste0(percent, "%")
    ),
    color = "black",
    size = 2.25
    ) +
    theme(
        legend.position = "none",
        plot.title = element_text(
            hjust = 0.5, vjust = -4, size = 10,
            family = "ProximaNova", face = "bold"
        ),
        text = element_text(family = "ProximaNova")
    ) +
    labs(title = "Florida")

print(vaccines_FLpie)

We can now easily overlap and size all these ggplots by passing our saved plot objects into plotGG():

pageCreate(width = 9.5, height = 3.5, default.units = "inches")

plotGG(
    plot = US_map,
    x = 0.1, y = 0,
    width = 6.5, height = 3.5, just = c("left", "top")
)
plotGG(
    plot = cases_NYline,
    x = 6.25, y = 1.8,
    width = 3.025, height = 1.4, just = c("left", "bottom")
)
plotGG(
    plot = cases_FLline,
    x = 6.25, y = 3.5,
    width = 3.025, height = 1.4, just = c("left", "bottom")
)

In particular, plotgardener makes it easy to resize and place our pie charts in a layout that overlaps our line plots without it affecting the sizing of the other plots on the page:

plotGG(
    plot = vaccines_NYpie,
    x = 6.37, y = -0.05,
    width = 1.45, height = 1.45, just = c("left", "top")
)
plotGG(
    plot = vaccines_FLpie,
    x = 6.37, y = 1.67,
    width = 1.45, height = 1.45, just = c("left", "top")
)

We can also easily add additional elements to further enhance our complex ggplot arrangments, like a precise placement of text labels:

plotText(
    label = c("not", "partially", "fully vaccinated"),
    fontfamily = "ProximaNova", fontcolor = "black", fontsize = 7,
    x = c(6.58, 7.3, 7.435),
    y = c(0.74, 1.12, 0.51), just = c("left", "bottom")
)
plotText(
    label = c("not", "partially", "fully vaccinated"),
    fontfamily = "ProximaNova", fontcolor = "black", fontsize = 7,
    x = c(6.58, 7.39, 7.435),
    y = c(2.47, 2.75, 2.2), just = c("left", "bottom")
)
plotText(label = paste("Sources: The COVID Tracking Project;",
                        "Johns Hopkins Center for Civic Impact"),
            fontfamily = "ProximaNova", fontcolor = "black",
            fontsize = 7, fontface = "italic",
            x = 0.15, y = 3.45, just = c("left", "bottom"))

We are then left with a complex, precise, and elegant arrangement of ggplots as if we had arranged them together with graphic design software:

ComplexHeatmap

Heatmaps are an integral visualization to many multi-panel genomic visualizations. Existing Bioconductor packages like ComplexHeatmap provide excellent methods to produce such heatmaps. Since these plots are also based on grid graphics, it is possible to incorporate them into plotgardener figure arrangements.

For example, let’s say we made a heatmap representing the density of ChIP-seq data at a particular peak:

library(ComplexHeatmap)
library(purrr)
library(RColorBrewer)

# Simulated data
genmat <- map(
    1:2000,
    function(i) {
        c(sort(abs(rnorm(50)))[1:25], rev(sort(abs(rnorm(50)))[1:25])) * i/5000
    }
) %>% do.call(rbind, .)
genmat <- genmat[nrow(genmat):1,]
Heatmap(genmat, col = colorRampPalette(brewer.pal(n = 9, "YlGnBu"))(9),
        border = "black",
        border_gp = gpar(lwd = 0.5),
        show_heatmap_legend = FALSE, 
        cluster_rows = FALSE, cluster_columns = FALSE)

We can capture this heatmap with the grid function grid.grabExpr():

library(grid)
heatmap <- grid.grabExpr(draw(Heatmap(genmat, 
                    col = colorRampPalette(brewer.pal(n = 9, "YlGnBu"))(9),
                    border = "black",
                    border_gp = gpar(lwd = 0.5),
                    show_heatmap_legend = FALSE, 
                    cluster_rows = FALSE, cluster_columns = FALSE)))

We can now pass this object into the plotGG function and incorporate the heatmap into a plotgardener layout with additional figure elements made by plotgardener:

data("GM12878_HiC_10kb")
data("GM12878_ChIP_CTCF_signal")
data("GM12878_ChIP_H3K27ac_signal")
library("org.Hs.eg.db")
library("TxDb.Hsapiens.UCSC.hg19.knownGene")

## Create page
pageCreate(
    width = 6, height = 4, default.units = "inches")

params <- pgParams(chrom = "chr21", chromstart = 28150000, chromend = 29150000, 
                   assembly = "hg19",
                   x = 0.5, width = 2, default.units = "inches")
ctcf_range <- pgParams(range = c(0, 77),
                       assembly = "hg19")
hk_range <- pgParams(range = c(0, 32.6),
                     assembly = "hg19")
hic_gm <- plotHicTriangle(data = GM12878_HiC_10kb, params = params,
                          zrange = c(0, 200), resolution = 10000,
                          y = 1.5, height = 1.25, just = c("left", "bottom"))
annoHeatmapLegend(plot = hic_gm, fontsize = 7, 
                  x = 2.5, y = 0.5, width = 0.07, height = 0.5, 
                  just = c("right", "top"), default.units = "inches")
ctcf_gm <- plotSignal(data = GM12878_ChIP_CTCF_signal, params = c(params, ctcf_range),
                      fill = "#253494", linecolor = "#253494",
                      y = 1.6, height = 0.6)
plotText(label = "CTCF", fontcolor = "#253494", fontsize = 8,
         x = 0.5, y = 1.6, just = c("left","top"), default.units = "inches")
hk_gm <- plotSignal(data = GM12878_ChIP_H3K27ac_signal, params = c(params, hk_range),
                    fill = "#37a7db", linecolor = "#37a7db",
                    y = 2.3, height = 0.6, just = c("left", "top"))
plotText(label = "H3K27ac", fontcolor = "#37a7db", fontsize = 8,
         x = 0.5, y = 2.4, just = c("left","bottom"), default.units = "inches")
genes_gm <- plotGenes(params = params, stroke = 1, fontsize = 6,
                      strandLabels = FALSE,
                      y = 3, height = 0.4)
annoGenomeLabel(plot = genes_gm, params = params, 
                scale = "Kb", fontsize = 7,
                y = 3.5, digits = 0)

## Add ComplexHeatmap
gg_heatmap <- plotGG(
    plot = heatmap,
    x = 3, y = 0.5,
    width = 2.5, height = 3
)
seekViewport(name = "heatmap_matrix_2")
grid.xaxis(at = c(0, 0.5, 1), label = FALSE, gp = gpar(lwd = 0.5))
seekViewport(name = "page")

plotText(label = "Simulated Data", x = 4.25, y = 0.5,
         fontsize = 12, just = "bottom")
plotText(label = c(-2, 0, 2), x = c(3.065, 4.255, 5.425), 
         y = 3.6, fontsize = 7)
plotText(label = "Distance from center", x = 4.25, 
         y = 3.75, fontsize = 8)

## Hide page guides
pageGuideHide()

Session Info

sessionInfo()
#> R version 4.3.2 (2023-10-31)
#> Platform: x86_64-apple-darwin20 (64-bit)
#> Running under: macOS Sonoma 14.2.1
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
#> 
#> locale:
#> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> time zone: America/New_York
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats4    grid      stats     graphics  grDevices utils     datasets 
#> [8] methods   base     
#> 
#> other attached packages:
#>  [1] TxDb.Hsapiens.UCSC.hg19.knownGene_3.2.2
#>  [2] GenomicFeatures_1.54.4                 
#>  [3] GenomicRanges_1.54.1                   
#>  [4] GenomeInfoDb_1.38.8                    
#>  [5] org.Hs.eg.db_3.18.0                    
#>  [6] AnnotationDbi_1.64.1                   
#>  [7] IRanges_2.36.0                         
#>  [8] S4Vectors_0.40.2                       
#>  [9] Biobase_2.62.0                         
#> [10] BiocGenerics_0.48.1                    
#> [11] RColorBrewer_1.1-3                     
#> [12] purrr_1.0.2                            
#> [13] ComplexHeatmap_2.18.0                  
#> [14] scales_1.3.0                           
#> [15] ggplot2_3.5.0                          
#> [16] showtext_0.9-7                         
#> [17] showtextdb_3.0                         
#> [18] sysfonts_0.8.9                         
#> [19] plotgardenerData_1.8.0                 
#> [20] plotgardener_1.8.2                     
#> 
#> loaded via a namespace (and not attached):
#>   [1] strawr_0.0.91               rstudioapi_0.16.0          
#>   [3] jsonlite_1.8.8              shape_1.4.6.1              
#>   [5] magrittr_2.0.3              farver_2.1.1               
#>   [7] rmarkdown_2.26              GlobalOptions_0.1.2        
#>   [9] fs_1.6.3                    BiocIO_1.12.0              
#>  [11] zlibbioc_1.48.2             ragg_1.3.0                 
#>  [13] vctrs_0.6.5                 memoise_2.0.1              
#>  [15] Rsamtools_2.18.0            RCurl_1.98-1.14            
#>  [17] htmltools_0.5.8             S4Arrays_1.2.1             
#>  [19] progress_1.2.3              curl_5.2.1                 
#>  [21] SparseArray_1.2.4           gridGraphics_0.5-1         
#>  [23] sass_0.4.9                  bslib_0.7.0                
#>  [25] desc_1.4.3                  cachem_1.0.8               
#>  [27] GenomicAlignments_1.38.2    lifecycle_1.0.4            
#>  [29] iterators_1.0.14            pkgconfig_2.0.3            
#>  [31] Matrix_1.6-5                R6_2.5.1                   
#>  [33] fastmap_1.1.1               GenomeInfoDbData_1.2.11    
#>  [35] MatrixGenerics_1.14.0       clue_0.3-65                
#>  [37] digest_0.6.35               colorspace_2.1-0           
#>  [39] textshaping_0.3.7           RSQLite_2.3.5              
#>  [41] filelock_1.0.3              labeling_0.4.3             
#>  [43] fansi_1.0.6                 httr_1.4.7                 
#>  [45] abind_1.4-5                 compiler_4.3.2             
#>  [47] bit64_4.0.5                 withr_3.0.0                
#>  [49] doParallel_1.0.17           BiocParallel_1.36.0        
#>  [51] DBI_1.2.2                   highr_0.10                 
#>  [53] biomaRt_2.58.2              rappdirs_0.3.3             
#>  [55] DelayedArray_0.28.0         rjson_0.2.21               
#>  [57] tools_4.3.2                 glue_1.7.0                 
#>  [59] restfulr_0.0.15             cluster_2.1.6              
#>  [61] generics_0.1.3              gtable_0.3.4               
#>  [63] data.table_1.15.2           hms_1.1.3                  
#>  [65] xml2_1.3.6                  utf8_1.2.4                 
#>  [67] XVector_0.42.0              foreach_1.5.2              
#>  [69] pillar_1.9.0                stringr_1.5.1              
#>  [71] yulab.utils_0.1.4           circlize_0.4.16            
#>  [73] dplyr_1.1.4                 BiocFileCache_2.10.1       
#>  [75] lattice_0.22-6              rtracklayer_1.62.0         
#>  [77] bit_4.0.5                   tidyselect_1.2.1           
#>  [79] Biostrings_2.70.3           knitr_1.45                 
#>  [81] SummarizedExperiment_1.32.0 xfun_0.43                  
#>  [83] matrixStats_1.2.0           stringi_1.8.3              
#>  [85] yaml_2.3.8                  evaluate_0.23              
#>  [87] codetools_0.2-19            tibble_3.2.1               
#>  [89] ggplotify_0.1.2             cli_3.6.2                  
#>  [91] systemfonts_1.0.6           munsell_0.5.0              
#>  [93] jquerylib_0.1.4             Rcpp_1.0.12                
#>  [95] dbplyr_2.5.0                png_0.1-8                  
#>  [97] XML_3.99-0.16.1             parallel_4.3.2             
#>  [99] pkgdown_2.0.7               blob_1.2.4                 
#> [101] prettyunits_1.2.0           plyranges_1.22.0           
#> [103] bitops_1.0-7                crayon_1.5.2               
#> [105] GetoptLong_1.0.5            rlang_1.1.3                
#> [107] KEGGREST_1.42.0