diff --git a/DESCRIPTION b/DESCRIPTION index ac8fe90..73ad0ec 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 +Title: Extract 'ggplot2' Layers Created Within a Function +Version: 0.3.3 Authors@R: person(given = "Maciej", family = "Nasinski", @@ -9,20 +9,20 @@ 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. - 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. +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: +Depends: ggplot2 +Imports: + patchwork Suggests: - backports, - knitr, - patchwork, - rmarkdown, + usethis, styler, testthat (>= 3.0.0) RoxygenNote: 7.3.2 Config/testthat/edition: 3 -VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index d317611..54ffc15 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,4 +11,5 @@ export(ggcall) export(ggcall_add_assignments) export(ggcall_env) export(ggplot) -importFrom(ggplot2,ggplot) +import(ggplot2, except = c(ggplot)) +import(patchwork) diff --git a/NEWS.md b/NEWS.md index f1d59c8..c4c5905 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,8 @@ +# ggcall v0.3.3 + +* 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 * Added support for ggplot related patchwork operators. diff --git a/R/patchwork.R b/R/patchwork.R deleted file mode 100644 index e260fce..0000000 --- a/R/patchwork.R +++ /dev/null @@ -1,40 +0,0 @@ -#' @keywords internal -patch_operator_base <- function(e1, e2, operator, class) { - if (!requireNamespace("patchwork", quietly = TRUE)) { - stop("patchwork package has to be installed.") - } - plot <- utils::getFromNamespace(sprintf("%s.%s", operator, class), "patchwork")(e1, e2) - if (inherits(e1, "ggcall") && inherits(e2, "ggcall")) { - lhs <- ggcall(e1) - rhs <- ggcall(e2) - attr(plot, "ggcall") <- as.call(list(as.name(operator), lhs, rhs)) - attr(plot, "ggcall_env") <- merge_env(attr(e1, "ggcall_env"), attr(e2, "ggcall_env")) - class(plot) <- unique(c("ggcall", class(plot))) - } - plot -} - -#' @export -"-.ggcall" <- function(e1, e2) { - patch_operator_base(e1, e2, "-", "ggplot") -} - -#' @export -"/.ggcall" <- function(e1, e2) { - patch_operator_base(e1, e2, "/", "ggplot") -} - -#' @export -"|.ggcall" <- function(e1, e2) { - patch_operator_base(e1, e2, "|", "ggplot") -} - -#' @export -"*.ggcall" <- function(e1, e2) { - patch_operator_base(e1, e2, "*", "gg") -} - -#' @export -"&.ggcall" <- function(e1, e2) { - patch_operator_base(e1, e2, "&", "gg") -} diff --git a/R/ggcall.R b/R/standalone-ggcall.R similarity index 89% rename from R/ggcall.R rename to R/standalone-ggcall.R index bb59994..3c9793f 100644 --- a/R/ggcall.R +++ b/R/standalone-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 @@ -10,8 +26,9 @@ #' attribute 'ggcall' that stores the history of plot construction. #' #' @seealso \code{\link[ggplot2]{ggplot}} -#' @importFrom 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") @@ -38,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 @@ -92,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) { @@ -112,8 +132,12 @@ 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 +#' \dontrun{ +#' styler::style_text( +#' paste(deparse(plot_call), collapse = "\n") +#' ) +#' } #' @export #' ggcall <- function(plot) { @@ -143,6 +167,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) { @@ -164,14 +189,17 @@ ggcall <- function(plot) { #' 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") #' ) -#' +#' } #' 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")) @@ -232,9 +260,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) #' @@ -259,11 +292,13 @@ 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() #' } -#' 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"]] @@ -309,3 +344,5 @@ extract_names <- function(expr) { return(character()) } + +# nocov end diff --git a/R/standalone-patchwork.R b/R/standalone-patchwork.R new file mode 100644 index 0000000..260ed0a --- /dev/null +++ b/R/standalone-patchwork.R @@ -0,0 +1,103 @@ +# --- +# 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 + +#' @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)) { + stop("patchwork package has to be installed.") + } + plot <- utils::getFromNamespace(sprintf("%s.%s", operator, class), "patchwork")(e1, e2) + if (inherits(e1, "ggcall") && inherits(e2, "ggcall")) { + lhs <- ggcall(e1) + rhs <- ggcall(e2) + attr(plot, "ggcall") <- as.call(list(as.name(operator), lhs, rhs)) + attr(plot, "ggcall_env") <- merge_env(attr(e1, "ggcall_env"), attr(e2, "ggcall_env")) + class(plot) <- unique(c("ggcall", class(plot))) + } + 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 64797b1..88ea380 100644 --- a/README.md +++ b/README.md @@ -16,97 +16,109 @@ 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. +[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`. -## 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. - -Please access the [Get Started vignette](https://polkas.github.io/ggcall/articles/ggcall.html) for more information. +```r +remotes::install_github("https://github.com/Polkas/ggcall_example") +library(ggcall.example) -## Usage +# Print the body of the function +forest_plot -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. +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) +) -Here is a **simple** illustrative example with a scenario in which a function generates a `ggplot2` plot based on input data: +# Call the function, gg_plot is a ggplot object +gg_forest <- forest_plot(df, "Estimate", "CI_lower", "CI_upper", "Treatment") +gg_plot -```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") -# - -styler::style_text(backports:::deparse1(plot_call)) +call_forest <- ggcall(gg_plot) -# 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: Style the code with styler +# install.packages("styler") +styler::style_text(paste(deparse(call_forest), 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: add assignments to call +styler::style_text(paste(deparse(ggcall_add_assignments(call_forest)), collapse = "\n")) # Optionally: reevaulate the call -# Reproduce the plot by evaluating the code -eval_ggcall(plot_call) -eval_ggcall(plot_call_with_assignments) +eval_ggcall(call_forest) +``` + +## Implementation in Your Own Package + +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 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. + +``` +install.packages("usethis") +``` + +STANDALONE means copy paste the files and add dependencies to your own package. + +Please create an R package if not having such yet. -# Optionally overwrite variables -eval_ggcall(plot_call, mtcars = mtcars[1:10, ], x = "disp") +``` +usethis::create_package() ``` -Functions Reference: +WITH `patchwork` support -| 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**| +``` +# 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_standalone("polkas/ggcall", "patchwork.R", ref = "v0.3.3") +# you may need to update the files time to time with usethis::use_standalone +``` + +WITHOUT `patchwork` support + +``` +# Add ggplot2as your package dependencies +# copy paste the ggcall.R file to your own package R directory + +usethis::use_standalone("polkas/ggcall", "ggcall.R", ref = "v0.3.3") +# you may need to update the files time to time with usethis::use_standalone +``` + +GENERAL COMMENTS: + +``` +# Apply only if needed +# In your own code remove all ggplot2:: prefix-ing before ggplot function calls +# ggplot2::ggplot(...) -> ggplot(...) +``` + +``` +# DO NOT import ggplot function from ggplot2 +#' @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 +``` + +``` +# Consider moving the ggplot2 from DESCRIPTION Imports to Depends +# if your users will benefit from extending results with further layers +``` ## Note diff --git a/inst/WORDLIST b/inst/WORDLIST index 0c4bf66..6276e7d 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -2,12 +2,14 @@ ARG aut backports bracketedPaste +BugReports codecov coenraads colorizer colorTheme Config cowplot +CRAN cre dev dirmngr @@ -17,6 +19,7 @@ eamodio env forwardPorts FRONTEND +gg ggally GGally ggcall @@ -24,6 +27,7 @@ ggcall’s ggpairs ggplot Github +github gitlens gmail gnupg @@ -31,6 +35,7 @@ htop https ikuyadeu init +io iproute jq json @@ -75,8 +80,10 @@ PTRACE README REditorSupport remoteUser +repo Reproducibility reproducibility +rlang rmarkdown RoxygenNote rsync @@ -85,6 +92,7 @@ runArgs sdras seccomp setuptools +STANALONE strace streetsidesoftware Styleguide @@ -96,6 +104,7 @@ sudo SYS testthat Uncomment +usethis usr ust ver diff --git a/man/eval_ggcall.Rd b/man/eval_ggcall.Rd index 39dfaef..5ca199e 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} @@ -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/plus-.gg.Rd b/man/ggcall-add-operator.Rd similarity index 81% rename from man/plus-.gg.Rd rename to man/ggcall-add-operator.Rd index e382236..a6dd748 100644 --- a/man/plus-.gg.Rd +++ b/man/ggcall-add-operator.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} @@ -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-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.Rd b/man/ggcall.Rd index 3384842..69ead5c 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} @@ -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) { @@ -39,6 +40,10 @@ 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 +\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 4a9334a..c2dedc6 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} @@ -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) { @@ -50,12 +51,15 @@ 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) +\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 -# 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 552c209..76ecff1 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} @@ -18,11 +18,13 @@ 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() } -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/ggplot.Rd b/man/ggplot.Rd index 384f373..03db23c 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} @@ -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/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} 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..62a838f 100644 --- a/tests/testthat/test_ggcall_with_assignments.R +++ b/tests/testthat/test_ggcall_with_assignments.R @@ -23,21 +23,26 @@ 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/.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 6efa89a..0000000 --- a/vignettes/ggcall.Rmd +++ /dev/null @@ -1,323 +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") -# - -styler::style_text(backports:::deparse1(plot_call)) - -# 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(backports:::deparse1(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: 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") -``` - -## 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. - -### General - -``` -# Apply only if needed -# Remove all ggplot2:: prefix-ing before ggplot function calls -# ggplot2::ggplot(...) -> ggplot(...) -``` - -### OPTION 1 - -``` -# 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 -``` - -### OPTION 2 - -``` -# 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 -``` - -``` -# 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 -``` - -### 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.