patchwork icon indicating copy to clipboard operation
patchwork copied to clipboard

Using `free()` with `plot_annotation()` raises an error (sometimes)

Open trekonom opened this issue 1 year ago • 4 comments

When running an older code of mine I stumbled over a possible bug introduced in version 1.3.0 when using free() and plot_annotation()

Without plot_annotation() the following reprex works fine:

library(ggplot2)
library(patchwork)

packageVersion("patchwork")
#> [1] '1.3.0'

list(free(ggplot()), ggplot()) |>
  wrap_plots()

But adding plot_annotation() raises an error:

list(free(ggplot()), ggplot()) |>
  wrap_plots() &
  plot_annotation()
#> Error in split.default(names(settings), settings): first argument must be a vector

Moreover, the issue does not arise in general but depends on the position of the plot to which free() is applied, e.g. if I switch the plots which make up the patch we don't get an error.

list(ggplot(), free(ggplot())) |>
  wrap_plots() &
  plot_annotation()

And as a reference, the buggy code worked fine in version 1.2.0:

library(ggplot2)
library(patchwork)

packageVersion("patchwork")
#> [1] '1.2.0'

list(free(ggplot()), ggplot()) |>
  wrap_plots() &
  plot_annotation()

trekonom avatar Oct 30 '24 07:10 trekonom

Were you able to solve this? I'm hitting the same error with the exact same setup.

rdboyes avatar Mar 13 '25 18:03 rdboyes

@rdboyes Nop. For my use case it was sufficient to switch back to patchwork 1.2.0.

trekonom avatar Mar 14 '25 07:03 trekonom

I'm seeing the exact same error on patchwork 1.3.1.

> sessionInfo()
R version 4.5.0 (2025-04-11)
Platform: aarch64-apple-darwin24.2.0
Running under: macOS Sequoia 15.5

Matrix products: default
BLAS:   /opt/homebrew/Cellar/openblas/0.3.29/lib/libopenblasp-r0.3.29.dylib
LAPACK: /opt/homebrew/Cellar/r/4.5.0/lib/R/lib/libRlapack.dylib;  LAPACK version 3.12.1

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] grid      graphics  grDevices utils     datasets  stats     methods
[8] base

other attached packages:
 [1] MKmisc_1.9        latexpdf_0.1.8    ggplotify_0.1.2   treemap_2.4-4
 [5] rrvgo_1.20.0      ggvenn_0.1.10     ggrepel_0.9.6     rlang_1.1.6
 [9] ggrastr_1.0.2     parameters_0.27.0 assertthat_0.2.1  withr_3.0.2
[13] httr_1.4.7        qs2_0.1.5         shadowtext_0.1.5  ggstance_0.3.7
[17] ggbreak_0.1.5     gridExtra_2.3     gginnards_0.2.0-1 scales_1.4.0
[21] patchwork_1.3.1   broom_1.0.8       ggpmisc_0.6.2     ggpp_0.5.9
[25] fs_1.6.6          qs_0.27.3         lubridate_1.9.4   forcats_1.0.0
[29] purrr_1.1.0       readr_2.1.5       tibble_3.3.0      tidyverse_2.0.0
[33] rctutils_0.1.0    tidyr_1.3.1       future_1.58.0     devtools_2.4.5
[37] usethis_3.1.0     openxlsx_4.2.8    magrittr_2.0.3    dplyr_1.1.4
[41] rex_1.2.1         glue_1.8.0        stringr_1.5.1     ggplot2_3.5.2
[45] colorout_1.3-2

