Can I make if_else() *wait* to evaluate conditional responses?

Does anyone know how to fix my code so dplyr::if_else "behaves" more like ifelse()? I think dplyr::if_else is a too anxious. I think my problem has something do with with when the true and false conditions are evaluated. Ironically, I used dplyr::if_else() for that reason (to controls execution of the true condition), but it is throwing an error instead, and I dont know how to fix it.

I believe nortest::ad.test() on the true line in the if_else() is evaluated before or may be simultaneously with the condition (big).

base::ifelse() did what I expected (see below).

# my whiny reprex ----

set.seed(42)

my_toy_data <- tibble::tibble(
  group = c(rep("A", 7), rep("B", 10)),
  num = rnorm(n = 17)
) 


my_toy_data # ok, fine.
#> # A tibble: 17 x 2
#>    group     num
#>    <chr>   <dbl>
#>  1 A      1.37  
#>  2 A     -0.565 
#>  3 A      0.363 
#>  4 A      0.633 
#>  5 A      0.404 
#>  6 A     -0.106 
#>  7 A      1.51  
#>  8 B     -0.0947
#>  9 B      2.02  
#> 10 B     -0.0627
#> 11 B      1.30  
#> 12 B      2.29  
#> 13 B     -1.39  
#> 14 B     -0.279 
#> 15 B     -0.133 
#> 16 B      0.636 
#> 17 B     -0.284

my_toy_data <- my_toy_data |>
  dplyr::group_by(group) |>
  dplyr::summarise(
    n =  dplyr::n(),
    keep_nums = num
  ) |>
  dplyr::mutate(
    big = n > 7)
#> `summarise()` has grouped output by 'group'. You can override using the
#> `.groups` argument.

my_toy_data # yes... good.
#> # A tibble: 17 x 4
#> # Groups:   group [2]
#>    group     n keep_nums big  
#>    <chr> <int>     <dbl> <lgl>
#>  1 A         7    1.37   FALSE
#>  2 A         7   -0.565  FALSE
#>  3 A         7    0.363  FALSE
#>  4 A         7    0.633  FALSE
#>  5 A         7    0.404  FALSE
#>  6 A         7   -0.106  FALSE
#>  7 A         7    1.51   FALSE
#>  8 B        10   -0.0947 TRUE 
#>  9 B        10    2.02   TRUE 
#> 10 B        10   -0.0627 TRUE 
#> 11 B        10    1.30   TRUE 
#> 12 B        10    2.29   TRUE 
#> 13 B        10   -1.39   TRUE 
#> 14 B        10   -0.279  TRUE 
#> 15 B        10   -0.133  TRUE 
#> 16 B        10    0.636  TRUE 
#> 17 B        10   -0.284  TRUE

# this is what I want... output wise
# but I dont want to use base::ifelse
my_toy_data |>
  dplyr::mutate(
    AD = ifelse(test =  big,
                yes = nortest::ad.test(keep_nums)$p.value,
                no = 0)
  )
#> # A tibble: 17 x 5
#> # Groups:   group [2]
#>    group     n keep_nums big      AD
#>    <chr> <int>     <dbl> <lgl> <dbl>
#>  1 A         7    1.37   FALSE 0    
#>  2 A         7   -0.565  FALSE 0    
#>  3 A         7    0.363  FALSE 0    
#>  4 A         7    0.633  FALSE 0    
#>  5 A         7    0.404  FALSE 0    
#>  6 A         7   -0.106  FALSE 0    
#>  7 A         7    1.51   FALSE 0    
#>  8 B        10   -0.0947 TRUE  0.166
#>  9 B        10    2.02   TRUE  0.166
#> 10 B        10   -0.0627 TRUE  0.166
#> 11 B        10    1.30   TRUE  0.166
#> 12 B        10    2.29   TRUE  0.166
#> 13 B        10   -1.39   TRUE  0.166
#> 14 B        10   -0.279  TRUE  0.166
#> 15 B        10   -0.133  TRUE  0.166
#> 16 B        10    0.636  TRUE  0.166
#> 17 B        10   -0.284  TRUE  0.166

# I want to use dplyr::if_else
# Wait, what?  Why doesn't this work?
my_toy_data |>
  dplyr::mutate(
    AD = dplyr::if_else(condition = big,
                        true = nortest::ad.test(keep_nums)$p.value,
                        false = 0)
  )
#> Error in `dplyr::mutate()`:
#> ! Problem while computing `AD = dplyr::if_else(...)`.
#> i The error occurred in group 1: group = "A".
#> Caused by error in `nortest::ad.test()`:
#> ! sample size must be greater than 7

#> Backtrace:
#>      x
#>   1. +-dplyr::mutate(...)
#>   2. +-dplyr:::mutate.data.frame(...)
#>   3. | \-dplyr:::mutate_cols(.data, dplyr_quosures(...), caller_env = caller_env())
#>   4. |   +-base::withCallingHandlers(...)
#>   5. |   \-mask$eval_all_mutate(quo)
#>   6. +-dplyr::if_else(...)
#>   7. +-nortest::ad.test(keep_nums)
#>   8. | \-base::stop("sample size must be greater than 7")
#>   9. \-base::.handleSimpleError(...)
#>  10.   \-dplyr (local) h(simpleError(msg, call))
#>  11.     \-rlang::abort(...)

Created on 2023-01-06 with reprex v2.0.2

I don't follow why you are fighting against ifelse given that you granted that it works as you would wish it to in your workflow.
if_else was designed to be a 'more anxious' ifelse - stricter - ; so it doesnt make much sense to me to want to use it over the other, given what you have said is important to you. Do you have some requirement to be in some way 'half way' between the strictness levels ? maybe say more about that ?

that said, you can 'massage' your data to work. Here I use purrr::safely to handle when nortest::ad.test is evaluated on the cases that would throw show stopping errors when if_else evaluates them (rather than ifelse skipping them))


safe_north <- purrr::safely(.f = nortest::ad.test,
                            otherwise = list(p.value=NA_real_))
my_toy_data |>
  dplyr::mutate(
    AD = dplyr::if_else(condition = big,
                        true = safe_north(keep_nums)$result$p.value,
                        false = 0)
  )
1 Like

Thank you @nirgrahamuk ! I probably should have stated my problem better, or differently. if_esle() exposed a gap in my knowledge. I have seen errors similar to this before... where parts of an analysis failed due to when or how arguments are evaluated. I dont recall them now... but I decided to make a reprex and post it this time. I have experienced similar problems, then found long and winding workarounds. I wanted to learn how to not be stopped by these problems, where the issue is not the function itself, but how it works.
Regardless, you've fixed it for me, and I am grateful. Thanks again.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.