Open sharlagelfand opened 3 years ago
I've been looking into this and it seems pretty easy when the pipeline doesn't contain ggplot2 code, but much more difficult (not possible?) when it doesn't. Some code / notes:
library(dplyr)
library(ggplot2)
#' You can get the code piped using sys.calls()
piped_code_from_call <- function(x, additional_arg = TRUE) {
sys.calls()[[1]]
}
mtcars %>%
count(cyl) %>%
piped_code_from_call()
# mtcars %>% count(cyl) %>% piped_code_from_call()
#' And then e.g. convert to character and remove the %>% piped_code_from_call() from it
piped_code_from_call <- function(x, additional_arg = TRUE) {
code <- deparse(sys.calls()[[1]])
stringr::str_remove(code, " %>% piped_code_from_call\\(\\)")
}
mtcars %>%
count(cyl) %>%
piped_code_from_call()
# [1] "mtcars %>% count(cyl)"
#' But unfortunately this doesn't work withg ggplot2 code:
mtcars %>%
count(cyl) %>%
ggplot() +
geom_point(aes(x = cyl)) %>%
piped_code_from_call()
# Error: Can't add `geom_point(aes(x = cyl)) %>% piped_code_from_call()` to a ggplot object.
# Run `rlang::last_error()` to see where the error occurred.
#' It does to some extent if you wrap the entire pipeline / ggplot2 code in parentheses
(
mtcars %>%
count(cyl) %>%
ggplot() +
geom_point(aes(x = cyl))
) %>%
piped_code_from_call()
# [1] "(mtcars %>% count(cyl) %>% ggplot() + geom_point(aes(x = cyl))) %>% "
# [2] " piped_code_from_call()"
#' But I'm not sure if different methods (i.e. no parentheses if no ggplot2 code, yes parentheses if ggplot2 code) is confusing / less desirable than just wrapping it in quotes
#' Ideally we could just pipe in, but I don't think that's possible...
I also have a feeling that this sys.calls()
approach only really works interactively, so e.g. not in RMarkdown - I tried to render a reprex with the above code (so I wouldn't have to copy the output in myself), and this is how it looks - expand for the full yuck!
@seankross: curious if you know any tricks that might work here, since you did the original pipeline parsing.
as per @sharlagelfand's comments above, seems like doing this with %>%
is possible, but (literally) adding in ggplot commands with the +
operator causes all kinds of trouble.
thanks for any ideas/tips/pointers!
Hey Jake and Sharla,
I would be happy to help think about this but I am underwater with work this week, but I can revisit this next week. All the work that has been done on this is so exciting!!
Sean
Hi Jake and Sharla,
My instinct for this to took really slick is to redefine the pipe (%>%
and/or |>
) and to redefine ggplot2's +
(implementation here: https://github.dev/tidyverse/ggplot2/blob/master/R/plot-construction.r). Here is a proof of concept:
library(tidyverse)
library(rlang)
`%>%` <- function(lhs, rhs) {
if(inherits(lhs, "quo_list")) {
result <- c(lhs, enquo(rhs))
} else {
result <- list(enquo(lhs), enquo(rhs))
}
class(result) <- "quo_list"
result
}
1:5 %>% sum()
# [[1]]
# <quosure>
# expr: ^<int: 1L, 2L, 3L, 4L, 5L>
# env: empty
#
# [[2]]
# <quosure>
# expr: ^sum()
# env: global
#
# attr(,"class")
# [1] "quo_list"
diamonds %>%
filter(carat > 1) %>%
ggplot(aes(carat, price, colour = cut)) +
geom_point() %>%
piped_code_from_call()
# [[1]]
# <quosure>
# expr: ^<tibble[,10]>
# env: empty
#
# [[2]]
# <quosure>
# expr: ^filter(carat > 1)
# env: global
#
# [[3]]
# <quosure>
# expr: ^ggplot(aes(carat, price, colour = cut))
# env: global
#
# [[4]]
# <quosure>
# expr: ^geom_point() %>% piped_code_from_call()
# env: global
#
# attr(,"class")
# [1] "quo_list"
As you can see this doesn't completely work because +
has not been redefined, and that requires some S3 magic that I was hoping you could help figure out 😁. You could probably avoid using rlang too but it made this quick to whip up. Let me know what you think.
Sean
Awesome, thanks @seankross!! Will take a look over this 🙌🏻
Would be very slick to allow the pipeline without needing to pass it as a character vector, e.g.
instead of
Have been looking into this a bit but writing it down here to track!