markfairbanks / tidytable

Tidy interface to 'data.table'
https://markfairbanks.github.io/tidytable/
Other
449 stars 33 forks source link

ifelse and if_else #801

Closed jfdesomzee closed 7 months ago

jfdesomzee commented 7 months ago

Hello,

So far I understand tidytable::if_else is a faster version of base::ifelse. Well that's not always the case as it's evaluate both outcomes. Suppose I want to make a function that only computes a complicated function in rare cases, for exemple:

long_function <- function() {
Sys.sleep(3)
  return(3)
  }

iris %>% tidytable::mutate(Test=ifelse(Sepal.Length<3,long_function(),1))
iris %>% tidytable::mutate(Test=base::ifelse(Sepal.Length<3,long_function(),1))

I also found convenient to be able to have different outcome types without getting an error:


iris %>% mutate(Test=ifelse(Sepal.Length<3,"S",1))
Error in `tidytable::if_else()`:
! Can't combine `true` <character> and `false` <double>.
iris %>% tidytable::mutate(Test=base::ifelse(Sepal.Length<3,"S",1))

What I don't understand is that there is no function ifelse in tidytable, R interprets ifelse as if_else I think because of the usage of tidytable::mutate. Is there another way to avoid that behavior without adding base:: in front of every ifelse in my code?

Tks in advance

markfairbanks commented 7 months ago

Well that's not always the case as it's evaluate both outcomes.

Yep you're right, it isn't the same in this case. This is also how dplyr::if_else() works.

What I don't understand is that there is no function ifelse in tidytable, R interprets ifelse as if_else I think because of the usage of tidytable::mutate.

Yes this is a semi-hidden feature of tidytable - it automatically substitutes in the faster tidytable::if_else().

Is there another way to avoid that behavior without adding base:: in front of every ifelse in my code?

As you found, the best way to escape this behavior is to namespace the function using base::ifelse(). If that's too much typing you can always do something like creating your own function with a slightly different name. I used my_ifelse() in this example but you could name it anything you want.

library(tidytable)

df <- tidytable(x = 1:3)

my_ifelse <- function(...) {
  ifelse(...)
}

df %>%
  mutate(x_check = my_ifelse(x >= 1, "x", stop("condition not met")))
#> # A tidytable: 3 × 2
#>       x x_check
#>   <int> <chr>  
#> 1     1 x      
#> 2     2 x      
#> 3     3 x

Hope this helps! If you have any questions please let me know.