maybe it would help, if you could give a short reprex for a case where is.null()
would throw an error. On the spot I can only think of if the object wouldn't exist, but I am not sure what other cases there may be.
Sure!
My example looked vaguely like this. I wanted to supply two expressions to a dplyr function. The second of these expressions was optional; if missing, the first expression should be used in both cases:
My initial thought was that we could do something like this:
library(tidyverse)
apply_if_not_null <- function(f, x) {
if (is.null(x)) {
x
} else {
f(x)
}
}
agg_example1 <- function(data, col1, col2 = NULL) {
col1 <- enquo(col1)
col2 <- apply_if_not_null(enquo, col2) %||% col1
data %>%
mutate(bin = cut_number(!!col2, 5)) %>%
group_by(bin) %>%
summarise(
bin_avg = mean(!!col1),
mass_avg = mean(mass, na.rm = TRUE),
n = n()
)
}
This works fine for the NULL case
agg_example1(starwars, height)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 6 x 4
#> bin bin_avg mass_avg n
#> <fct> <dbl> <dbl> <int>
#> 1 [66,165] 128. 41.9 19
#> 2 (165,175] 171. 187. 14
#> 3 (175,183] 181. 79.2 18
#> 4 (183,193] 189. 80.2 14
#> 5 (193,264] 213. 106. 16
#> 6 <NA> NA NaN 6
But not for the case where both arguments are provided:
agg_example1(starwars, height, birth_year)
#> Error in bind(enquo, col2): object 'birth_year' not found
As alluded to earlier, this specific issue can be solved using the quo_is_null
function:
agg_example2 <- function(data, col1, col2 = NULL) {
col1 <- enquo(col1)
col2 <- enquo(col2)
col2 <- if (rlang::quo_is_null(col2)) col1 else col2
data %>%
mutate(bin = cut_number(!!col2, 5)) %>%
group_by(bin) %>%
summarise(
bin_avg = mean(!!col1),
mass_avg = mean(mass, na.rm = TRUE),
n = n()
)
}
agg_example2(starwars, height)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 6 x 4
#> bin bin_avg mass_avg n
#> <fct> <dbl> <dbl> <int>
#> 1 [66,165] 128. 41.9 19
#> 2 (165,175] 171. 187. 14
#> 3 (175,183] 181. 79.2 18
#> 4 (183,193] 189. 80.2 14
#> 5 (193,264] 213. 106. 16
#> 6 <NA> NA NaN 6
agg_example2(starwars, height, birth_year)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 6 x 4
#> bin bin_avg mass_avg n
#> <fct> <dbl> <dbl> <int>
#> 1 [8,31.2] 168. 76.2 9
#> 2 (31.2,45.6] 170. 77.0 8
#> 3 (45.6,57.2] 175. 78.9 9
#> 4 (57.2,82] 179 73.6 9
#> 5 (82,896] 174. 259 8
#> 6 <NA> NA 74.0 44
Created on 2022-02-03 by the reprex package (v0.3.0)
I suppose this won't work because once you enter lazy evaluation, you have to do everything within lazy evaluation.
Btw.: I didn't mean exists("x") && is.null(x)
, because then is.null would be evaluated
Fwiw, R evaluates expressions either side of &&
lazily, so in this case x
is not evaluated:
exists("x") && x
#> [1] FALSE
x <- TRUE
exists("x") && x
#> [1] TRUE
Created on 2022-02-03 by the reprex package (v0.3.0)