loaded via a namespace (and not attached):
  [1] splines_4.5.0           later_1.4.2             R.oo_1.27.1
  [4] datawizard_1.2.0        lifecycle_1.0.4         globals_0.18.0
  [7] NLP_0.3-2               lattice_0.22-7          MASS_7.3-65
 [10] insight_1.3.1           backports_1.5.0         limma_3.64.1
 [13] remotes_2.5.0           httpuv_1.6.16           zip_2.3.3
 [16] askpass_1.2.1           sessioninfo_1.2.3       pkgbuild_1.4.8
 [19] reticulate_1.43.0       DBI_1.2.3               RColorBrewer_1.1-3
 [22] pkgload_1.4.0           R.utils_2.13.0          BiocGenerics_0.54.0
 [25] yulab.utils_0.2.0       GenomeInfoDbData_1.2.14 IRanges_2.42.0
 [28] S4Vectors_0.46.0        tm_0.7-16               listenv_0.9.1
 [31] pheatmap_1.0.13         umap_0.2.10.0           MatrixModels_0.5-4
 [34] RSpectra_0.16-2         parallelly_1.45.0       codetools_0.2-20
 [37] xml2_1.3.8              RApiSerialize_0.1.4     tidyselect_1.2.1
 [40] aplot_0.2.8             UCSC.utils_1.4.0        farver_2.1.2
 [43] stats4_4.5.0            jsonlite_2.0.0          ellipsis_0.3.2
 [46] survival_3.8-3          tools_4.5.0             Rcpp_1.1.0
 [49] mgcv_1.9-3              GenomeInfoDb_1.44.0     fastmap_1.2.0
 [52] SparseM_1.84-2          openssl_2.3.3           digest_0.6.37
 [55] timechange_0.3.0        R6_2.6.1                mime_0.13
 [58] gridGraphics_0.5-1      colorspace_2.1-1        GO.db_3.21.0
 [61] dichromat_2.0-0.1       RSQLite_2.4.2           R.methodsS3_1.8.2
 [64] generics_0.1.4          data.table_1.17.8       robustbase_0.99-4-1
 [67] htmlwidgets_1.6.4       pkgconfig_2.0.3         gtable_0.3.6
 [70] blob_1.2.4              S7_0.2.0                XVector_0.48.0
 [73] htmltools_0.5.8.1       profvis_0.4.0           Biobase_2.68.0
 [76] png_0.1-8               wordcloud_2.6           ggfun_0.2.0
 [79] tzdb_0.5.0              nlme_3.1-168            curl_6.4.0
 [82] cachem_1.1.0            parallel_4.5.0          miniUI_0.1.2
 [85] vipor_0.4.7             AnnotationDbi_1.70.0    pillar_1.11.0
 [88] vctrs_0.6.5             slam_0.1-55             urlchecker_1.0.1
 [91] promises_1.3.3          stringfish_0.17.0       xtable_1.8-4
 [94] beeswarm_0.4.0          cli_3.6.5               compiler_4.5.0
 [97] crayon_1.5.3            labeling_0.4.3          ggbeeswarm_0.7.2
[100] stringi_1.8.7           gridBase_0.4-7          Biostrings_2.76.0
[103] lazyeval_0.2.2          bayestestR_0.16.1       GOSemSim_2.34.0
[106] quantreg_6.1            Matrix_1.7-3            hms_1.1.3
[109] bit64_4.6.0-1           statmod_1.5.0           KEGGREST_1.48.0
[112] shiny_1.11.1            igraph_2.1.4            memoise_2.0.1
[115] RcppParallel_5.1.10     DEoptimR_1.1-3-1        bit_4.6.0
[118] polynom_1.4-1

DarwinAwardWinner avatar Aug 02 '25 01:08 DarwinAwardWinner

I experimented a bit with some obvious-seeming solutions (modifying simplify_gt.free_table), and these seemed to fix things in testing, but not in my actual use case. I'll have to debug further when I have the time. Here's what I tried:

Test code:

# https://github.com/thomasp85/patchwork/issues/405

test_that("free() and wrap_plots() and plot_annotation() work together", {
    expect_doppelganger('Combining free(), wrap_plots(), and plot_annotation()', {
        list(free(ggplot()), ggplot()) |>
            wrap_plots() &
            plot_annotation()
    })
})

Attempted fix:

simplify_gt.free_table <- function(gt) {
  settings <- attr(gt, "free_settings")
  if (is.null(settings)) {
    settings <- c(t = "panel", r = "panel", b = "panel", l = "panel")
  }
  settings <- split(names(settings), settings)
  gt_new <- NextMethod()
  if (!is.null(settings$label)) {
    gt_new <- free_label(gt_new, c("t", "r", "b", "l") %in% settings$label)
  }
  if (!is.null(settings$space)) {
    gt_new <- free_space(gt_new, c("t", "r", "b", "l") %in% settings$space)
  }
  if (!is.null(settings$panel)) {
    gt_new <- free_panel(gt_new, c("t", "r", "b", "l") %in% settings$panel)
  }
  gt_new
}

The above test fails on the current main branch and passes with the above modification to the package code, but my real use case still fails, so it's not a complete fix (and I'm not entirely sure it's correct in the cases where it does work anyway).

DarwinAwardWinner avatar Aug 02 '25 19:08 DarwinAwardWinner