From 0751d7d058f3820fa3ea3b0a5e65f01fd89b1488 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Mon, 18 Nov 2024 22:10:54 +0100 Subject: [PATCH 01/29] before cran --- DESCRIPTION | 2 +- NEWS.md | 4 ++++ inst/WORDLIST | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index ac8fe90..9117f55 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: ggcall Type: Package Title: Extract ggplot2 Layers Created within a Function -Version: 0.3.0 +Version: 0.3.0.9000 Authors@R: person(given = "Maciej", family = "Nasinski", diff --git a/NEWS.md b/NEWS.md index f1d59c8..3ce5a92 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# ggcall v0.3.0.9000 + +* First CRAN release. + # ggcall v0.3.0 * Added support for ggplot related patchwork operators. diff --git a/inst/WORDLIST b/inst/WORDLIST index 0c4bf66..3d66cd0 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -8,6 +8,7 @@ colorizer colorTheme Config cowplot +CRAN cre dev dirmngr From 70b593f68fc5340ea1b4b7ddbdbb0af7899b984f Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Tue, 19 Nov 2024 17:37:41 +0100 Subject: [PATCH 02/29] title case title --- DESCRIPTION | 6 +++--- NEWS.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9117f55..db73c85 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: ggcall Type: Package -Title: Extract ggplot2 Layers Created within a Function -Version: 0.3.0.9000 +Title: Extract ggplot2 Layers Created Within a Function +Version: 0.3.1 Authors@R: person(given = "Maciej", family = "Nasinski", @@ -9,7 +9,7 @@ Authors@R: email = "nasinski.maciej@gmail.com") Maintainer: Maciej Nasinski Description: - Enhance ggplot2 with the ability to extract the code used to create a ggplot object, even when it is generated within a function. + Enhance ggplot2 with the ability to extract the code used to create a ggplot2 object, even when it is generated within a function. This feature aids in understanding, replicating, and modifying complex ggplot2 visualizations produced in functional workflows. License: Apache License (>= 2) Encoding: UTF-8 diff --git a/NEWS.md b/NEWS.md index 3ce5a92..85d49b3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# ggcall v0.3.0.9000 +# ggcall v0.3.1 * First CRAN release. From 00bc425dd23577ce2f8720b518c9c572994de917 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Wed, 20 Nov 2024 10:42:54 +0100 Subject: [PATCH 03/29] add quote for ggplot2 in DESCRIPTION --- DESCRIPTION | 8 ++++---- NEWS.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index db73c85..c75ed33 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: ggcall Type: Package -Title: Extract ggplot2 Layers Created Within a Function -Version: 0.3.1 +Title: Extract 'ggplot2' Layers Created Within a Function +Version: 0.3.2 Authors@R: person(given = "Maciej", family = "Nasinski", @@ -9,8 +9,8 @@ Authors@R: email = "nasinski.maciej@gmail.com") Maintainer: Maciej Nasinski Description: - Enhance ggplot2 with the ability to extract the code used to create a ggplot2 object, even when it is generated within a function. - This feature aids in understanding, replicating, and modifying complex ggplot2 visualizations produced in functional workflows. + Enhance 'ggplot2' with the ability to extract the code used to create a 'ggplot2' object, even when it is generated within a function. + This feature aids in understanding, replicating, and modifying complex 'ggplot2' visualizations produced in functional workflows. License: Apache License (>= 2) Encoding: UTF-8 LazyData: true diff --git a/NEWS.md b/NEWS.md index 85d49b3..b8b5dc7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# ggcall v0.3.1 +# ggcall v0.3.2 * First CRAN release. From 984510bfc382064832a612f19bf5ce9998f1c4cf Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 11:13:04 +0100 Subject: [PATCH 04/29] cran comments and standalone --- DESCRIPTION | 3 +- NEWS.md | 3 +- R/ggcall.R | 23 +++- R/patchwork.R | 19 +++ README.md | 6 +- man/ggcall.Rd | 5 +- tests/testthat/test_ggcall.R | 5 +- tests/testthat/test_ggcall_with_assignments.R | 18 +-- tests/testthat/test_patchwork.R | 113 ++++++++++-------- vignettes/ggcall.Rmd | 10 +- 10 files changed, 139 insertions(+), 66 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c75ed33..0a2e77d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -11,13 +11,14 @@ Maintainer: Maciej Nasinski Description: Enhance 'ggplot2' with the ability to extract the code used to create a 'ggplot2' object, even when it is generated within a function. This feature aids in understanding, replicating, and modifying complex 'ggplot2' visualizations produced in functional workflows. +URL: https://github.com/Polkas/ggcall, https://polkas.github.io/ggcall/ +BugReports: https://github.com/Polkas/ggcall/issues License: Apache License (>= 2) Encoding: UTF-8 LazyData: true Depends: ggplot2 Suggests: - backports, knitr, patchwork, rmarkdown, diff --git a/NEWS.md b/NEWS.md index b8b5dc7..fa57f4e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ -# ggcall v0.3.2 +# ggcall v0.3.3 * First CRAN release. +* Support usethis standalone, usethis::use_standalone can be used on ggcall to not depend on the ggcall package rather copy paste file to your package. # ggcall v0.3.0 diff --git a/R/ggcall.R b/R/ggcall.R index bb59994..4fcc615 100644 --- a/R/ggcall.R +++ b/R/ggcall.R @@ -1,3 +1,19 @@ +# --- +# repo: polkas/ggcall +# file: ggcall.R +# last-updated: 2024-11-21 +# license: https://unlicense.org +# imports: ggplot2 +# --- +# +# This file provides a minimal shim to provide a ggcall functionality on top of +# ggplot2. +# +# ## Changelog +# + +# nocov start + #' Enhanced `ggplot` Function with History Tracking #' #' Overrides the default `ggplot` function from the ggplot2 package, adding the @@ -112,7 +128,10 @@ ggplot <- function(...) { #' } #' plot_call <- ggcall(func(mtcars, "wt", "mpg")) #' # Optionally: Style the code with styler -#' styler::style_text(backports:::deparse1(plot_call)) +#' # deparse1 is recommended and available in R>=4.0.0 +#' styler::style_text( +#' paste(deparse(plot_call, 500), collapse = " ") +#' ) #' #' @export #' @@ -309,3 +328,5 @@ extract_names <- function(expr) { return(character()) } + +# nocov end diff --git a/R/patchwork.R b/R/patchwork.R index e260fce..67c45f6 100644 --- a/R/patchwork.R +++ b/R/patchwork.R @@ -1,3 +1,20 @@ +# --- +# repo: polkas/ggcall +# file: patchwork +# last-updated: 2024-11-21 +# license: https://unlicense.org +# dependencies: ggcall.R +# imports: [ggplot2, patchwork] +# --- +# +# This file provides a minimal shim to provide a ggcall functionality on top of +# ggplot2. Additionally patchwork operators are supported. +# +# ## Changelog +# + +# nocov start + #' @keywords internal patch_operator_base <- function(e1, e2, operator, class) { if (!requireNamespace("patchwork", quietly = TRUE)) { @@ -38,3 +55,5 @@ patch_operator_base <- function(e1, e2, operator, class) { "&.ggcall" <- function(e1, e2) { patch_operator_base(e1, e2, "&", "gg") } + +# nocov end diff --git a/README.md b/README.md index 64797b1..71ccb9b 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,11 @@ plot_call # attr(,"ggcall_env") # -styler::style_text(backports:::deparse1(plot_call)) +# Optionally: Style the code with styler +# install.packages("styler") +styler::style_text( + paste(deparse(plot_call), collapse = "\n") +) # Optionally: add assignments to call plot_call_with_assignments <- ggcall_add_assignments(plot_call) diff --git a/man/ggcall.Rd b/man/ggcall.Rd index 3384842..44f3d46 100644 --- a/man/ggcall.Rd +++ b/man/ggcall.Rd @@ -39,6 +39,9 @@ func <- function(data, x, y, bool = TRUE) { } plot_call <- ggcall(func(mtcars, "wt", "mpg")) # Optionally: Style the code with styler -styler::style_text(backports:::deparse1(plot_call)) +# deparse1 is recommended and available in R>=4.0.0 +styler::style_text( + paste(deparse(plot_call, 500), collapse = " ") +) } diff --git a/tests/testthat/test_ggcall.R b/tests/testthat/test_ggcall.R index c2c4e20..0d81e08 100644 --- a/tests/testthat/test_ggcall.R +++ b/tests/testthat/test_ggcall.R @@ -39,7 +39,10 @@ test_that("ggcall incorrect input", { test_that("ggcall returns correct call", { plot_call1 <- ggcall(func("wt", "mpg")) plot_call2 <- ggcall(funy()) - testthat::expect_identical(backports:::deparse1(plot_call1), backports:::deparse1(plot_call2)) + testthat::expect_identical( + paste(deparse(plot_call1), collapse = "\n"), + paste(deparse(plot_call2), collapse = "\n") + ) }) # nolint start diff --git a/tests/testthat/test_ggcall_with_assignments.R b/tests/testthat/test_ggcall_with_assignments.R index 8b83943..0ee330c 100644 --- a/tests/testthat/test_ggcall_with_assignments.R +++ b/tests/testthat/test_ggcall_with_assignments.R @@ -23,21 +23,21 @@ test_that("ggcall_add_assignments correctly adds assignments", { result_call <- ggcall_add_assignments(plot_call) expect_true(inherits(result_call, "ggcall_code")) - expect_true(grepl("data <-", backports:::deparse1(result_call))) - expect_true(grepl("x <-", backports:::deparse1(result_call))) - expect_true(grepl("y <-", backports:::deparse1(result_call))) - expect_true(grepl('data <- ggcall_env\\(plot_call\\)\\[\\[\\"data\\"\\]\\]', backports:::deparse1(result_call))) - expect_true(grepl('x <- \\"wt\\"', backports:::deparse1(result_call))) - expect_true(grepl('y <- \\"mpg\\"', backports:::deparse1(result_call))) - expect_true(grepl("ggplot\\(data", backports:::deparse1(result_call))) + expect_true(grepl("data <-", paste(deparse(result_call), collapse = "\n"))) + expect_true(grepl("x <-", paste(deparse(result_call), collapse = "\n"))) + expect_true(grepl("y <-", paste(deparse(result_call), collapse = "\n"))) + expect_true(grepl('data <- ggcall_env\\(plot_call\\)\\[\\[\\"data\\"\\]\\]', paste(deparse(result_call), collapse = "\n"))) + expect_true(grepl('x <- \\"wt\\"', paste(deparse(result_call), collapse = "\n"))) + expect_true(grepl('y <- \\"mpg\\"', paste(deparse(result_call), collapse = "\n"))) + expect_true(grepl("ggplot\\(data", paste(deparse(result_call), collapse = "\n"))) expect_silent(eval(result_call)) }) test_that("ggcall_add_assignments incorrectly adds assignments", { result_call <- ggcall_add_assignments(plot_call, vars = "x") - expect_true(grepl("x <-", backports:::deparse1(result_call))) - expect_false(grepl("data <-", backports:::deparse1(result_call))) + expect_true(grepl("x <-", paste(deparse(result_call), collapse = "\n"))) + expect_false(grepl("data <-", paste(deparse(result_call), collapse = "\n"))) expect_error(eval(result_call)) }) diff --git a/tests/testthat/test_patchwork.R b/tests/testthat/test_patchwork.R index 0dfd277..ba5caa9 100644 --- a/tests/testthat/test_patchwork.R +++ b/tests/testthat/test_patchwork.R @@ -9,15 +9,18 @@ p4 <- ggplot(mtcars) + geom_bar(aes(carb)) test_that("patchwork + operator pure", { expect_error(p1 + p2 + p3, NA) gcall <- ggcall(p1 + p2 + p3) - decall <- backports:::deparse1(gcall) + decall <- paste(deparse(gcall), collapse = "\n") expect_identical( decall, - backports:::deparse1( - quote( - ggplot(mtcars) + geom_point(aes(mpg, disp)) + - (ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))) + - (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) - ) + paste( + deparse( + quote( + ggplot(mtcars) + geom_point(aes(mpg, disp)) + + (ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))) + + (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) + ) + ), + collapse = "\n" ) ) expect_true(is.ggplot(eval_ggcall(gcall))) @@ -26,19 +29,22 @@ test_that("patchwork + operator pure", { test_that("patchwork operators - direct pure", { expect_error(p1 | p2 - p3 * p4 + p1 & p2 | p3, NA) gcall <- ggcall(p1 | p2 - p3 * p4 + p1 & p2 | p3) - decall <- backports:::deparse1(gcall) + decall <- paste(deparse(gcall), collapse = "\n") expect_identical( decall, - backports:::deparse1( - quote( - ggplot(mtcars) + geom_point(aes(mpg, disp)) | - ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) - - (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) * - (ggplot(mtcars) + geom_bar(aes(carb))) + - (ggplot(mtcars) + geom_point(aes(mpg, disp))) & - ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) | - ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl) - ) + paste( + deparse( + quote( + ggplot(mtcars) + geom_point(aes(mpg, disp)) | + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) - + (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) * + (ggplot(mtcars) + geom_bar(aes(carb))) + + (ggplot(mtcars) + geom_point(aes(mpg, disp))) & + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) | + ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl) + ) + ), + collapse = "\n" ) ) expect_true(is.ggplot(eval_ggcall(gcall))) @@ -49,16 +55,19 @@ library(patchwork) test_that("patchwork + operator with patchwork", { expect_error(p1 + p2 + p3 + plot_layout(ncol = 1), NA) gcall <- ggcall(p1 + p2 + p3 + plot_layout(ncol = 1)) - decall <- backports:::deparse1(gcall) + decall <- paste(deparse(gcall), collapse = "\n") expect_identical( decall, - backports:::deparse1( - quote( - ggplot(mtcars) + geom_point(aes(mpg, disp)) + - (ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))) + - (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) + - plot_layout(ncol = 1) - ) + paste( + deparse( + quote( + ggplot(mtcars) + geom_point(aes(mpg, disp)) + + (ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))) + + (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) + + plot_layout(ncol = 1) + ) + ), + collapse = "\n" ) ) expect_true(is.ggplot(eval_ggcall(gcall))) @@ -67,19 +76,22 @@ test_that("patchwork + operator with patchwork", { test_that("patchwork operators - direct", { expect_error(p1 | p2 - p3 * p4 + p1 & p2 | p3, NA) gcall <- ggcall(p1 | p2 - p3 * p4 + p1 & p2 | p3) - decall <- backports:::deparse1(gcall) + decall <- paste(deparse(gcall), collapse = "\n") expect_identical( decall, - backports:::deparse1( - quote( - ggplot(mtcars) + geom_point(aes(mpg, disp)) | - ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) - - (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) * - (ggplot(mtcars) + geom_bar(aes(carb))) + - (ggplot(mtcars) + geom_point(aes(mpg, disp))) & - ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) | - ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl) - ) + paste( + deparse( + quote( + ggplot(mtcars) + geom_point(aes(mpg, disp)) | + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) - + (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) * + (ggplot(mtcars) + geom_bar(aes(carb))) + + (ggplot(mtcars) + geom_point(aes(mpg, disp))) & + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) | + ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl) + ) + ), + collapse = "\n" ) ) expect_true(is.ggplot(eval_ggcall(gcall))) @@ -98,21 +110,24 @@ test_that("patchwork operators - internal", { gcall <- ggcall(funy()) - decall <- backports:::deparse1(gcall) + decall <- paste(deparse(gcall), collapse = "\n") expect_identical( decall, - backports:::deparse1( - quote( - ggplot(mtcars) + geom_point(aes(mpg, disp)) | - ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) - - (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) * - (ggplot(mtcars) + geom_bar(aes(carb))) + - (ggplot(mtcars) + geom_point(aes(mpg, disp))) & - ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) | - (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) / - (ggplot(mtcars) + geom_point(aes(mpg, disp))) - ) + paste( + deparse( + quote( + ggplot(mtcars) + geom_point(aes(mpg, disp)) | + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) - + (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) * + (ggplot(mtcars) + geom_bar(aes(carb))) + + (ggplot(mtcars) + geom_point(aes(mpg, disp))) & + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) | + (ggplot(mtcars) + geom_bar(aes(gear)) + facet_wrap(~cyl)) / + (ggplot(mtcars) + geom_point(aes(mpg, disp))) + ) + ), + collapse = "\n" ) ) expect_true(is.ggplot(eval_ggcall(gcall))) diff --git a/vignettes/ggcall.Rmd b/vignettes/ggcall.Rmd index 6efa89a..74836dc 100644 --- a/vignettes/ggcall.Rmd +++ b/vignettes/ggcall.Rmd @@ -79,7 +79,11 @@ plot_call # attr(,"ggcall_env") # -styler::style_text(backports:::deparse1(plot_call)) +# Optionally: Style the code with styler +# install.packages("styler") +styler::style_text( + paste(deparse(plot_call_with_assignments), collapse = "\n") +) # Optionally: add assignments to call plot_call_with_assignments <- ggcall_add_assignments(plot_call) @@ -148,7 +152,9 @@ plot_call # Optionally: Style the code with styler # install.packages("styler") -styler::style_text(backports:::deparse1(plot_call)) +styler::style_text( + paste(deparse(plot_call), collapse = "\n") +) # ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + # geom_point(alpha = 0.4) + # facet_grid(~gear) + From de6472dd0be30296b3bcf86fce0345070e6d9b65 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 11:13:25 +0100 Subject: [PATCH 05/29] cran comments and standalone --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0a2e77d..62c9822 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: ggcall Type: Package Title: Extract 'ggplot2' Layers Created Within a Function -Version: 0.3.2 +Version: 0.3.3 Authors@R: person(given = "Maciej", family = "Nasinski", From b7713ed8d5312ecff21b0ca867099be932f8d1f9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:13:44 +0000 Subject: [PATCH 06/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- inst/WORDLIST | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/inst/WORDLIST b/inst/WORDLIST index 3d66cd0..9e13bfc 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -2,6 +2,7 @@ ARG aut backports bracketedPaste +BugReports codecov coenraads colorizer @@ -25,6 +26,7 @@ ggcall’s ggpairs ggplot Github +github gitlens gmail gnupg @@ -32,6 +34,7 @@ htop https ikuyadeu init +io iproute jq json @@ -97,6 +100,7 @@ sudo SYS testthat Uncomment +usethis usr ust ver From 215a7ad986cd08e02981e799b93e406cd78ce8e8 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 11:16:41 +0100 Subject: [PATCH 07/29] prefix standalone --- R/{ggcall.R => standalone-ggcall.R} | 0 R/{patchwork.R => standalone-patchwork.R} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename R/{ggcall.R => standalone-ggcall.R} (100%) rename R/{patchwork.R => standalone-patchwork.R} (100%) diff --git a/R/ggcall.R b/R/standalone-ggcall.R similarity index 100% rename from R/ggcall.R rename to R/standalone-ggcall.R diff --git a/R/patchwork.R b/R/standalone-patchwork.R similarity index 100% rename from R/patchwork.R rename to R/standalone-patchwork.R From 9e8fdfd17d342d2bd86988cc01331a404f66f061 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 11:19:32 +0100 Subject: [PATCH 08/29] depends --- R/standalone-ggcall.R | 2 +- R/standalone-patchwork.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/standalone-ggcall.R b/R/standalone-ggcall.R index 4fcc615..d7a1d9f 100644 --- a/R/standalone-ggcall.R +++ b/R/standalone-ggcall.R @@ -3,7 +3,7 @@ # file: ggcall.R # last-updated: 2024-11-21 # license: https://unlicense.org -# imports: ggplot2 +# depends: ggplot2 # --- # # This file provides a minimal shim to provide a ggcall functionality on top of diff --git a/R/standalone-patchwork.R b/R/standalone-patchwork.R index 67c45f6..7513e66 100644 --- a/R/standalone-patchwork.R +++ b/R/standalone-patchwork.R @@ -4,7 +4,7 @@ # last-updated: 2024-11-21 # license: https://unlicense.org # dependencies: ggcall.R -# imports: [ggplot2, patchwork] +# depends: [ggplot2, patchwork] # --- # # This file provides a minimal shim to provide a ggcall functionality on top of From b90cf6faa43dbdf34b90c5ae81ab4cfd8848fcb3 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 11:49:31 +0100 Subject: [PATCH 09/29] update --- DESCRIPTION | 1 + README.md | 70 ++++++++++++++++++++++++++++++++++++++++---- vignettes/ggcall.Rmd | 58 ++++++++++++++++++++++++++---------- 3 files changed, 108 insertions(+), 21 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 62c9822..579e112 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,6 +20,7 @@ Depends: ggplot2 Suggests: knitr, + usethis, patchwork, rmarkdown, styler, diff --git a/README.md b/README.md index 71ccb9b..37104e8 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,73 @@ Then, each template will generate the expected plot, and the ggplot2 code behind Please access the [Get Started vignette](https://polkas.github.io/ggcall/articles/ggcall.html) for more information. -## Implementation -The ggcall can be implemented in a few ways. -One of them is to copy and paste one or two R files to your package R directory. -Another option is to use the ggcall as a DESCRIPTION file dependency for your package. +## Developers Implementation -Please access the [Get Started vignette](https://polkas.github.io/ggcall/articles/ggcall.html) for more information. +The ggcall can be implemented in a few ways to your package, direct dependency or standalone. + + +`usethis` package is optional but it truly simplify the process. +The `usethis` >= 2.2.0 is required. + +``` +install.packages("usethis") +``` + +GENERAL COMMENTS: + +``` +# Apply only if needed +# In your own code remove all ggplot2:: prefix-ing before ggplot function calls +# ggplot2::ggplot(...) -> ggplot(...) +``` + +DIRECT DEPENDENCY: + +An option is to use the ggcall as a DESCRIPTION file import dependency for your package. + +``` +usethis::use_package("ggcall") +``` + +``` +# DO NOT import ggplot function from ggplot2 instead import it from ggcall + +# a. When importing all ggplot2 functions +# #' @rawNamespace import(ggplot2, except = c(ggplot)) +# #' @import ggcall +# b. When importing specific ggplot2 functions +# #' @importFrom ggplot2 geom_line +# #' @importFrom ggcall ggplot +``` + +STANALONE - copy paste the files to your own project: + +with `patchwork` support + +``` +# copy paste the ggcall.R file to your own package R directory +# copy paste the patchwork.R file to your own package R directory +``` + +```r +usethis::use_package("ggplot2") +usethis::use_package("patchwork") +usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.4") +# you may need to update the file time to time +``` + +without `patchwork` support + +``` +# copy paste the ggcall.R file to your own package R directory +``` + +``` +usethis::use_package("ggplot2") +usethis::use_standalone("polkas/ggcall", "ggcall.R", ref = "v0.3.3") +# you may need to update the file time to time +``` ## Usage diff --git a/vignettes/ggcall.Rmd b/vignettes/ggcall.Rmd index 74836dc..3e1fed5 100644 --- a/vignettes/ggcall.Rmd +++ b/vignettes/ggcall.Rmd @@ -185,34 +185,32 @@ eval_ggcall(plot_call_with_assignments) eval_ggcall(plot_call, mtcars = mtcars[1:10, ], x = "disp") ``` -## Implementation +## Developers Implementation -The ggcall can be implemented in a few ways. -One of them is to copy and paste one or two R files to your package R directory. -Another option is to use the ggcall as a DESCRIPTION file dependency for your package. +The ggcall can be implemented in a few ways to your package, direct dependency or standalone. -### General + +`usethis` package is optional but it truly simplify the process. +The `usethis` >= 2.2.0 is required. ``` -# Apply only if needed -# Remove all ggplot2:: prefix-ing before ggplot function calls -# ggplot2::ggplot(...) -> ggplot(...) +install.packages("usethis") ``` -### OPTION 1 +GENERAL COMMENTS: ``` -# copy paste the ggcall.R file to your own package -# OPTIONAL copy paste the patchwork.R file if you need patchwork support -# you may need to update the file time to time +# Apply only if needed +# In your own code remove all ggplot2:: prefix-ing before ggplot function calls +# ggplot2::ggplot(...) -> ggplot(...) ``` -### OPTION 2 +DIRECT DEPENDENCY: + +An option is to use the ggcall as a DESCRIPTION file import dependency for your package. ``` -# ADD ggcall to Depends or Imports section in DESCRIPTION file -# If added to Depends then all ggcall functions will be preloaded when loading your own package -# If added to Imports the end user will have to library(ggcall) to get the ggcall functionalities +usethis::use_package("ggcall") ``` ``` @@ -226,6 +224,34 @@ Another option is to use the ggcall as a DESCRIPTION file dependency for your pa # #' @importFrom ggcall ggplot ``` +STANALONE - copy paste the files to your own project: + +with `patchwork` support + +``` +# copy paste the ggcall.R file to your own package R directory +# copy paste the patchwork.R file to your own package R directory +``` + +```r +usethis::use_package("ggplot2") +usethis::use_package("patchwork") +usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.4") +# you may need to update the file time to time +``` + +without `patchwork` support + +``` +# copy paste the ggcall.R file to your own package R directory +``` + +``` +usethis::use_package("ggplot2") +usethis::use_standalone("polkas/ggcall", "ggcall.R", ref = "v0.3.3") +# you may need to update the file time to time +``` + ### Example implementation in GGally package A notable example of ggcall’s successful integration is seen in the `GGally` package fork. From 83e31d078ad94596e7f3fb4b3ba3b28bb65af1bb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:50:37 +0000 Subject: [PATCH 10/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- inst/WORDLIST | 1 + 1 file changed, 1 insertion(+) diff --git a/inst/WORDLIST b/inst/WORDLIST index 9e13bfc..89d1bd5 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -89,6 +89,7 @@ runArgs sdras seccomp setuptools +STANALONE strace streetsidesoftware Styleguide From e135345a86805287bda199c521b8d92bc15d16cb Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 12:35:19 +0100 Subject: [PATCH 11/29] Update README.md --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 37104e8..c90abe1 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ Please access the [Get Started vignette](https://polkas.github.io/ggcall/article The ggcall can be implemented in a few ways to your package, direct dependency or standalone. +A "standalone" file implements a minimum set of functionality in such a way that it can be copied into another package. +`usethis::use_standalone()` makes it easy to get such a file into your own repo/package. `usethis` package is optional but it truly simplify the process. The `usethis` >= 2.2.0 is required. @@ -41,14 +43,6 @@ GENERAL COMMENTS: # ggplot2::ggplot(...) -> ggplot(...) ``` -DIRECT DEPENDENCY: - -An option is to use the ggcall as a DESCRIPTION file import dependency for your package. - -``` -usethis::use_package("ggcall") -``` - ``` # DO NOT import ggplot function from ggplot2 instead import it from ggcall @@ -60,6 +54,14 @@ usethis::use_package("ggcall") # #' @importFrom ggcall ggplot ``` +DIRECT DEPENDENCY: + +An option is to use the ggcall as a DESCRIPTION file import dependency for your package. + +``` +usethis::use_package("ggcall") +``` + STANALONE - copy paste the files to your own project: with `patchwork` support From b3f008837b31acba8a5ab0c16f1e01053ba6051a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:35:54 +0000 Subject: [PATCH 12/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- inst/WORDLIST | 1 + 1 file changed, 1 insertion(+) diff --git a/inst/WORDLIST b/inst/WORDLIST index 89d1bd5..b4d3e37 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -79,6 +79,7 @@ PTRACE README REditorSupport remoteUser +repo Reproducibility reproducibility rmarkdown From 9f083ff00d13305d2f52ff754368646bf8da361a Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 13:37:13 +0100 Subject: [PATCH 13/29] add import --- NAMESPACE | 2 +- R/standalone-ggcall.R | 2 +- README.md | 1 + man/eval_ggcall.Rd | 2 +- man/ggcall.Rd | 2 +- man/ggcall_add_assignments.Rd | 2 +- man/ggcall_env.Rd | 2 +- man/ggplot.Rd | 2 +- man/plus-.gg.Rd | 2 +- vignettes/ggcall.Rmd | 4 ++-- 10 files changed, 11 insertions(+), 10 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index d317611..e1b298a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,4 +11,4 @@ export(ggcall) export(ggcall_add_assignments) export(ggcall_env) export(ggplot) -importFrom(ggplot2,ggplot) +import(ggplot2, except = c(ggplot)) diff --git a/R/standalone-ggcall.R b/R/standalone-ggcall.R index d7a1d9f..6fb884b 100644 --- a/R/standalone-ggcall.R +++ b/R/standalone-ggcall.R @@ -26,7 +26,7 @@ #' attribute 'ggcall' that stores the history of plot construction. #' #' @seealso \code{\link[ggplot2]{ggplot}} -#' @importFrom ggplot2 ggplot +#' @rawNamespace import(ggplot2, except = c(ggplot)) #' @examples #' p <- ggplot(mtcars, aes(x = wt, y = mpg)) #' # the + function has to come from ggcall package diff --git a/README.md b/README.md index c90abe1..cf017c3 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ STANALONE - copy paste the files to your own project: with `patchwork` support ``` +# Add ggplot2 and patchwork as your package dependencies # copy paste the ggcall.R file to your own package R directory # copy paste the patchwork.R file to your own package R directory ``` diff --git a/man/eval_ggcall.Rd b/man/eval_ggcall.Rd index 39dfaef..0582ce3 100644 --- a/man/eval_ggcall.Rd +++ b/man/eval_ggcall.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ggcall.R +% Please edit documentation in R/standalone-ggcall.R \name{eval_ggcall} \alias{eval_ggcall} \title{Evaluate ggcall} diff --git a/man/ggcall.Rd b/man/ggcall.Rd index 44f3d46..71259cd 100644 --- a/man/ggcall.Rd +++ b/man/ggcall.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ggcall.R +% Please edit documentation in R/standalone-ggcall.R \name{ggcall} \alias{ggcall} \title{Retrieve Construction Call from ggplot Object} diff --git a/man/ggcall_add_assignments.Rd b/man/ggcall_add_assignments.Rd index 4a9334a..fa31694 100644 --- a/man/ggcall_add_assignments.Rd +++ b/man/ggcall_add_assignments.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ggcall.R +% Please edit documentation in R/standalone-ggcall.R \name{ggcall_add_assignments} \alias{ggcall_add_assignments} \title{Add Assignments to ggplot Construction Code} diff --git a/man/ggcall_env.Rd b/man/ggcall_env.Rd index 552c209..432e599 100644 --- a/man/ggcall_env.Rd +++ b/man/ggcall_env.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ggcall.R +% Please edit documentation in R/standalone-ggcall.R \name{ggcall_env} \alias{ggcall_env} \title{Retrieve Environment from ggcall} diff --git a/man/ggplot.Rd b/man/ggplot.Rd index 384f373..e530321 100644 --- a/man/ggplot.Rd +++ b/man/ggplot.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ggcall.R +% Please edit documentation in R/standalone-ggcall.R \name{ggplot} \alias{ggplot} \title{Enhanced `ggplot` Function with History Tracking} diff --git a/man/plus-.gg.Rd b/man/plus-.gg.Rd index e382236..0852bfc 100644 --- a/man/plus-.gg.Rd +++ b/man/plus-.gg.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ggcall.R +% Please edit documentation in R/standalone-ggcall.R \name{+.gg} \alias{+.gg} \title{Custom '+' Operator for ggcall Objects} diff --git a/vignettes/ggcall.Rmd b/vignettes/ggcall.Rmd index 3e1fed5..abe2a5c 100644 --- a/vignettes/ggcall.Rmd +++ b/vignettes/ggcall.Rmd @@ -191,7 +191,7 @@ The ggcall can be implemented in a few ways to your package, direct dependency o `usethis` package is optional but it truly simplify the process. -The `usethis` >= 2.2.0 is required. +The `usethis` >= 2.2.0 is required to use `usethis::use_standalone`. ``` install.packages("usethis") @@ -236,7 +236,7 @@ with `patchwork` support ```r usethis::use_package("ggplot2") usethis::use_package("patchwork") -usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.4") +usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.3") # you may need to update the file time to time ``` From c5a414501b8493dfa780838385e6671009e562f1 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 13:41:24 +0100 Subject: [PATCH 14/29] edit docs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cf017c3..58170d1 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ with `patchwork` support ```r usethis::use_package("ggplot2") usethis::use_package("patchwork") +usethis::use_package("styler", "Suggests") usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.4") # you may need to update the file time to time ``` @@ -87,6 +88,7 @@ without `patchwork` support ``` usethis::use_package("ggplot2") +usethis::use_package("styler", "Suggests") usethis::use_standalone("polkas/ggcall", "ggcall.R", ref = "v0.3.3") # you may need to update the file time to time ``` From c34c728e1ad2756f60d37ca261798bd373c8f044 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 16:58:14 +0100 Subject: [PATCH 15/29] improve --- DESCRIPTION | 5 +-- NAMESPACE | 1 + R/standalone-ggcall.R | 7 +++-- R/standalone-patchwork.R | 47 +++++++++++++++++++++++++++- README.md | 37 +++++++--------------- man/ggcall-operators.Rd | 59 +++++++++++++++++++++++++++++++++++ man/ggcall_add_assignments.Rd | 4 ++- man/ggcall_env.Rd | 3 +- man/patch_operator_base.Rd | 24 ++++++++++++++ 9 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 man/ggcall-operators.Rd create mode 100644 man/patch_operator_base.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 579e112..83d25a8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,12 +16,13 @@ BugReports: https://github.com/Polkas/ggcall/issues License: Apache License (>= 2) Encoding: UTF-8 LazyData: true -Depends: +Depends: ggplot2 +Imports: + patchwork Suggests: knitr, usethis, - patchwork, rmarkdown, styler, testthat (>= 3.0.0) diff --git a/NAMESPACE b/NAMESPACE index e1b298a..54ffc15 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -12,3 +12,4 @@ export(ggcall_add_assignments) export(ggcall_env) export(ggplot) import(ggplot2, except = c(ggplot)) +import(patchwork) diff --git a/R/standalone-ggcall.R b/R/standalone-ggcall.R index 6fb884b..3a5e8bc 100644 --- a/R/standalone-ggcall.R +++ b/R/standalone-ggcall.R @@ -190,7 +190,9 @@ ggcall <- function(plot) { #' eval_ggcall(plot_call_with_assignments) #' #' # Will Fail as data is needed and skipped -#' # eval_ggcall(ggcall_add_assignments(plot_call, vars = c("x", "y"))) +#' \dontrun{ +#' eval_ggcall(ggcall_add_assignments(plot_call, vars = c("x", "y"))) +#' } #' @export ggcall_add_assignments <- function(call, vars = extract_names(call)) { stopifnot(inherits(call, "ggcall_code")) @@ -282,7 +284,8 @@ eval_ggcall <- function(call, ...) { #' ggplot(data, aes(x = !!as.name(x), y = !!as.name(y))) + #' geom_point() #' } -#' plot_call <- ggcall(fun(mtcars, "wt", "mpg")) +#' gplot <- fun(mtcars, "wt", "mpg") +#' plot_call <- ggcall(gplot) #' env <- ggcall_env(plot_call) #' ls(env) #' env[["data"]] diff --git a/R/standalone-patchwork.R b/R/standalone-patchwork.R index 7513e66..38278b3 100644 --- a/R/standalone-patchwork.R +++ b/R/standalone-patchwork.R @@ -4,7 +4,8 @@ # last-updated: 2024-11-21 # license: https://unlicense.org # dependencies: ggcall.R -# depends: [ggplot2, patchwork] +# depends:ggplot2 +# imports: patchwork # --- # # This file provides a minimal shim to provide a ggcall functionality on top of @@ -15,6 +16,14 @@ # nocov start +#' @title Base Function for Patchwork Operators +#' @description A helper function that applies patchwork operators to `ggcall` objects. +#' @param e1 The left-hand side `ggcall` object. +#' @param e2 The right-hand side `ggcall` object. +#' @param operator The operator as a string (e.g., "-", "/", "|", "*", "&"). +#' @param class The class to which the operator is applied ("ggplot" or "gg"). +#' @return A combined `ggcall` object resulting from the operation. +#' @import patchwork #' @keywords internal patch_operator_base <- function(e1, e2, operator, class) { if (!requireNamespace("patchwork", quietly = TRUE)) { @@ -31,29 +40,65 @@ patch_operator_base <- function(e1, e2, operator, class) { plot } +#' @title Minus Operator for ggcall Objects +#' @description Applies the minus operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. +#' @details This function allows for the subtraction of `ggcall` objects using the `patchwork` syntax. +#' @inheritParams patch_operator_base +#' @return A combined `ggcall` object after applying the minus operation. +#' @keywords internal +#' @rdname ggcall-operators #' @export "-.ggcall" <- function(e1, e2) { patch_operator_base(e1, e2, "-", "ggplot") } +#' @title Division Operator for ggcall Objects +#' @description Applies the division operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. +#' @details This function allows for the division of `ggcall` objects using the `patchwork` syntax. +#' @inheritParams patch_operator_base +#' @return A combined `ggcall` object after applying the division operation. +#' @keywords internal +#' @rdname ggcall-operators #' @export "/.ggcall" <- function(e1, e2) { patch_operator_base(e1, e2, "/", "ggplot") } +#' @title Or Operator for ggcall Objects +#' @description Applies the or operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. +#' @details This function allows for the combination of `ggcall` objects using the `patchwork` syntax. +#' @inheritParams patch_operator_base +#' @return A combined `ggcall` object after applying the or operation. +#' @keywords internal +#' @rdname ggcall-operators #' @export "|.ggcall" <- function(e1, e2) { patch_operator_base(e1, e2, "|", "ggplot") } +#' @title Multiplication Operator for ggcall Objects +#' @description Applies the multiplication operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. +#' @details This function allows for the multiplication of `ggcall` objects using the `patchwork` syntax. +#' @inheritParams patch_operator_base +#' @return A combined `ggcall` object after applying the multiplication operation. +#' @keywords internal +#' @rdname ggcall-operators #' @export "*.ggcall" <- function(e1, e2) { patch_operator_base(e1, e2, "*", "gg") } +#' @title And Operator for ggcall Objects +#' @description Applies the and operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. +#' @details This function allows for the combination of `ggcall` objects using the `patchwork` syntax. +#' @inheritParams patch_operator_base +#' @return A combined `ggcall` object after applying the and operation. +#' @keywords internal +#' @rdname ggcall-operators #' @export "&.ggcall" <- function(e1, e2) { patch_operator_base(e1, e2, "&", "gg") } + # nocov end diff --git a/README.md b/README.md index 58170d1..bdabc23 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,14 @@ Then, each template will generate the expected plot, and the ggplot2 code behind Please access the [Get Started vignette](https://polkas.github.io/ggcall/articles/ggcall.html) for more information. -## Developers Implementation +## Implementation -The ggcall can be implemented in a few ways to your package, direct dependency or standalone. +The ggcall can be implemented as a standalone solution. A "standalone" file implements a minimum set of functionality in such a way that it can be copied into another package. `usethis::use_standalone()` makes it easy to get such a file into your own repo/package. +[Example of standalone file in another package, rlang](https://github.com/r-lib/rlang/blob/main/R/standalone-purrr.R) -`usethis` package is optional but it truly simplify the process. The `usethis` >= 2.2.0 is required. ``` @@ -44,22 +44,8 @@ GENERAL COMMENTS: ``` ``` -# DO NOT import ggplot function from ggplot2 instead import it from ggcall - -# a. When importing all ggplot2 functions -# #' @rawNamespace import(ggplot2, except = c(ggplot)) -# #' @import ggcall -# b. When importing specific ggplot2 functions -# #' @importFrom ggplot2 geom_line -# #' @importFrom ggcall ggplot -``` - -DIRECT DEPENDENCY: - -An option is to use the ggcall as a DESCRIPTION file import dependency for your package. - -``` -usethis::use_package("ggcall") +# DO NOT import ggplot function from ggplot2 +#' @rawNamespace import(ggplot2, except = c(ggplot)) ``` STANALONE - copy paste the files to your own project: @@ -67,30 +53,31 @@ STANALONE - copy paste the files to your own project: with `patchwork` support ``` -# Add ggplot2 and patchwork as your package dependencies +# Add ggplot2 (Depends), patchwork (Imports) and styler (Suggests) as your package dependencies # copy paste the ggcall.R file to your own package R directory # copy paste the patchwork.R file to your own package R directory ``` ```r -usethis::use_package("ggplot2") +usethis::use_package("ggplot2", type = "Depends") usethis::use_package("patchwork") usethis::use_package("styler", "Suggests") -usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.4") -# you may need to update the file time to time +usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.3") +# you may need to update the files time to time, run usethis::use_standalone ``` without `patchwork` support ``` +# Add ggplot2 (Depends) and styler (Suggests) as your package dependencies # copy paste the ggcall.R file to your own package R directory ``` ``` -usethis::use_package("ggplot2") +usethis::use_package("ggplot2", type = "Depends") usethis::use_package("styler", "Suggests") usethis::use_standalone("polkas/ggcall", "ggcall.R", ref = "v0.3.3") -# you may need to update the file time to time +# you may need to update the file time to time, run usethis::use_standalone ``` ## Usage diff --git a/man/ggcall-operators.Rd b/man/ggcall-operators.Rd new file mode 100644 index 0000000..31649ce --- /dev/null +++ b/man/ggcall-operators.Rd @@ -0,0 +1,59 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/standalone-patchwork.R +\name{-.ggcall} +\alias{-.ggcall} +\alias{/.ggcall} +\alias{|.ggcall} +\alias{*.ggcall} +\alias{&.ggcall} +\title{Minus Operator for ggcall Objects} +\usage{ +\method{-}{ggcall}(e1, e2) + +\method{/}{ggcall}(e1, e2) + +\method{|}{ggcall}(e1, e2) + +\method{*}{ggcall}(e1, e2) + +\method{&}{ggcall}(e1, e2) +} +\arguments{ +\item{e1}{The left-hand side `ggcall` object.} + +\item{e2}{The right-hand side `ggcall` object.} +} +\value{ +A combined `ggcall` object after applying the minus operation. + +A combined `ggcall` object after applying the division operation. + +A combined `ggcall` object after applying the or operation. + +A combined `ggcall` object after applying the multiplication operation. + +A combined `ggcall` object after applying the and operation. +} +\description{ +Applies the minus operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. + +Applies the division operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. + +Applies the or operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. + +Applies the multiplication operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. + +Applies the and operator to `ggcall` objects, utilizing the corresponding operator from the `patchwork` package. +} +\details{ +This function allows for the subtraction of `ggcall` objects using the `patchwork` syntax. + +This function allows for the division of `ggcall` objects using the `patchwork` syntax. + +This function allows for the combination of `ggcall` objects using the `patchwork` syntax. + +This function allows for the multiplication of `ggcall` objects using the `patchwork` syntax. + +This function allows for the combination of `ggcall` objects using the `patchwork` syntax. +} +\keyword{internal} diff --git a/man/ggcall_add_assignments.Rd b/man/ggcall_add_assignments.Rd index fa31694..0901b54 100644 --- a/man/ggcall_add_assignments.Rd +++ b/man/ggcall_add_assignments.Rd @@ -57,5 +57,7 @@ styler::style_text( eval_ggcall(plot_call_with_assignments) # Will Fail as data is needed and skipped -# eval_ggcall(ggcall_add_assignments(plot_call, vars = c("x", "y"))) +\dontrun{ + eval_ggcall(ggcall_add_assignments(plot_call, vars = c("x", "y"))) +} } diff --git a/man/ggcall_env.Rd b/man/ggcall_env.Rd index 432e599..168b006 100644 --- a/man/ggcall_env.Rd +++ b/man/ggcall_env.Rd @@ -22,7 +22,8 @@ fun <- function(data, x, y) { ggplot(data, aes(x = !!as.name(x), y = !!as.name(y))) + geom_point() } -plot_call <- ggcall(fun(mtcars, "wt", "mpg")) +gplot <- fun(mtcars, "wt", "mpg") +plot_call <- ggcall(gplot) env <- ggcall_env(plot_call) ls(env) env[["data"]] diff --git a/man/patch_operator_base.Rd b/man/patch_operator_base.Rd new file mode 100644 index 0000000..388a7d9 --- /dev/null +++ b/man/patch_operator_base.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/standalone-patchwork.R +\name{patch_operator_base} +\alias{patch_operator_base} +\title{Base Function for Patchwork Operators} +\usage{ +patch_operator_base(e1, e2, operator, class) +} +\arguments{ +\item{e1}{The left-hand side `ggcall` object.} + +\item{e2}{The right-hand side `ggcall` object.} + +\item{operator}{The operator as a string (e.g., "-", "/", "|", "*", "&").} + +\item{class}{The class to which the operator is applied ("ggplot" or "gg").} +} +\value{ +A combined `ggcall` object resulting from the operation. +} +\description{ +A helper function that applies patchwork operators to `ggcall` objects. +} +\keyword{internal} From 2fd4a2f66bb13042c403a13f1ad36e547485267d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:59:01 +0000 Subject: [PATCH 16/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/standalone-ggcall.R | 2 +- inst/WORDLIST | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/R/standalone-ggcall.R b/R/standalone-ggcall.R index 3a5e8bc..4280b74 100644 --- a/R/standalone-ggcall.R +++ b/R/standalone-ggcall.R @@ -191,7 +191,7 @@ ggcall <- function(plot) { #' #' # Will Fail as data is needed and skipped #' \dontrun{ -#' eval_ggcall(ggcall_add_assignments(plot_call, vars = c("x", "y"))) +#' eval_ggcall(ggcall_add_assignments(plot_call, vars = c("x", "y"))) #' } #' @export ggcall_add_assignments <- function(call, vars = extract_names(call)) { diff --git a/inst/WORDLIST b/inst/WORDLIST index b4d3e37..6276e7d 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -19,6 +19,7 @@ eamodio env forwardPorts FRONTEND +gg ggally GGally ggcall @@ -82,6 +83,7 @@ remoteUser repo Reproducibility reproducibility +rlang rmarkdown RoxygenNote rsync From 9f23561831bb3f191ed9ac061107286b3bd67a2a Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 17:00:56 +0100 Subject: [PATCH 17/29] improve --- R/standalone-patchwork.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/standalone-patchwork.R b/R/standalone-patchwork.R index 38278b3..1efd631 100644 --- a/R/standalone-patchwork.R +++ b/R/standalone-patchwork.R @@ -4,7 +4,7 @@ # last-updated: 2024-11-21 # license: https://unlicense.org # dependencies: ggcall.R -# depends:ggplot2 +# depends: ggplot2 # imports: patchwork # --- # From b9207ff22fa49b29bb3dfb29a00c69a78fbdebfb Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 22:10:55 +0100 Subject: [PATCH 18/29] update --- DESCRIPTION | 5 +- NAMESPACE | 2 + R/barbell-plot.R | 64 ++++ R/forest-plot.R | 52 +++ R/standalone-ggcall.R | 21 +- R/standalone-patchwork.R | 3 +- README.md | 157 ++++----- man/barbell_plot.Rd | 48 +++ man/eval_ggcall.Rd | 11 +- man/forest_plot.Rd | 49 +++ man/{plus-.gg.Rd => ggcall-add-operator.Rd} | 3 +- man/ggcall.Rd | 1 + man/ggcall_add_assignments.Rd | 3 +- man/ggcall_env.Rd | 1 + man/ggplot.Rd | 1 + tests/testthat/test-barbell-plot.R | 127 +++++++ tests/testthat/test-forest-plot.R | 122 +++++++ vignettes/.gitignore | 2 - vignettes/ggcall.Rmd | 355 -------------------- 19 files changed, 558 insertions(+), 469 deletions(-) create mode 100644 R/barbell-plot.R create mode 100644 R/forest-plot.R create mode 100644 man/barbell_plot.Rd create mode 100644 man/forest_plot.Rd rename man/{plus-.gg.Rd => ggcall-add-operator.Rd} (89%) create mode 100644 tests/testthat/test-barbell-plot.R create mode 100644 tests/testthat/test-forest-plot.R delete mode 100644 vignettes/.gitignore delete mode 100644 vignettes/ggcall.Rmd diff --git a/DESCRIPTION b/DESCRIPTION index 83d25a8..bbe4fec 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,10 +16,9 @@ BugReports: https://github.com/Polkas/ggcall/issues License: Apache License (>= 2) Encoding: UTF-8 LazyData: true -Depends: - ggplot2 Imports: - patchwork + patchwork, + ggplot2 Suggests: knitr, usethis, diff --git a/NAMESPACE b/NAMESPACE index 54ffc15..4eb6ede 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,7 +6,9 @@ S3method("+",gg) S3method("-",ggcall) S3method("/",ggcall) S3method("|",ggcall) +export(barbell_plot) export(eval_ggcall) +export(forest_plot) export(ggcall) export(ggcall_add_assignments) export(ggcall_env) diff --git a/R/barbell-plot.R b/R/barbell-plot.R new file mode 100644 index 0000000..48e00ea --- /dev/null +++ b/R/barbell-plot.R @@ -0,0 +1,64 @@ +#' @title General Barbell Plot Function +#' @description Creates a barbell plot from a data frame containing two sets of values to compare across categories. +#' @param barbell_data A data frame containing the data for the barbell plot. +#' @param category_col The name of the column containing the category labels (as a string). +#' @param value1_col The name of the first value column to compare (as a string). +#' @param value2_col The name of the second value column to compare (as a string). +#' @param group_labels A character vector of length 2 providing labels for the two values. Defaults to `c("Value 1", "Value 2")`. +#' @param xlab The label for the x-axis. Defaults to "Value". +#' @param title The title of the plot. Defaults to "Barbell Plot". +#' @return A ggplot object representing the barbell plot. +#' @examples +#' # Example data +#' df <- data.frame( +#' Category = c("A", "B", "C", "D"), +#' Before = c(3.5, 4.2, 2.8, 5.1), +#' After = c(4.0, 4.5, 3.1, 5.5) +#' ) +#' +#' # Create the barbell plot +#' barbell_plot(df, "Category", "Before", "After", group_labels = c("Before", "After")) +#' @export +barbell_plot <- function(barbell_data, category_col, value1_col, value2_col, + group_labels = c("Value 1", "Value 2"), + xlab = "Value", title = "Barbell Plot") { + + required_cols <- c(category_col, value1_col, value2_col) + missing_cols <- setdiff(required_cols, names(barbell_data)) + if (length(missing_cols) > 0) { + stop("The following required columns are missing in 'data': ", paste(missing_cols, collapse = ", ")) + } + + numeric_cols <- c(value1_col, value2_col) + for (col in numeric_cols) { + if (!is.numeric(barbell_data[[col]])) { + stop("Column '", col, "' must be numeric.") + } + } + + if (length(group_labels) != 2) { + stop("Parameter 'group_labels' must be a character vector of length 2.") + } + + data_long <- data.frame( + Category = rep(barbell_data[[category_col]], 2), + Value = c(barbell_data[[value1_col]], barbell_data[[value2_col]]), + Group = rep(group_labels, each = nrow(barbell_data)) + ) + + data_long$Category <- factor(data_long$Category, levels = unique(barbell_data[[category_col]])) + + barbell_title <- title + + ggplot(data_long, aes(x = .data[["Value"]], y = .data[["Category"]], group = .data[["Category"]])) + + geom_line(aes(group = .data[["Category"]]), color = "gray70", linewidth = 1) + + geom_point(aes(color = .data[["Group"]]), size = 4) + + scale_color_manual(values = c("#00BFC4", "#F8766D"), name = "") + + labs(x = xlab, y = "", title = barbell_title) + + theme_minimal() + + theme( + axis.text.y = element_text(size = 10), + plot.title = element_text(hjust = 0.5, face = "bold"), + legend.position = "bottom" + ) +} diff --git a/R/forest-plot.R b/R/forest-plot.R new file mode 100644 index 0000000..e57afee --- /dev/null +++ b/R/forest-plot.R @@ -0,0 +1,52 @@ +#' @title Forest Plot Function +#' @description Creates a forest plot from a data frame containing estimates and confidence intervals. +#' @param forest_data A data frame containing the data for the forest plot. +#' @param estimate_col The name of the column containing the point estimates (as a string). +#' @param ci_lower_col The name of the column containing the lower bounds of the confidence intervals (as a string). +#' @param ci_upper_col The name of the column containing the upper bounds of the confidence intervals (as a string). +#' @param label_col The name of the column containing the labels for each estimate (as a string). +#' @param xlab The label for the x-axis. Defaults to "Estimate". +#' @param title The title of the plot. Defaults to "Forest Plot". +#' @return A ggplot object representing the forest plot. +#' @examples +#' # Example data +#' df <- data.frame( +#' Treatment = c("Treatment A", "Treatment B", "Treatment C"), +#' Estimate = c(0.2, 0.5, -0.1), +#' CI_lower = c(0.1, 0.3, -0.3), +#' CI_upper = c(0.3, 0.7, 0.1) +#' ) +#' +#' # Create the forest plot +#' forest_plot(df, "Estimate", "CI_lower", "CI_upper", "Treatment") +#' @export +forest_plot <- function(forest_data, estimate_col, ci_lower_col, ci_upper_col, label_col, + xlab = "Estimate", title = "Forest Plot") { + + required_cols <- c(estimate_col, ci_lower_col, ci_upper_col, label_col) + missing_cols <- setdiff(required_cols, names(forest_data)) + if (length(missing_cols) > 0) { + stop("The following required columns are missing in 'data': ", paste(missing_cols, collapse = ", ")) + } + + numeric_cols <- c(estimate_col, ci_lower_col, ci_upper_col) + for (col in numeric_cols) { + if (!is.numeric(forest_data[[col]])) { + stop("Column '", col, "' must be numeric.") + } + } + + forest_data[[label_col]] <- factor(forest_data[[label_col]], levels = unique(forest_data[[label_col]])) + + forest_title <- title + + ggplot(forest_data, aes(x = .data[[estimate_col]], y = .data[[label_col]])) + + geom_point(size = 3) + + geom_errorbarh(aes(xmin = .data[[ci_lower_col]], xmax = .data[[ci_upper_col]]), height = 0.2) + + labs(x = xlab, y = "", title = forest_title) + + theme_minimal() + + theme( + axis.text.y = element_text(size = 10), + plot.title = element_text(hjust = 0.5, face = "bold") + ) +} diff --git a/R/standalone-ggcall.R b/R/standalone-ggcall.R index 4280b74..5008d0e 100644 --- a/R/standalone-ggcall.R +++ b/R/standalone-ggcall.R @@ -3,7 +3,7 @@ # file: ggcall.R # last-updated: 2024-11-21 # license: https://unlicense.org -# depends: ggplot2 +# imports: ggplot2 # --- # # This file provides a minimal shim to provide a ggcall functionality on top of @@ -28,6 +28,7 @@ #' @seealso \code{\link[ggplot2]{ggplot}} #' @rawNamespace import(ggplot2, except = c(ggplot)) #' @examples +#' library(ggplot2, exclude = "ggplot") #' p <- ggplot(mtcars, aes(x = wt, y = mpg)) #' # the + function has to come from ggcall package #' attr(p + geom_point(), "ggcall") @@ -54,10 +55,12 @@ ggplot <- function(...) { #' conjunction with the enhanced ggplot function provided by this package. #' #' @param e1 A ggplot object of class 'ggcall'. -#' @param e2 A layer or theme to add to the ggplot object. +#' @param e2 A layer, theme or ggcall to add. #' #' @return A modified ggplot object with updated plot history. +#' @rdname ggcall-add-operator #' @examples +#' library(ggplot2, exclude = "ggplot") #' p <- ggplot(mtcars, aes(x = wt, y = mpg)) + #' geom_point() #' attr(p, "ggcall") # View the plot call @@ -108,6 +111,7 @@ ggplot <- function(...) { #' a list representing the history of the ggplot object. #' #' @examples +#' library(ggplot2, exclude = "ggplot") #' # Example: Create a function which combines a few ggplot layers #' # Typically, it will be a function from your R package where you implemented ggcall #' func <- function(data, x, y, bool = TRUE) { @@ -162,6 +166,7 @@ ggcall <- function(plot) { #' More complex variables are referenced to ggcall environment. #' #' @examples +#' library(ggplot2, exclude = "ggplot") #' # Example: Create a function which combines a few ggplot layers #' # Typically, it will be a function from your R package where you implemented ggcall #' func <- function(data, x, y, bool = TRUE) { @@ -253,9 +258,14 @@ ggcall_add_assignments <- function(call, vars = extract_names(call)) { #' @return The resulting ggplot object produced by evaluating the expression `x`. #' #' @examples -#' p <- ggplot(mtcars, aes(x = wt, y = mpg)) + -#' geom_point() -#' plot_call <- ggcall(p) +#' library(ggplot2, exclude = "ggplot") +#' +#' func <- function() { +#' ggplot(mtcars, aes(x = wt, y = mpg)) + +#' geom_point() +#' } +#' gplot <- func() +#' plot_call <- ggcall(gplot) #' reconstructed_plot <- eval_ggcall(plot_call) #' print(reconstructed_plot) #' @@ -280,6 +290,7 @@ eval_ggcall <- function(call, ...) { #' @param call An expression representing the ggplot construction code. #' @return The environment in which the ggplot construction code was created. #' @examples +#' library(ggplot2, exclude = "ggplot") #' fun <- function(data, x, y) { #' ggplot(data, aes(x = !!as.name(x), y = !!as.name(y))) + #' geom_point() diff --git a/R/standalone-patchwork.R b/R/standalone-patchwork.R index 1efd631..260ed0a 100644 --- a/R/standalone-patchwork.R +++ b/R/standalone-patchwork.R @@ -4,8 +4,7 @@ # last-updated: 2024-11-21 # license: https://unlicense.org # dependencies: ggcall.R -# depends: ggplot2 -# imports: patchwork +# imports: [ggplot2, patchwork] # --- # # This file provides a minimal shim to provide a ggcall functionality on top of diff --git a/README.md b/README.md index bdabc23..b865a14 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,57 @@ among others, providing mathematical operators for combining multiple plots. An excellent implementation example is to create a bunch of ggplot templates, and we want them to be functions. Then, each template will generate the expected plot, and the ggplot2 code behind is easy to get. -## Details +## Example -Please access the [Get Started vignette](https://polkas.github.io/ggcall/articles/ggcall.html) for more information. +`forest_plot` and `barbell` functions are a part of `ggcall` package. +Typically, it will be a function returning `ggplot2` object from your own R package where you implemented `ggcall`. -## Implementation +```r +remotes::install_github("https://github.com/Polkas/ggcall") +library(ggcall) + +# Print the body of the function +forest_plot + +df <- data.frame( + Treatment = c("Treatment A", "Treatment B", "Treatment C"), + Estimate = c(0.2, 0.5, -0.1), + CI_lower = c(0.1, 0.3, -0.3), + CI_upper = c(0.3, 0.7, 0.1) +) + +# Call the function, gg_plot is a ggplot object +gg_forest <- forest_plot(df, "Estimate", "CI_lower", "CI_upper", "Treatment") +gg_plot + +# Retrieve the plot construction code +call_forest <- ggcall(gg_plot) + +# Optionally: Style the code with styler +# install.packages("styler") +styler::style_text(paste(deparse(call_forest), collapse = "\n")) + +# Optionally: add assignments to call +styler::style_text(paste(deparse(ggcall_add_assignments(call_forest)), collapse = "\n")) + +# Optionally: reevaulate the call +eval_ggcall(call_forest) + +df <- data.frame( + Category = c("A", "B", "C", "D"), + Before = c(3.5, 4.2, 2.8, 5.1), + After = c(4.0, 4.5, 3.1, 5.5) +) + +gg_barbell <- barbell_plot(df, "Category", "Before", "After", group_labels = c("Before", "After")) + +call_barbell <- ggcall(gg_barbell) + +eval_ggcall(gg_barbell) +``` + +## Implementation in Your Own Package The ggcall can be implemented as a standalone solution. @@ -35,30 +80,21 @@ The `usethis` >= 2.2.0 is required. install.packages("usethis") ``` -GENERAL COMMENTS: +STANALONE - copy paste the files and add dependencies to your own package: -``` -# Apply only if needed -# In your own code remove all ggplot2:: prefix-ing before ggplot function calls -# ggplot2::ggplot(...) -> ggplot(...) -``` +Please create an R package if not having such yet. ``` -# DO NOT import ggplot function from ggplot2 -#' @rawNamespace import(ggplot2, except = c(ggplot)) +usethis::create_package() ``` -STANALONE - copy paste the files to your own project: - with `patchwork` support ``` # Add ggplot2 (Depends), patchwork (Imports) and styler (Suggests) as your package dependencies # copy paste the ggcall.R file to your own package R directory # copy paste the patchwork.R file to your own package R directory -``` -```r usethis::use_package("ggplot2", type = "Depends") usethis::use_package("patchwork") usethis::use_package("styler", "Suggests") @@ -71,98 +107,25 @@ without `patchwork` support ``` # Add ggplot2 (Depends) and styler (Suggests) as your package dependencies # copy paste the ggcall.R file to your own package R directory -``` -``` usethis::use_package("ggplot2", type = "Depends") usethis::use_package("styler", "Suggests") usethis::use_standalone("polkas/ggcall", "ggcall.R", ref = "v0.3.3") # you may need to update the file time to time, run usethis::use_standalone ``` -## Usage - -Imagine using a package or function that generates a complex `ggplot2` visualization. -Then, the ggplot code used to create the plot is not exposed. -`ggcall` overcomes this barrier by extracting the hidden code, making it accessible for examination and modification. - -Here is a **simple** illustrative example with a scenario in which a function generates a `ggplot2` plot based on input data: - -```r -remotes::install_github("https://github.com/Polkas/ggcall") -library(ggcall) - -# Example: Create a function which combines a few ggplot layers -# Typically, it will be a function from your R package where you implemented ggcall -create_custom_plot <- function(data, x, y, bool = TRUE) { - # layers have to be added with + - gg <- ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + - geom_point(alpha = 0.4) + - facet_grid(~gear) - - if (bool) { - gg <- gg + theme(axis.title.x = element_blank()) - } - - func_internal <- function(gg) { - gg + labs(title = "custom title") - } - - func_internal(gg) -} - -# gg_plot is a ggplot object -gg_plot <- create_custom_plot(mtcars, "wt", "mpg") -print(gg_plot) -# Retrieve the plot construction code -plot_call <- ggcall(gg_plot) -plot_call -# ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + geom_point(alpha = 0.4) + -# facet_grid(~gear) + theme(axis.title.x = element_blank()) + -# labs(title = "custom title") -# attr(,"class") -# [1] "ggcall_code" -# attr(,"ggcall_env") -# - -# Optionally: Style the code with styler -# install.packages("styler") -styler::style_text( - paste(deparse(plot_call), collapse = "\n") -) - -# Optionally: add assignments to call -plot_call_with_assignments <- ggcall_add_assignments(plot_call) -styler::style_text( - paste(deparse(plot_call_with_assignments), collapse = "\n") -) - -# Optionally: access call environment -# Access call environment and/or use it to evaluate the call -plot_call_env <- ggcall_env(plot_call) -as.list(plot_call_env) - -# Optionally: reevaulate the call -# Reproduce the plot by evaluating the code -eval_ggcall(plot_call) -eval_ggcall(plot_call_with_assignments) +GENERAL COMMENTS: -# Optionally overwrite variables -eval_ggcall(plot_call, mtcars = mtcars[1:10, ], x = "disp") +``` +# Apply only if needed +# In your own code remove all ggplot2:: prefix-ing before ggplot function calls +# ggplot2::ggplot(...) -> ggplot(...) ``` -Functions Reference: - -| Function | Description | -|-------------------------|-------------------------------------------------------------------------------------| -| `ggplot` | **Overrides the default `ggplot` function from the ggplot2 package, adding the capability to track the history of plot construction.**| -| `+.gg` | **Enhances the '+' operator for ggplot objects to track the history of plot layers and modifications.** | -| `ggcall` | **Extracts the complete history of a ggplot object's construction, providing a way to reproduce or inspect the plot.**| -| `ggcall_add_assignments`| **Modifies a `ggcall()` object by adding variable assignments to it.**| -| `eval_ggcall` | **Evaluates an expression representing a ggplot construction code.**| -| `ggcall_env` | **Extracts the environment in which the ggplot construction code was originally created.**| -|`+`, `-`, `*`, `\|`, `&` and `/` | **Overloaded patchwork operators**| - +``` +# DO NOT import ggplot function from ggplot2 +#' @rawNamespace import(ggplot2, except = c(ggplot)) +``` ## Note diff --git a/man/barbell_plot.Rd b/man/barbell_plot.Rd new file mode 100644 index 0000000..fee75da --- /dev/null +++ b/man/barbell_plot.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/barbell-plot.R +\name{barbell_plot} +\alias{barbell_plot} +\title{General Barbell Plot Function} +\usage{ +barbell_plot( + barbell_data, + category_col, + value1_col, + value2_col, + group_labels = c("Value 1", "Value 2"), + xlab = "Value", + title = "Barbell Plot" +) +} +\arguments{ +\item{barbell_data}{A data frame containing the data for the barbell plot.} + +\item{category_col}{The name of the column containing the category labels (as a string).} + +\item{value1_col}{The name of the first value column to compare (as a string).} + +\item{value2_col}{The name of the second value column to compare (as a string).} + +\item{group_labels}{A character vector of length 2 providing labels for the two values. Defaults to `c("Value 1", "Value 2")`.} + +\item{xlab}{The label for the x-axis. Defaults to "Value".} + +\item{title}{The title of the plot. Defaults to "Barbell Plot".} +} +\value{ +A ggplot object representing the barbell plot. +} +\description{ +Creates a barbell plot from a data frame containing two sets of values to compare across categories. +} +\examples{ +# Example data +df <- data.frame( + Category = c("A", "B", "C", "D"), + Before = c(3.5, 4.2, 2.8, 5.1), + After = c(4.0, 4.5, 3.1, 5.5) +) + +# Create the barbell plot +barbell_plot(df, "Category", "Before", "After", group_labels = c("Before", "After")) +} diff --git a/man/eval_ggcall.Rd b/man/eval_ggcall.Rd index 0582ce3..5ca199e 100644 --- a/man/eval_ggcall.Rd +++ b/man/eval_ggcall.Rd @@ -23,9 +23,14 @@ It specifically uses the environment stored in the 'ggcall_env' attribute of the expression, ensuring that the plot is reconstructed in the correct context. } \examples{ -p <- ggplot(mtcars, aes(x = wt, y = mpg)) + - geom_point() -plot_call <- ggcall(p) +library(ggplot2, exclude = "ggplot") + +func <- function() { + ggplot(mtcars, aes(x = wt, y = mpg)) + + geom_point() +} +gplot <- func() +plot_call <- ggcall(gplot) reconstructed_plot <- eval_ggcall(plot_call) print(reconstructed_plot) diff --git a/man/forest_plot.Rd b/man/forest_plot.Rd new file mode 100644 index 0000000..41f1b9d --- /dev/null +++ b/man/forest_plot.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/forest-plot.R +\name{forest_plot} +\alias{forest_plot} +\title{Forest Plot Function} +\usage{ +forest_plot( + forest_data, + estimate_col, + ci_lower_col, + ci_upper_col, + label_col, + xlab = "Estimate", + title = "Forest Plot" +) +} +\arguments{ +\item{forest_data}{A data frame containing the data for the forest plot.} + +\item{estimate_col}{The name of the column containing the point estimates (as a string).} + +\item{ci_lower_col}{The name of the column containing the lower bounds of the confidence intervals (as a string).} + +\item{ci_upper_col}{The name of the column containing the upper bounds of the confidence intervals (as a string).} + +\item{label_col}{The name of the column containing the labels for each estimate (as a string).} + +\item{xlab}{The label for the x-axis. Defaults to "Estimate".} + +\item{title}{The title of the plot. Defaults to "Forest Plot".} +} +\value{ +A ggplot object representing the forest plot. +} +\description{ +Creates a forest plot from a data frame containing estimates and confidence intervals. +} +\examples{ +# Example data +df <- data.frame( + Treatment = c("Treatment A", "Treatment B", "Treatment C"), + Estimate = c(0.2, 0.5, -0.1), + CI_lower = c(0.1, 0.3, -0.3), + CI_upper = c(0.3, 0.7, 0.1) +) + +# Create the forest plot +forest_plot(df, "Estimate", "CI_lower", "CI_upper", "Treatment") +} diff --git a/man/plus-.gg.Rd b/man/ggcall-add-operator.Rd similarity index 89% rename from man/plus-.gg.Rd rename to man/ggcall-add-operator.Rd index 0852bfc..a6dd748 100644 --- a/man/plus-.gg.Rd +++ b/man/ggcall-add-operator.Rd @@ -9,7 +9,7 @@ \arguments{ \item{e1}{A ggplot object of class 'ggcall'.} -\item{e2}{A layer or theme to add to the ggplot object.} +\item{e2}{A layer, theme or ggcall to add.} } \value{ A modified ggplot object with updated plot history. @@ -20,6 +20,7 @@ plot layers and modifications. This function is meant to be used in conjunction with the enhanced ggplot function provided by this package. } \examples{ +library(ggplot2, exclude = "ggplot") p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() attr(p, "ggcall") # View the plot call diff --git a/man/ggcall.Rd b/man/ggcall.Rd index 71259cd..e453d43 100644 --- a/man/ggcall.Rd +++ b/man/ggcall.Rd @@ -19,6 +19,7 @@ providing a way to reproduce or inspect the plot. Designed to work with ggplot objects of class 'ggcall'. } \examples{ +library(ggplot2, exclude = "ggplot") # Example: Create a function which combines a few ggplot layers # Typically, it will be a function from your R package where you implemented ggcall func <- function(data, x, y, bool = TRUE) { diff --git a/man/ggcall_add_assignments.Rd b/man/ggcall_add_assignments.Rd index 0901b54..3561a11 100644 --- a/man/ggcall_add_assignments.Rd +++ b/man/ggcall_add_assignments.Rd @@ -29,6 +29,7 @@ Currently only atomic variables are supported to be assign directly. More complex variables are referenced to ggcall environment. } \examples{ +library(ggplot2, exclude = "ggplot") # Example: Create a function which combines a few ggplot layers # Typically, it will be a function from your R package where you implemented ggcall func <- function(data, x, y, bool = TRUE) { @@ -58,6 +59,6 @@ eval_ggcall(plot_call_with_assignments) # Will Fail as data is needed and skipped \dontrun{ - eval_ggcall(ggcall_add_assignments(plot_call, vars = c("x", "y"))) +eval_ggcall(ggcall_add_assignments(plot_call, vars = c("x", "y"))) } } diff --git a/man/ggcall_env.Rd b/man/ggcall_env.Rd index 168b006..76ecff1 100644 --- a/man/ggcall_env.Rd +++ b/man/ggcall_env.Rd @@ -18,6 +18,7 @@ was originally created. This function is designed to work with expressions generated by `ggcall`. } \examples{ +library(ggplot2, exclude = "ggplot") fun <- function(data, x, y) { ggplot(data, aes(x = !!as.name(x), y = !!as.name(y))) + geom_point() diff --git a/man/ggplot.Rd b/man/ggplot.Rd index e530321..03db23c 100644 --- a/man/ggplot.Rd +++ b/man/ggplot.Rd @@ -19,6 +19,7 @@ capability to track the history of plot construction. This function initializes a history attribute in the `ggplot` object. } \examples{ +library(ggplot2, exclude = "ggplot") p <- ggplot(mtcars, aes(x = wt, y = mpg)) # the + function has to come from ggcall package attr(p + geom_point(), "ggcall") diff --git a/tests/testthat/test-barbell-plot.R b/tests/testthat/test-barbell-plot.R new file mode 100644 index 0000000..e0a0cf8 --- /dev/null +++ b/tests/testthat/test-barbell-plot.R @@ -0,0 +1,127 @@ +test_that("barbell_plot works with valid input", { + df <- data.frame( + Category = c("A", "B", "C", "D"), + Before = c(3.5, 4.2, 2.8, 5.1), + After = c(4.0, 4.5, 3.1, 5.5) + ) + + plot <- barbell_plot(df, "Category", "Before", "After", group_labels = c("Before", "After")) + + expect_s3_class(plot, "ggplot") + + expect_equal(plot$labels$x, "Value") + expect_equal(plot$labels$title, "Barbell Plot") + + expected_categories <- unique(df$Category) + actual_categories <- levels(plot$data$Category) + expect_equal(actual_categories, expected_categories) + +}) + +test_that("barbell_plot handles missing columns appropriately", { + df_missing <- data.frame( + Category = c("A", "B", "C", "D"), + Before = c(3.5, 4.2, 2.8, 5.1) + ) + + expect_error( + barbell_plot(df_missing, "Category", "Before", "After"), + "The following required columns are missing in 'data': After" + ) +}) + +test_that("barbell_plot checks for numeric value columns", { + df_non_numeric <- data.frame( + Category = c("A", "B", "C", "D"), + Before = c("3.5", "4.2", "2.8", "5.1"), # Non-numeric + After = c(4.0, 4.5, 3.1, 5.5) + ) + + expect_error( + barbell_plot(df_non_numeric, "Category", "Before", "After"), + "Column 'Before' must be numeric." + ) +}) + +test_that("barbell_plot checks group_labels length", { + df <- data.frame( + Category = c("A", "B", "C", "D"), + Value1 = c(3.5, 4.2, 2.8, 5.1), + Value2 = c(4.0, 4.5, 3.1, 5.5) + ) + + expect_error( + barbell_plot(df, "Category", "Value1", "Value2", group_labels = c("Group1")), + "Parameter 'group_labels' must be a character vector of length 2." + ) +}) + +test_that("barbell_plot works with custom axis labels and title", { + df <- data.frame( + Item = c("Item1", "Item2", "Item3"), + Baseline = c(10, 15, 20), + FollowUp = c(12, 14, 22) + ) + + plot <- barbell_plot( + df, + category_col = "Item", + value1_col = "Baseline", + value2_col = "FollowUp", + group_labels = c("Baseline", "Follow-Up"), + xlab = "Score", + title = "Comparison Over Time" + ) + + expect_s3_class(plot, "ggplot") + + expect_equal(plot$labels$x, "Score") + expect_equal(plot$labels$title, "Comparison Over Time") + +}) + + +test_that("forest_plot ggcall", { + df <- data.frame( + Item = c("Item1", "Item2", "Item3"), + Baseline = c(10, 15, 20), + FollowUp = c(12, 14, 22) + ) + + plot <- barbell_plot( + df, + category_col = "Item", + value1_col = "Baseline", + value2_col = "FollowUp", + group_labels = c("Baseline", "Follow-Up"), + xlab = "Score", + title = "Comparison Over Time" + ) + + expect_s3_class(plot, "ggcall") + call_plot <- ggcall(plot) + expect_s3_class(call_plot, "ggcall_code") + expect_s3_class(eval_ggcall(call_plot), "ggplot") + real <- paste(deparse(call_plot), collapse = "\n") + expected <- paste( + deparse( + quote( + ggplot(data_long, aes(x = .data[["Value"]], y = .data[["Category"]], group = .data[["Category"]])) + + geom_line(aes(group = .data[["Category"]]), color = "gray70", linewidth = 1) + + geom_point(aes(color = .data[["Group"]]), size = 4) + + scale_color_manual(values = c("#00BFC4", "#F8766D"), name = "") + + labs(x = xlab, y = "", title = barbell_title) + + theme_minimal() + + theme(axis.text.y = element_text(size = 10), plot.title = element_text(hjust = 0.5, face = "bold"), + legend.position = "bottom") + ) + ), + collapse = "\n" + ) + expect_identical( + real, + expected + ) +}) + + diff --git a/tests/testthat/test-forest-plot.R b/tests/testthat/test-forest-plot.R new file mode 100644 index 0000000..526efb7 --- /dev/null +++ b/tests/testthat/test-forest-plot.R @@ -0,0 +1,122 @@ +test_that("forest_plot works with valid input", { + df <- data.frame( + Treatment = c("Treatment A", "Treatment B", "Treatment C"), + Estimate = c(0.2, 0.5, -0.1), + CI_lower = c(0.1, 0.3, -0.3), + CI_upper = c(0.3, 0.7, 0.1) + ) + + plot <- forest_plot(df, "Estimate", "CI_lower", "CI_upper", "Treatment") + + expect_s3_class(plot, "ggplot") + + expect_equal(plot$labels$x, "Estimate") + expect_equal(plot$labels$title, "Forest Plot") + + expected_labels <- unique(df$Treatment) + actual_labels <- ggplot_build(plot)$layout$panel_params[[1]]$y$get_labels() + expect_equal(actual_labels, expected_labels) +}) + +test_that("forest_plot handles missing columns appropriately", { + df_missing <- data.frame( + Treatment = c("Treatment A", "Treatment B", "Treatment C"), + Estimate = c(0.2, 0.5, -0.1) + ) + + expect_error( + forest_plot(df_missing, "Estimate", "CI_lower", "CI_upper", "Treatment"), + "The following required columns are missing in 'data': CI_lower, CI_upper" + ) +}) + +test_that("forest_plot works with custom axis labels and title", { + df <- data.frame( + Variable = c("Var1", "Var2", "Var3"), + Est = c(1.2, 0.8, 1.5), + Lower = c(0.9, 0.5, 1.1), + Upper = c(1.5, 1.1, 1.9) + ) + + plot <- forest_plot( + df, + estimate_col = "Est", + ci_lower_col = "Lower", + ci_upper_col = "Upper", + label_col = "Variable", + xlab = "Odds Ratio", + title = "Custom Forest Plot" + ) + + expect_s3_class(plot, "ggplot") + + expect_equal(plot$labels$x, "Odds Ratio") + expect_equal(plot$labels$title, "Custom Forest Plot") +}) + +test_that("forest_plot handles non-numeric data gracefully", { + df_non_numeric <- data.frame( + Treatment = c("Treatment A", "Treatment B", "Treatment C"), + Estimate = c("0.2", "0.5", "-0.1"), # Estimates as strings + CI_lower = c(0.1, 0.3, -0.3), + CI_upper = c(0.3, 0.7, 0.1) + ) + + df_non_numeric$Estimate <- as.character(df_non_numeric$Estimate) + + expect_error( + forest_plot(df_non_numeric, "Estimate", "CI_lower", "CI_upper", "Treatment"), + "must be numeric" + ) +}) + +test_that("forest_plot can handle data with additional irrelevant columns", { + df_extra <- data.frame( + Treatment = c("Treatment A", "Treatment B", "Treatment C"), + Estimate = c(0.2, 0.5, -0.1), + CI_lower = c(0.1, 0.3, -0.3), + CI_upper = c(0.3, 0.7, 0.1), + Extra1 = c("A", "B", "C"), + Extra2 = 1:3 + ) + + plot <- forest_plot(df_extra, "Estimate", "CI_lower", "CI_upper", "Treatment") + + expect_s3_class(plot, "ggplot") +}) + +test_that("forest_plot ggcall", { + df_extra <- data.frame( + Treatment = c("Treatment A", "Treatment B", "Treatment C"), + Estimate = c(0.2, 0.5, -0.1), + CI_lower = c(0.1, 0.3, -0.3), + CI_upper = c(0.3, 0.7, 0.1), + Extra1 = c("A", "B", "C"), + Extra2 = 1:3 + ) + + plot <- forest_plot(df_extra, "Estimate", "CI_lower", "CI_upper", "Treatment") + expect_s3_class(plot, "ggcall") + call_plot <- ggcall(plot) + expect_s3_class(call_plot, "ggcall_code") + expect_s3_class(eval_ggcall(call_plot), "ggplot") + real <- paste(deparse(call_plot), collapse = "\n") + expected <- paste( + deparse( + quote( + ggplot(forest_data, aes(x = .data[[estimate_col]], y = .data[[label_col]])) + + geom_point(size = 3) + + geom_errorbarh(aes(xmin = .data[[ci_lower_col]], xmax = .data[[ci_upper_col]]), height = 0.2) + + labs(x = xlab, y = "", title = forest_title) + + theme_minimal() + + theme(axis.text.y = element_text(size = 10), plot.title = element_text(hjust = 0.5, face = "bold")) + ) + ), + collapse = "\n" + ) + expect_identical( + real, + expected + ) +}) + diff --git a/vignettes/.gitignore b/vignettes/.gitignore deleted file mode 100644 index 097b241..0000000 --- a/vignettes/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.html -*.R diff --git a/vignettes/ggcall.Rmd b/vignettes/ggcall.Rmd deleted file mode 100644 index abe2a5c..0000000 --- a/vignettes/ggcall.Rmd +++ /dev/null @@ -1,355 +0,0 @@ ---- -title: "Get Started" -author: "Maciej Nasinski" -output: - rmarkdown::html_document: - theme: "spacelab" - highlight: "kate" - toc: true - toc_float: true -vignette: > - %\VignetteIndexEntry{Get Started} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - -## Overview - -Transparency and reproducibility are fundamental principles in data analysis across various fields, from academic -research to industry applications. The `ggcall` package enhances the functionality of `ggplot2` by enabling users to retrieve the complete code used to generate a `ggplot` object inside a function. This package is beneficial for understanding and replicating complex `ggplot2` plots returned by a function. From technical point of view, `ggcall` extends `ggplot2` `+` operator and `ggplot` function to track the history of plot construction. - -`ggcall` makes a developer's life easier and limits the need to use base r metaprogramming or `rlang`. - -`patchwork` ggplot2 related operators like `+`, `-`, `*`, `|`, `&` and `/` are optionally supported. -`patchwork` is a package that expands the API to allow for arbitrarily complex composition of plots by, -among others, providing mathematical operators for combining multiple plots. - -An excellent implementation example is to create a bunch of ggplot templates, and we want them to be functions. -Then, each template will generate the expected plot, and the ggplot2 code behind is easy to get. - -## Usage - -Imagine using a package or function that generates a complex `ggplot2` visualization. -Then, the ggplot code used to create the plot is not exposed. -`ggcall` overcomes this barrier by extracting the hidden code, making it accessible for examination and modification. - -Here is a **simple** illustrative example with a scenario in which a function generates a `ggplot2` plot based on input data: - -```r -remotes::install_github("https://github.com/Polkas/ggcall") -library(ggcall) - -# Example: Create a function which combines a few ggplot layers -# Typically, it will be a function from your R package where you implemented ggcall -create_custom_plot <- function(data, x, y, bool = TRUE) { - # layers have to be added with + - gg <- ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + - geom_point(alpha = 0.4) + - facet_grid(~gear) - - if (bool) { - gg <- gg + theme(axis.title.x = element_blank()) - } - - func_internal <- function(gg) { - gg + labs(title = "custom title") - } - - func_internal(gg) -} - -# gg_plot is a ggplot object -gg_plot <- create_custom_plot(mtcars, "wt", "mpg") -print(gg_plot) -# Retrieve the plot construction code -plot_call <- ggcall(gg_plot) -plot_call -# ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + geom_point(alpha = 0.4) + -# facet_grid(~gear) + theme(axis.title.x = element_blank()) + -# labs(title = "custom title") -# attr(,"class") -# [1] "ggcall_code" -# attr(,"ggcall_env") -# - -# Optionally: Style the code with styler -# install.packages("styler") -styler::style_text( - paste(deparse(plot_call_with_assignments), collapse = "\n") -) - -# Optionally: add assignments to call -plot_call_with_assignments <- ggcall_add_assignments(plot_call) -styler::style_text( - paste(deparse(plot_call_with_assignments), collapse = "\n") -) - -# Optionally: access call environment -# Access call environment and/or use it to evaluate the call -plot_call_env <- ggcall_env(plot_call) -as.list(plot_call_env) - -# Optionally: reevaulate the call -# Reproduce the plot by evaluating the code -eval_ggcall(plot_call) -eval_ggcall(plot_call_with_assignments) - -# Optionally overwrite variables -eval_ggcall(plot_call, mtcars = mtcars[1:10, ], x = "disp") -``` - -Here is an example with `patchwork` usage. - -```r -remotes::install_github("https://github.com/Polkas/ggcall") -library(ggcall) -library(patchwork) - -# Example: Create a function which combines a few ggplot layers -# Typically, it will be a function from your R package where you implemented ggcall -create_custom_plot <- function(data, x, y, bool = TRUE) { - # layers have to be added with + - gg <- ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + - geom_point(alpha = 0.4) + - facet_grid(~gear) - - if (bool) { - gg <- gg + theme(axis.title.x = element_blank()) - } - - func_internal <- function(gg) { - gg + labs(title = "custom title") - } - - # patchwork + - func_internal(gg) + - # another ggplot added with patchwork - ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + - geom_point() + - theme(axis.title.y = element_blank(), axis.title.x = element_text(hjust = -0.15)) + - plot_annotation(caption = "My Caption") -} - -# gg_plot is a ggplot object -gg_plot <- create_custom_plot(mtcars, "wt", "mpg") -print(gg_plot) -# Retrieve the plot construction code -plot_call <- ggcall(gg_plot) -plot_call -# ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + geom_point(alpha = 0.4) + -# facet_grid(~gear) + theme(axis.title.x = element_blank()) + -# labs(title = "custom title") + ggplot(data, aes(x = .data[[x]], -# y = .data[[y]])) + geom_point() + theme(axis.title.y = element_blank(), -# axis.title.x = element_text(hjust = -0.15)) + plot_annotation(caption = "My Caption") -# ... - -# Optionally: Style the code with styler -# install.packages("styler") -styler::style_text( - paste(deparse(plot_call), collapse = "\n") -) -# ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + -# geom_point(alpha = 0.4) + -# facet_grid(~gear) + -# theme(axis.title.x = element_blank()) + -# labs(title = "custom title") + -# ggplot(data, aes(x = .data[[x]], y = .data[[y]])) + -# geom_point() + -# theme(axis.title.y = element_blank(), axis.title.x = element_text(hjust = -0.15)) + -# plot_annotation(caption = "My Caption") - -# Optionally: add assignments to call -plot_call_with_assignments <- ggcall_add_assignments(plot_call) -styler::style_text( - paste(deparse(plot_call_with_assignments), collapse = "\n") -) - -# Optionally: access call environment -# Access call environment and/or use it to evaluate the call -plot_call_env <- ggcall_env(plot_call) -as.list(plot_call_env) - -# Optionally: reevaulate the call -# Reproduce the plot by evaluating the code -eval_ggcall(plot_call) -eval_ggcall(plot_call_with_assignments) - -# Optionally overwrite variables -eval_ggcall(plot_call, mtcars = mtcars[1:10, ], x = "disp") -``` - -## Developers Implementation - -The ggcall can be implemented in a few ways to your package, direct dependency or standalone. - - -`usethis` package is optional but it truly simplify the process. -The `usethis` >= 2.2.0 is required to use `usethis::use_standalone`. - -``` -install.packages("usethis") -``` - -GENERAL COMMENTS: - -``` -# Apply only if needed -# In your own code remove all ggplot2:: prefix-ing before ggplot function calls -# ggplot2::ggplot(...) -> ggplot(...) -``` - -DIRECT DEPENDENCY: - -An option is to use the ggcall as a DESCRIPTION file import dependency for your package. - -``` -usethis::use_package("ggcall") -``` - -``` -# DO NOT import ggplot function from ggplot2 instead import it from ggcall - -# a. When importing all ggplot2 functions -# #' @rawNamespace import(ggplot2, except = c(ggplot)) -# #' @import ggcall -# b. When importing specific ggplot2 functions -# #' @importFrom ggplot2 geom_line -# #' @importFrom ggcall ggplot -``` - -STANALONE - copy paste the files to your own project: - -with `patchwork` support - -``` -# copy paste the ggcall.R file to your own package R directory -# copy paste the patchwork.R file to your own package R directory -``` - -```r -usethis::use_package("ggplot2") -usethis::use_package("patchwork") -usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.3") -# you may need to update the file time to time -``` - -without `patchwork` support - -``` -# copy paste the ggcall.R file to your own package R directory -``` - -``` -usethis::use_package("ggplot2") -usethis::use_standalone("polkas/ggcall", "ggcall.R", ref = "v0.3.3") -# you may need to update the file time to time -``` - -### Example implementation in GGally package - -A notable example of ggcall’s successful integration is seen in the `GGally` package fork. -`GGally` package is a `ggplot2` extension used to create correlation matrices and scatterplot matrices. -As `GGally` had already overwritten the `+.gg` operator to extend ggplot2’s functionality for their own reasons, demonstrating that overwriting operators can be considered an acceptable and practical solution. - -The `GGally` package required only minor changes to implement `ggcall`, showcasing how easily it can be integrated into existing solutions. The copy and paste `ggcall` files to `GGally` are R/ggcall.R and OPTIONAL R/patchwork.R and the extended -already existing `+.gg` operator is located in R/ggpairs_add.R. - -The `GGally` package fork with implemented `ggcall` is available on [Github](https://github.com/Polkas/ggally). - -Here is an illustrative example with the `GGally::ggcorr` function from the fork with `ggcall`: - -``` -remotes::install_github("https://github.com/Polkas/ggally") -library(GGally) - -########################### -# Example for GGally ggcorr -########################### - -data(mtcars) -gg <- GGally::ggcorr( - mtcars, - name = expression(rho), - geom = "circle", - max_size = 10, - min_size = 2, - size = 3, - hjust = 0.75, - nbreaks = 6, - angle = -45, - palette = "PuOr", - legend.position = "top" -) + -ggtitle("Correlation Matrix for mtcars Dataset") -# gg is a ggplot object -gg - -# Retrieve the plot construction code -gg_call <- ggcall(gg) -gg_call - -# Optionally: Style the code with styler -styler::style_text(deparse1(gg_call)) - -# Optionally: add assignments to call -gg_call_with_assignments <- ggcall_add_assignments(gg_call) -gg_call_with_assignments -styler::style_text( - paste(deparse(gg_call_with_assignments), collapse = "\n") -) - -# Optionally: reevaulate the call -# Reproduce the plot by evaluating the code -eval_ggcall(gg_call_with_assignments) -eval_ggcall(ggcall_add_assignments(gg_call)) - -############################## -# Example for GGally ggscatmat -############################## - -data(iris) -gg <- GGally::ggscatmat(iris, color = "Species", columns = 1:4) -# gg is a ggplot object -gg - -# Retrieve the plot construction code -gg_call <- ggcall(gg) -gg_call - -# Optionally: Style the code with styler -styler::style_text(deparse1(gg_call)) - -# Optionally: add assignments to call -gg_call_with_assignments <- ggcall_add_assignments(gg_call) -gg_call_with_assignments -styler::style_text( - paste(deparse(gg_call_with_assignments), collapse = "\n") -) - -# Optionally: reevaulate the call -# Reproduce the plot by evaluating the code -eval_ggcall(gg_call) -eval_ggcall(gg_call_with_assignments) - -########################## -# Example for GGally ggduo -########################## - -# Not supported for ggmatrix like plots -# ggcall will fail as ggmatrix plots are not build with pure ggplot2 - -gg <- GGally::ggduo(tips, mapping = ggplot2::aes(colour = sex), columnsX = 3:4, columnsY = 1:2) -ggplot2::is.ggplot(gg) -# Fail gg_call <- ggcall(gg) -``` - -## Note - -The solution is in the development and is expected not to work in specific situations. From e6aaf52137ccc2efcadef60da79890ff15e8e028 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 21:11:40 +0000 Subject: [PATCH 19/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/barbell-plot.R | 1 - R/forest-plot.R | 1 - tests/testthat/test-barbell-plot.R | 12 +++++------- tests/testthat/test-forest-plot.R | 3 +-- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/R/barbell-plot.R b/R/barbell-plot.R index 48e00ea..7d0cb41 100644 --- a/R/barbell-plot.R +++ b/R/barbell-plot.R @@ -22,7 +22,6 @@ barbell_plot <- function(barbell_data, category_col, value1_col, value2_col, group_labels = c("Value 1", "Value 2"), xlab = "Value", title = "Barbell Plot") { - required_cols <- c(category_col, value1_col, value2_col) missing_cols <- setdiff(required_cols, names(barbell_data)) if (length(missing_cols) > 0) { diff --git a/R/forest-plot.R b/R/forest-plot.R index e57afee..e74b8d1 100644 --- a/R/forest-plot.R +++ b/R/forest-plot.R @@ -22,7 +22,6 @@ #' @export forest_plot <- function(forest_data, estimate_col, ci_lower_col, ci_upper_col, label_col, xlab = "Estimate", title = "Forest Plot") { - required_cols <- c(estimate_col, ci_lower_col, ci_upper_col, label_col) missing_cols <- setdiff(required_cols, names(forest_data)) if (length(missing_cols) > 0) { diff --git a/tests/testthat/test-barbell-plot.R b/tests/testthat/test-barbell-plot.R index e0a0cf8..c0a9758 100644 --- a/tests/testthat/test-barbell-plot.R +++ b/tests/testthat/test-barbell-plot.R @@ -15,7 +15,6 @@ test_that("barbell_plot works with valid input", { expected_categories <- unique(df$Category) actual_categories <- levels(plot$data$Category) expect_equal(actual_categories, expected_categories) - }) test_that("barbell_plot handles missing columns appropriately", { @@ -33,7 +32,7 @@ test_that("barbell_plot handles missing columns appropriately", { test_that("barbell_plot checks for numeric value columns", { df_non_numeric <- data.frame( Category = c("A", "B", "C", "D"), - Before = c("3.5", "4.2", "2.8", "5.1"), # Non-numeric + Before = c("3.5", "4.2", "2.8", "5.1"), # Non-numeric After = c(4.0, 4.5, 3.1, 5.5) ) @@ -77,7 +76,6 @@ test_that("barbell_plot works with custom axis labels and title", { expect_equal(plot$labels$x, "Score") expect_equal(plot$labels$title, "Comparison Over Time") - }) @@ -112,8 +110,10 @@ test_that("forest_plot ggcall", { scale_color_manual(values = c("#00BFC4", "#F8766D"), name = "") + labs(x = xlab, y = "", title = barbell_title) + theme_minimal() + - theme(axis.text.y = element_text(size = 10), plot.title = element_text(hjust = 0.5, face = "bold"), - legend.position = "bottom") + theme( + axis.text.y = element_text(size = 10), plot.title = element_text(hjust = 0.5, face = "bold"), + legend.position = "bottom" + ) ) ), collapse = "\n" @@ -123,5 +123,3 @@ test_that("forest_plot ggcall", { expected ) }) - - diff --git a/tests/testthat/test-forest-plot.R b/tests/testthat/test-forest-plot.R index 526efb7..0cf9375 100644 --- a/tests/testthat/test-forest-plot.R +++ b/tests/testthat/test-forest-plot.R @@ -57,7 +57,7 @@ test_that("forest_plot works with custom axis labels and title", { test_that("forest_plot handles non-numeric data gracefully", { df_non_numeric <- data.frame( Treatment = c("Treatment A", "Treatment B", "Treatment C"), - Estimate = c("0.2", "0.5", "-0.1"), # Estimates as strings + Estimate = c("0.2", "0.5", "-0.1"), # Estimates as strings CI_lower = c(0.1, 0.3, -0.3), CI_upper = c(0.3, 0.7, 0.1) ) @@ -119,4 +119,3 @@ test_that("forest_plot ggcall", { expected ) }) - From 7ecb7c0014f6a0d45db9fa0fae81d18a67d76a3a Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 23:09:35 +0100 Subject: [PATCH 20/29] update --- DESCRIPTION | 5 +++-- README.md | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index bbe4fec..e56cfb3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,9 +16,10 @@ BugReports: https://github.com/Polkas/ggcall/issues License: Apache License (>= 2) Encoding: UTF-8 LazyData: true -Imports: - patchwork, +Depends: ggplot2 +Imports: + patchwork Suggests: knitr, usethis, diff --git a/README.md b/README.md index b865a14..6c20cff 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ eval_ggcall(gg_barbell) The ggcall can be implemented as a standalone solution. A "standalone" file implements a minimum set of functionality in such a way that it can be copied into another package. -`usethis::use_standalone()` makes it easy to get such a file into your own repo/package. +`usethis::use_standalone()` makes it easy to get such a file into your own repo/package and later update it if needed. [Example of standalone file in another package, rlang](https://github.com/r-lib/rlang/blob/main/R/standalone-purrr.R) The `usethis` >= 2.2.0 is required. @@ -80,7 +80,7 @@ The `usethis` >= 2.2.0 is required. install.packages("usethis") ``` -STANALONE - copy paste the files and add dependencies to your own package: +STANDALONE means copy paste the files and add dependencies to your own package. Please create an R package if not having such yet. @@ -88,30 +88,25 @@ Please create an R package if not having such yet. usethis::create_package() ``` -with `patchwork` support +WITH `patchwork` support ``` -# Add ggplot2 (Depends), patchwork (Imports) and styler (Suggests) as your package dependencies +# Add ggplot2, patchwork as your package dependencies # copy paste the ggcall.R file to your own package R directory # copy paste the patchwork.R file to your own package R directory -usethis::use_package("ggplot2", type = "Depends") -usethis::use_package("patchwork") -usethis::use_package("styler", "Suggests") usethis::use_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.3") -# you may need to update the files time to time, run usethis::use_standalone +# you may need to update the files time to time with usethis::use_standalone ``` -without `patchwork` support +WITHOUT `patchwork` support ``` -# Add ggplot2 (Depends) and styler (Suggests) as your package dependencies +# Add ggplot2as your package dependencies # copy paste the ggcall.R file to your own package R directory -usethis::use_package("ggplot2", type = "Depends") -usethis::use_package("styler", "Suggests") usethis::use_standalone("polkas/ggcall", "ggcall.R", ref = "v0.3.3") -# you may need to update the file time to time, run usethis::use_standalone +# you may need to update the files time to time with usethis::use_standalone ``` GENERAL COMMENTS: @@ -127,6 +122,11 @@ GENERAL COMMENTS: #' @rawNamespace import(ggplot2, except = c(ggplot)) ``` +``` +# Combine ggcall +.gg operator with your own one if you already overwrited it in your package +# e.g. GGally package requires such step +``` + ## Note The solution is in the development and is expected not to work in specific situations. From 50fb7a2c4cf4257c391255c91db19ae845e4e885 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 23:23:15 +0100 Subject: [PATCH 21/29] dont run styler --- R/standalone-ggcall.R | 18 ++++++++++-------- man/ggcall.Rd | 9 +++++---- man/ggcall_add_assignments.Rd | 9 +++++---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/R/standalone-ggcall.R b/R/standalone-ggcall.R index 5008d0e..483a3c1 100644 --- a/R/standalone-ggcall.R +++ b/R/standalone-ggcall.R @@ -133,10 +133,11 @@ ggplot <- function(...) { #' plot_call <- ggcall(func(mtcars, "wt", "mpg")) #' # Optionally: Style the code with styler #' # deparse1 is recommended and available in R>=4.0.0 -#' styler::style_text( -#' paste(deparse(plot_call, 500), collapse = " ") -#' ) -#' +#' \dontrun{ +#' styler::style_text( +#' paste(deparse(plot_call), collapse = "\n") +#' ) +#' } #' @export #' ggcall <- function(plot) { @@ -188,10 +189,11 @@ ggcall <- function(plot) { #' plot_call <- ggcall(func(mtcars, "wt", "mpg")) #' # Optionally: Add assignments #' plot_call_with_assignments <- ggcall_add_assignments(plot_call) -#' styler::style_text( -#' paste(deparse(plot_call_with_assignments), collapse = "\n") -#' ) -#' +#' \dontrun{ +#' styler::style_text( +#' paste(deparse(plot_call_with_assignments), collapse = "\n") +#' ) +#' } #' eval_ggcall(plot_call_with_assignments) #' #' # Will Fail as data is needed and skipped diff --git a/man/ggcall.Rd b/man/ggcall.Rd index e453d43..9bb4e0f 100644 --- a/man/ggcall.Rd +++ b/man/ggcall.Rd @@ -41,8 +41,9 @@ func <- function(data, x, y, bool = TRUE) { plot_call <- ggcall(func(mtcars, "wt", "mpg")) # Optionally: Style the code with styler # deparse1 is recommended and available in R>=4.0.0 -styler::style_text( - paste(deparse(plot_call, 500), collapse = " ") -) - +\dontrun{ + styler::style_text( + paste(deparse(plot_call), collapse = "\n") + ) +} } diff --git a/man/ggcall_add_assignments.Rd b/man/ggcall_add_assignments.Rd index 3561a11..e7f6a4c 100644 --- a/man/ggcall_add_assignments.Rd +++ b/man/ggcall_add_assignments.Rd @@ -51,10 +51,11 @@ func <- function(data, x, y, bool = TRUE) { plot_call <- ggcall(func(mtcars, "wt", "mpg")) # Optionally: Add assignments plot_call_with_assignments <- ggcall_add_assignments(plot_call) -styler::style_text( - paste(deparse(plot_call_with_assignments), collapse = "\n") -) - +\dontrun{ + styler::style_text( + paste(deparse(plot_call_with_assignments), collapse = "\n") + ) +} eval_ggcall(plot_call_with_assignments) # Will Fail as data is needed and skipped From 0037c1648f481e42476a9df8350470a08837d884 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:24:23 +0000 Subject: [PATCH 22/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/standalone-ggcall.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/R/standalone-ggcall.R b/R/standalone-ggcall.R index 483a3c1..3c9793f 100644 --- a/R/standalone-ggcall.R +++ b/R/standalone-ggcall.R @@ -134,9 +134,9 @@ ggplot <- function(...) { #' # Optionally: Style the code with styler #' # deparse1 is recommended and available in R>=4.0.0 #' \dontrun{ -#' styler::style_text( -#' paste(deparse(plot_call), collapse = "\n") -#' ) +#' styler::style_text( +#' paste(deparse(plot_call), collapse = "\n") +#' ) #' } #' @export #' @@ -190,9 +190,9 @@ ggcall <- function(plot) { #' # Optionally: Add assignments #' plot_call_with_assignments <- ggcall_add_assignments(plot_call) #' \dontrun{ -#' styler::style_text( -#' paste(deparse(plot_call_with_assignments), collapse = "\n") -#' ) +#' styler::style_text( +#' paste(deparse(plot_call_with_assignments), collapse = "\n") +#' ) #' } #' eval_ggcall(plot_call_with_assignments) #' From 5e0eec600a09aee4c24121c830511844393f7f17 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 23:49:42 +0100 Subject: [PATCH 23/29] ggcall_example --- R/barbell-plot.R | 63 --------------- R/forest-plot.R | 51 ------------ README.md | 7 +- tests/testthat/test-barbell-plot.R | 125 ----------------------------- tests/testthat/test-forest-plot.R | 121 ---------------------------- 5 files changed, 4 insertions(+), 363 deletions(-) delete mode 100644 R/barbell-plot.R delete mode 100644 R/forest-plot.R delete mode 100644 tests/testthat/test-barbell-plot.R delete mode 100644 tests/testthat/test-forest-plot.R diff --git a/R/barbell-plot.R b/R/barbell-plot.R deleted file mode 100644 index 7d0cb41..0000000 --- a/R/barbell-plot.R +++ /dev/null @@ -1,63 +0,0 @@ -#' @title General Barbell Plot Function -#' @description Creates a barbell plot from a data frame containing two sets of values to compare across categories. -#' @param barbell_data A data frame containing the data for the barbell plot. -#' @param category_col The name of the column containing the category labels (as a string). -#' @param value1_col The name of the first value column to compare (as a string). -#' @param value2_col The name of the second value column to compare (as a string). -#' @param group_labels A character vector of length 2 providing labels for the two values. Defaults to `c("Value 1", "Value 2")`. -#' @param xlab The label for the x-axis. Defaults to "Value". -#' @param title The title of the plot. Defaults to "Barbell Plot". -#' @return A ggplot object representing the barbell plot. -#' @examples -#' # Example data -#' df <- data.frame( -#' Category = c("A", "B", "C", "D"), -#' Before = c(3.5, 4.2, 2.8, 5.1), -#' After = c(4.0, 4.5, 3.1, 5.5) -#' ) -#' -#' # Create the barbell plot -#' barbell_plot(df, "Category", "Before", "After", group_labels = c("Before", "After")) -#' @export -barbell_plot <- function(barbell_data, category_col, value1_col, value2_col, - group_labels = c("Value 1", "Value 2"), - xlab = "Value", title = "Barbell Plot") { - required_cols <- c(category_col, value1_col, value2_col) - missing_cols <- setdiff(required_cols, names(barbell_data)) - if (length(missing_cols) > 0) { - stop("The following required columns are missing in 'data': ", paste(missing_cols, collapse = ", ")) - } - - numeric_cols <- c(value1_col, value2_col) - for (col in numeric_cols) { - if (!is.numeric(barbell_data[[col]])) { - stop("Column '", col, "' must be numeric.") - } - } - - if (length(group_labels) != 2) { - stop("Parameter 'group_labels' must be a character vector of length 2.") - } - - data_long <- data.frame( - Category = rep(barbell_data[[category_col]], 2), - Value = c(barbell_data[[value1_col]], barbell_data[[value2_col]]), - Group = rep(group_labels, each = nrow(barbell_data)) - ) - - data_long$Category <- factor(data_long$Category, levels = unique(barbell_data[[category_col]])) - - barbell_title <- title - - ggplot(data_long, aes(x = .data[["Value"]], y = .data[["Category"]], group = .data[["Category"]])) + - geom_line(aes(group = .data[["Category"]]), color = "gray70", linewidth = 1) + - geom_point(aes(color = .data[["Group"]]), size = 4) + - scale_color_manual(values = c("#00BFC4", "#F8766D"), name = "") + - labs(x = xlab, y = "", title = barbell_title) + - theme_minimal() + - theme( - axis.text.y = element_text(size = 10), - plot.title = element_text(hjust = 0.5, face = "bold"), - legend.position = "bottom" - ) -} diff --git a/R/forest-plot.R b/R/forest-plot.R deleted file mode 100644 index e74b8d1..0000000 --- a/R/forest-plot.R +++ /dev/null @@ -1,51 +0,0 @@ -#' @title Forest Plot Function -#' @description Creates a forest plot from a data frame containing estimates and confidence intervals. -#' @param forest_data A data frame containing the data for the forest plot. -#' @param estimate_col The name of the column containing the point estimates (as a string). -#' @param ci_lower_col The name of the column containing the lower bounds of the confidence intervals (as a string). -#' @param ci_upper_col The name of the column containing the upper bounds of the confidence intervals (as a string). -#' @param label_col The name of the column containing the labels for each estimate (as a string). -#' @param xlab The label for the x-axis. Defaults to "Estimate". -#' @param title The title of the plot. Defaults to "Forest Plot". -#' @return A ggplot object representing the forest plot. -#' @examples -#' # Example data -#' df <- data.frame( -#' Treatment = c("Treatment A", "Treatment B", "Treatment C"), -#' Estimate = c(0.2, 0.5, -0.1), -#' CI_lower = c(0.1, 0.3, -0.3), -#' CI_upper = c(0.3, 0.7, 0.1) -#' ) -#' -#' # Create the forest plot -#' forest_plot(df, "Estimate", "CI_lower", "CI_upper", "Treatment") -#' @export -forest_plot <- function(forest_data, estimate_col, ci_lower_col, ci_upper_col, label_col, - xlab = "Estimate", title = "Forest Plot") { - required_cols <- c(estimate_col, ci_lower_col, ci_upper_col, label_col) - missing_cols <- setdiff(required_cols, names(forest_data)) - if (length(missing_cols) > 0) { - stop("The following required columns are missing in 'data': ", paste(missing_cols, collapse = ", ")) - } - - numeric_cols <- c(estimate_col, ci_lower_col, ci_upper_col) - for (col in numeric_cols) { - if (!is.numeric(forest_data[[col]])) { - stop("Column '", col, "' must be numeric.") - } - } - - forest_data[[label_col]] <- factor(forest_data[[label_col]], levels = unique(forest_data[[label_col]])) - - forest_title <- title - - ggplot(forest_data, aes(x = .data[[estimate_col]], y = .data[[label_col]])) + - geom_point(size = 3) + - geom_errorbarh(aes(xmin = .data[[ci_lower_col]], xmax = .data[[ci_upper_col]]), height = 0.2) + - labs(x = xlab, y = "", title = forest_title) + - theme_minimal() + - theme( - axis.text.y = element_text(size = 10), - plot.title = element_text(hjust = 0.5, face = "bold") - ) -} diff --git a/README.md b/README.md index 6c20cff..ebb3d5d 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,14 @@ Then, each template will generate the expected plot, and the ggplot2 code behind ## Example -`forest_plot` and `barbell` functions are a part of `ggcall` package. +`forest_plot` and `barbell` functions are a part of `ggcall.example` package. Typically, it will be a function returning `ggplot2` object from your own R package where you implemented `ggcall`. +[the ggcall_example repository](https://github.com/Polkas/ggcall_example) contains a simple implementation of ggcall. ```r -remotes::install_github("https://github.com/Polkas/ggcall") -library(ggcall) +remotes::install_github("https://github.com/Polkas/ggcall_example") +library(ggcall.example) # Print the body of the function forest_plot diff --git a/tests/testthat/test-barbell-plot.R b/tests/testthat/test-barbell-plot.R deleted file mode 100644 index c0a9758..0000000 --- a/tests/testthat/test-barbell-plot.R +++ /dev/null @@ -1,125 +0,0 @@ -test_that("barbell_plot works with valid input", { - df <- data.frame( - Category = c("A", "B", "C", "D"), - Before = c(3.5, 4.2, 2.8, 5.1), - After = c(4.0, 4.5, 3.1, 5.5) - ) - - plot <- barbell_plot(df, "Category", "Before", "After", group_labels = c("Before", "After")) - - expect_s3_class(plot, "ggplot") - - expect_equal(plot$labels$x, "Value") - expect_equal(plot$labels$title, "Barbell Plot") - - expected_categories <- unique(df$Category) - actual_categories <- levels(plot$data$Category) - expect_equal(actual_categories, expected_categories) -}) - -test_that("barbell_plot handles missing columns appropriately", { - df_missing <- data.frame( - Category = c("A", "B", "C", "D"), - Before = c(3.5, 4.2, 2.8, 5.1) - ) - - expect_error( - barbell_plot(df_missing, "Category", "Before", "After"), - "The following required columns are missing in 'data': After" - ) -}) - -test_that("barbell_plot checks for numeric value columns", { - df_non_numeric <- data.frame( - Category = c("A", "B", "C", "D"), - Before = c("3.5", "4.2", "2.8", "5.1"), # Non-numeric - After = c(4.0, 4.5, 3.1, 5.5) - ) - - expect_error( - barbell_plot(df_non_numeric, "Category", "Before", "After"), - "Column 'Before' must be numeric." - ) -}) - -test_that("barbell_plot checks group_labels length", { - df <- data.frame( - Category = c("A", "B", "C", "D"), - Value1 = c(3.5, 4.2, 2.8, 5.1), - Value2 = c(4.0, 4.5, 3.1, 5.5) - ) - - expect_error( - barbell_plot(df, "Category", "Value1", "Value2", group_labels = c("Group1")), - "Parameter 'group_labels' must be a character vector of length 2." - ) -}) - -test_that("barbell_plot works with custom axis labels and title", { - df <- data.frame( - Item = c("Item1", "Item2", "Item3"), - Baseline = c(10, 15, 20), - FollowUp = c(12, 14, 22) - ) - - plot <- barbell_plot( - df, - category_col = "Item", - value1_col = "Baseline", - value2_col = "FollowUp", - group_labels = c("Baseline", "Follow-Up"), - xlab = "Score", - title = "Comparison Over Time" - ) - - expect_s3_class(plot, "ggplot") - - expect_equal(plot$labels$x, "Score") - expect_equal(plot$labels$title, "Comparison Over Time") -}) - - -test_that("forest_plot ggcall", { - df <- data.frame( - Item = c("Item1", "Item2", "Item3"), - Baseline = c(10, 15, 20), - FollowUp = c(12, 14, 22) - ) - - plot <- barbell_plot( - df, - category_col = "Item", - value1_col = "Baseline", - value2_col = "FollowUp", - group_labels = c("Baseline", "Follow-Up"), - xlab = "Score", - title = "Comparison Over Time" - ) - - expect_s3_class(plot, "ggcall") - call_plot <- ggcall(plot) - expect_s3_class(call_plot, "ggcall_code") - expect_s3_class(eval_ggcall(call_plot), "ggplot") - real <- paste(deparse(call_plot), collapse = "\n") - expected <- paste( - deparse( - quote( - ggplot(data_long, aes(x = .data[["Value"]], y = .data[["Category"]], group = .data[["Category"]])) + - geom_line(aes(group = .data[["Category"]]), color = "gray70", linewidth = 1) + - geom_point(aes(color = .data[["Group"]]), size = 4) + - scale_color_manual(values = c("#00BFC4", "#F8766D"), name = "") + - labs(x = xlab, y = "", title = barbell_title) + - theme_minimal() + - theme( - axis.text.y = element_text(size = 10), plot.title = element_text(hjust = 0.5, face = "bold"), - legend.position = "bottom" - ) - ) - ), - collapse = "\n" - ) - expect_identical( - real, - expected - ) -}) diff --git a/tests/testthat/test-forest-plot.R b/tests/testthat/test-forest-plot.R deleted file mode 100644 index 0cf9375..0000000 --- a/tests/testthat/test-forest-plot.R +++ /dev/null @@ -1,121 +0,0 @@ -test_that("forest_plot works with valid input", { - df <- data.frame( - Treatment = c("Treatment A", "Treatment B", "Treatment C"), - Estimate = c(0.2, 0.5, -0.1), - CI_lower = c(0.1, 0.3, -0.3), - CI_upper = c(0.3, 0.7, 0.1) - ) - - plot <- forest_plot(df, "Estimate", "CI_lower", "CI_upper", "Treatment") - - expect_s3_class(plot, "ggplot") - - expect_equal(plot$labels$x, "Estimate") - expect_equal(plot$labels$title, "Forest Plot") - - expected_labels <- unique(df$Treatment) - actual_labels <- ggplot_build(plot)$layout$panel_params[[1]]$y$get_labels() - expect_equal(actual_labels, expected_labels) -}) - -test_that("forest_plot handles missing columns appropriately", { - df_missing <- data.frame( - Treatment = c("Treatment A", "Treatment B", "Treatment C"), - Estimate = c(0.2, 0.5, -0.1) - ) - - expect_error( - forest_plot(df_missing, "Estimate", "CI_lower", "CI_upper", "Treatment"), - "The following required columns are missing in 'data': CI_lower, CI_upper" - ) -}) - -test_that("forest_plot works with custom axis labels and title", { - df <- data.frame( - Variable = c("Var1", "Var2", "Var3"), - Est = c(1.2, 0.8, 1.5), - Lower = c(0.9, 0.5, 1.1), - Upper = c(1.5, 1.1, 1.9) - ) - - plot <- forest_plot( - df, - estimate_col = "Est", - ci_lower_col = "Lower", - ci_upper_col = "Upper", - label_col = "Variable", - xlab = "Odds Ratio", - title = "Custom Forest Plot" - ) - - expect_s3_class(plot, "ggplot") - - expect_equal(plot$labels$x, "Odds Ratio") - expect_equal(plot$labels$title, "Custom Forest Plot") -}) - -test_that("forest_plot handles non-numeric data gracefully", { - df_non_numeric <- data.frame( - Treatment = c("Treatment A", "Treatment B", "Treatment C"), - Estimate = c("0.2", "0.5", "-0.1"), # Estimates as strings - CI_lower = c(0.1, 0.3, -0.3), - CI_upper = c(0.3, 0.7, 0.1) - ) - - df_non_numeric$Estimate <- as.character(df_non_numeric$Estimate) - - expect_error( - forest_plot(df_non_numeric, "Estimate", "CI_lower", "CI_upper", "Treatment"), - "must be numeric" - ) -}) - -test_that("forest_plot can handle data with additional irrelevant columns", { - df_extra <- data.frame( - Treatment = c("Treatment A", "Treatment B", "Treatment C"), - Estimate = c(0.2, 0.5, -0.1), - CI_lower = c(0.1, 0.3, -0.3), - CI_upper = c(0.3, 0.7, 0.1), - Extra1 = c("A", "B", "C"), - Extra2 = 1:3 - ) - - plot <- forest_plot(df_extra, "Estimate", "CI_lower", "CI_upper", "Treatment") - - expect_s3_class(plot, "ggplot") -}) - -test_that("forest_plot ggcall", { - df_extra <- data.frame( - Treatment = c("Treatment A", "Treatment B", "Treatment C"), - Estimate = c(0.2, 0.5, -0.1), - CI_lower = c(0.1, 0.3, -0.3), - CI_upper = c(0.3, 0.7, 0.1), - Extra1 = c("A", "B", "C"), - Extra2 = 1:3 - ) - - plot <- forest_plot(df_extra, "Estimate", "CI_lower", "CI_upper", "Treatment") - expect_s3_class(plot, "ggcall") - call_plot <- ggcall(plot) - expect_s3_class(call_plot, "ggcall_code") - expect_s3_class(eval_ggcall(call_plot), "ggplot") - real <- paste(deparse(call_plot), collapse = "\n") - expected <- paste( - deparse( - quote( - ggplot(forest_data, aes(x = .data[[estimate_col]], y = .data[[label_col]])) + - geom_point(size = 3) + - geom_errorbarh(aes(xmin = .data[[ci_lower_col]], xmax = .data[[ci_upper_col]]), height = 0.2) + - labs(x = xlab, y = "", title = forest_title) + - theme_minimal() + - theme(axis.text.y = element_text(size = 10), plot.title = element_text(hjust = 0.5, face = "bold")) - ) - ), - collapse = "\n" - ) - expect_identical( - real, - expected - ) -}) From aeb41f27ebd1713714e3663281828a55a7075af0 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Thu, 21 Nov 2024 23:58:31 +0100 Subject: [PATCH 24/29] clean check --- DESCRIPTION | 3 --- NAMESPACE | 2 -- README.md | 20 +++++--------- man/barbell_plot.Rd | 48 ---------------------------------- man/forest_plot.Rd | 49 ----------------------------------- man/ggcall.Rd | 6 ++--- man/ggcall_add_assignments.Rd | 6 ++--- 7 files changed, 12 insertions(+), 122 deletions(-) delete mode 100644 man/barbell_plot.Rd delete mode 100644 man/forest_plot.Rd diff --git a/DESCRIPTION b/DESCRIPTION index e56cfb3..73ad0ec 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -21,11 +21,8 @@ Depends: Imports: patchwork Suggests: - knitr, usethis, - rmarkdown, styler, testthat (>= 3.0.0) RoxygenNote: 7.3.2 Config/testthat/edition: 3 -VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index 4eb6ede..54ffc15 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,9 +6,7 @@ S3method("+",gg) S3method("-",ggcall) S3method("/",ggcall) S3method("|",ggcall) -export(barbell_plot) export(eval_ggcall) -export(forest_plot) export(ggcall) export(ggcall_add_assignments) export(ggcall_env) diff --git a/README.md b/README.md index ebb3d5d..449e843 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,10 @@ Then, each template will generate the expected plot, and the ggplot2 code behind ## Example +[The ggcall_example repository](https://github.com/Polkas/ggcall_example) contains a simple implementation of ggcall. `forest_plot` and `barbell` functions are a part of `ggcall.example` package. Typically, it will be a function returning `ggplot2` object from your own R package where you implemented `ggcall`. -[the ggcall_example repository](https://github.com/Polkas/ggcall_example) contains a simple implementation of ggcall. - ```r remotes::install_github("https://github.com/Polkas/ggcall_example") library(ggcall.example) @@ -53,18 +52,6 @@ styler::style_text(paste(deparse(ggcall_add_assignments(call_forest)), collapse # Optionally: reevaulate the call eval_ggcall(call_forest) - -df <- data.frame( - Category = c("A", "B", "C", "D"), - Before = c(3.5, 4.2, 2.8, 5.1), - After = c(4.0, 4.5, 3.1, 5.5) -) - -gg_barbell <- barbell_plot(df, "Category", "Before", "After", group_labels = c("Before", "After")) - -call_barbell <- ggcall(gg_barbell) - -eval_ggcall(gg_barbell) ``` ## Implementation in Your Own Package @@ -128,6 +115,11 @@ GENERAL COMMENTS: # e.g. GGally package requires such step ``` +``` +consider moving the ggplot2 from DESCRIPTION Imports to Depends +if your users will benefit from extending results with further layers +``` + ## Note The solution is in the development and is expected not to work in specific situations. diff --git a/man/barbell_plot.Rd b/man/barbell_plot.Rd deleted file mode 100644 index fee75da..0000000 --- a/man/barbell_plot.Rd +++ /dev/null @@ -1,48 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/barbell-plot.R -\name{barbell_plot} -\alias{barbell_plot} -\title{General Barbell Plot Function} -\usage{ -barbell_plot( - barbell_data, - category_col, - value1_col, - value2_col, - group_labels = c("Value 1", "Value 2"), - xlab = "Value", - title = "Barbell Plot" -) -} -\arguments{ -\item{barbell_data}{A data frame containing the data for the barbell plot.} - -\item{category_col}{The name of the column containing the category labels (as a string).} - -\item{value1_col}{The name of the first value column to compare (as a string).} - -\item{value2_col}{The name of the second value column to compare (as a string).} - -\item{group_labels}{A character vector of length 2 providing labels for the two values. Defaults to `c("Value 1", "Value 2")`.} - -\item{xlab}{The label for the x-axis. Defaults to "Value".} - -\item{title}{The title of the plot. Defaults to "Barbell Plot".} -} -\value{ -A ggplot object representing the barbell plot. -} -\description{ -Creates a barbell plot from a data frame containing two sets of values to compare across categories. -} -\examples{ -# Example data -df <- data.frame( - Category = c("A", "B", "C", "D"), - Before = c(3.5, 4.2, 2.8, 5.1), - After = c(4.0, 4.5, 3.1, 5.5) -) - -# Create the barbell plot -barbell_plot(df, "Category", "Before", "After", group_labels = c("Before", "After")) -} diff --git a/man/forest_plot.Rd b/man/forest_plot.Rd deleted file mode 100644 index 41f1b9d..0000000 --- a/man/forest_plot.Rd +++ /dev/null @@ -1,49 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/forest-plot.R -\name{forest_plot} -\alias{forest_plot} -\title{Forest Plot Function} -\usage{ -forest_plot( - forest_data, - estimate_col, - ci_lower_col, - ci_upper_col, - label_col, - xlab = "Estimate", - title = "Forest Plot" -) -} -\arguments{ -\item{forest_data}{A data frame containing the data for the forest plot.} - -\item{estimate_col}{The name of the column containing the point estimates (as a string).} - -\item{ci_lower_col}{The name of the column containing the lower bounds of the confidence intervals (as a string).} - -\item{ci_upper_col}{The name of the column containing the upper bounds of the confidence intervals (as a string).} - -\item{label_col}{The name of the column containing the labels for each estimate (as a string).} - -\item{xlab}{The label for the x-axis. Defaults to "Estimate".} - -\item{title}{The title of the plot. Defaults to "Forest Plot".} -} -\value{ -A ggplot object representing the forest plot. -} -\description{ -Creates a forest plot from a data frame containing estimates and confidence intervals. -} -\examples{ -# Example data -df <- data.frame( - Treatment = c("Treatment A", "Treatment B", "Treatment C"), - Estimate = c(0.2, 0.5, -0.1), - CI_lower = c(0.1, 0.3, -0.3), - CI_upper = c(0.3, 0.7, 0.1) -) - -# Create the forest plot -forest_plot(df, "Estimate", "CI_lower", "CI_upper", "Treatment") -} diff --git a/man/ggcall.Rd b/man/ggcall.Rd index 9bb4e0f..69ead5c 100644 --- a/man/ggcall.Rd +++ b/man/ggcall.Rd @@ -42,8 +42,8 @@ plot_call <- ggcall(func(mtcars, "wt", "mpg")) # Optionally: Style the code with styler # deparse1 is recommended and available in R>=4.0.0 \dontrun{ - styler::style_text( - paste(deparse(plot_call), collapse = "\n") - ) +styler::style_text( + paste(deparse(plot_call), collapse = "\n") +) } } diff --git a/man/ggcall_add_assignments.Rd b/man/ggcall_add_assignments.Rd index e7f6a4c..c2dedc6 100644 --- a/man/ggcall_add_assignments.Rd +++ b/man/ggcall_add_assignments.Rd @@ -52,9 +52,9 @@ plot_call <- ggcall(func(mtcars, "wt", "mpg")) # Optionally: Add assignments plot_call_with_assignments <- ggcall_add_assignments(plot_call) \dontrun{ - styler::style_text( - paste(deparse(plot_call_with_assignments), collapse = "\n") - ) +styler::style_text( + paste(deparse(plot_call_with_assignments), collapse = "\n") +) } eval_ggcall(plot_call_with_assignments) From 1a990d41e8dd2fb21c3bd76c92397b9b02f99d16 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Fri, 22 Nov 2024 00:02:38 +0100 Subject: [PATCH 25/29] clean check --- tests/testthat/test_ggcall_with_assignments.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/testthat/test_ggcall_with_assignments.R b/tests/testthat/test_ggcall_with_assignments.R index 0ee330c..34a3890 100644 --- a/tests/testthat/test_ggcall_with_assignments.R +++ b/tests/testthat/test_ggcall_with_assignments.R @@ -26,7 +26,10 @@ test_that("ggcall_add_assignments correctly adds assignments", { expect_true(grepl("data <-", paste(deparse(result_call), collapse = "\n"))) expect_true(grepl("x <-", paste(deparse(result_call), collapse = "\n"))) expect_true(grepl("y <-", paste(deparse(result_call), collapse = "\n"))) - expect_true(grepl('data <- ggcall_env\\(plot_call\\)\\[\\[\\"data\\"\\]\\]', paste(deparse(result_call), collapse = "\n"))) + expect_true( + grepl('data <- ggcall_env\\(plot_call\\)\\[\\[\\"data\\"\\]\\]', + paste(deparse(result_call), collapse = "\n")) + ) expect_true(grepl('x <- \\"wt\\"', paste(deparse(result_call), collapse = "\n"))) expect_true(grepl('y <- \\"mpg\\"', paste(deparse(result_call), collapse = "\n"))) expect_true(grepl("ggplot\\(data", paste(deparse(result_call), collapse = "\n"))) From 8444f5022cdecb9731aa7d21836a1d06dd28702b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 23:03:39 +0000 Subject: [PATCH 26/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/testthat/test_ggcall_with_assignments.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test_ggcall_with_assignments.R b/tests/testthat/test_ggcall_with_assignments.R index 34a3890..62a838f 100644 --- a/tests/testthat/test_ggcall_with_assignments.R +++ b/tests/testthat/test_ggcall_with_assignments.R @@ -27,8 +27,10 @@ test_that("ggcall_add_assignments correctly adds assignments", { expect_true(grepl("x <-", paste(deparse(result_call), collapse = "\n"))) expect_true(grepl("y <-", paste(deparse(result_call), collapse = "\n"))) expect_true( - grepl('data <- ggcall_env\\(plot_call\\)\\[\\[\\"data\\"\\]\\]', - paste(deparse(result_call), collapse = "\n")) + grepl( + 'data <- ggcall_env\\(plot_call\\)\\[\\[\\"data\\"\\]\\]', + paste(deparse(result_call), collapse = "\n") + ) ) expect_true(grepl('x <- \\"wt\\"', paste(deparse(result_call), collapse = "\n"))) expect_true(grepl('y <- \\"mpg\\"', paste(deparse(result_call), collapse = "\n"))) From 8261d710bc09b95ec15751440f7d8eafaccc7e50 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Fri, 22 Nov 2024 00:06:07 +0100 Subject: [PATCH 27/29] edit NEWS --- NEWS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index fa57f4e..3c079c1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,7 @@ # ggcall v0.3.3 -* First CRAN release. -* Support usethis standalone, usethis::use_standalone can be used on ggcall to not depend on the ggcall package rather copy paste file to your package. +* Support usethis standalone, `usethis::use_standalone` can be used with ggcall. +* Added a reference to `ggcall.example` package, an example implementation of ggcall. # ggcall v0.3.0 From 178193f4b044dc2a5534989aa01a5919b7d5083f Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Fri, 22 Nov 2024 00:06:41 +0100 Subject: [PATCH 28/29] edit NEWS --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 3c079c1..c4c5905 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # ggcall v0.3.3 -* Support usethis standalone, `usethis::use_standalone` can be used with ggcall. +* Added support for usethis standalone, `usethis::use_standalone` can be used with ggcall. * Added a reference to `ggcall.example` package, an example implementation of ggcall. # ggcall v0.3.0 From 54726c4bf128065d62d8d8ee1ae1039459535cc5 Mon Sep 17 00:00:00 2001 From: Maciej Nasinski Date: Fri, 22 Nov 2024 00:10:43 +0100 Subject: [PATCH 29/29] edit NEWS --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 449e843..88ea380 100644 --- a/README.md +++ b/README.md @@ -116,8 +116,8 @@ GENERAL COMMENTS: ``` ``` -consider moving the ggplot2 from DESCRIPTION Imports to Depends -if your users will benefit from extending results with further layers +# Consider moving the ggplot2 from DESCRIPTION Imports to Depends +# if your users will benefit from extending results with further layers ``` ## Note