Dplyr If_Else() VS Base R Ifelse()

dplyr if_else() vs base R ifelse()

if_else is more strict. It checks that both alternatives are of the same type and otherwise throws an error, while ifelse will promote types as necessary. This may be a benefit in some circumstances, but may otherwise break scripts if you don't check for errors or explicitly force type conversion. For example:

ifelse(c(TRUE,TRUE,FALSE),"a",3)
[1] "a" "a" "3"
if_else(c(TRUE,TRUE,FALSE),"a",3)
Error: `false` must be type character, not double

`dplyr::if_else()` compared to base R `ifelse()` - why rbindlist error?

Not a flextable expert but after breaking down your problem I observe

df <- tibble::tibble(col1 = c(5, 2), col2 = c(6, 4))

ifelse(apply(df[, 1:2], 1, sum) > 10 & df[, 2] > 5, "True", "False")
# col2
#[1,] "True"
#[2,] "False"

which is 2 X 1 matrix and

dplyr::if_else(apply(df[, 1:2], 1, sum) > 10 & df[, 2] > 5, "True", "False")
#[1] "True" "False"

is a character vector. So if you do

df2 <- tibble(col1 = c(5, 2), col2 = c(6, 4)) %>% 
mutate(col3 = as.character(ifelse(apply(.[, 1:2], 1, sum) > 10 & .[, 2] > 5,
"True", "False")))

it works as expected.

Use dplyr's if_else function with functional true/false values

You shouldn't use ifelse for this. ifelse and if_else are for vectors--specifically for when your input is a vector and your output is a vector of the same length. You can't make vectors of functions, so ifelse is a poor choice for returning a function. Your code will only work in the special case when the input has length 1---which is what the control/flow function if() is for.

Your ifelse code will fail with a strange error if x has length > 1, try setting x = 2:3 and running your code.

> x = 2:3
> ifelse(x == 2, min, max)
Error in rep(yes, length.out = len) :
attempt to replicate an object of type 'builtin'

if(), on the other hand, will still check the first element and return the correct result for the first element while giving you a warning about the input length:

> if(x == 2) min else max
function (..., na.rm = FALSE) .Primitive("min")
Warning message:
In if (x == 2) min else max :
the condition has length > 1 and only the first element will be used

When you're checking a condition that should always have length 1 you should use if(){}else{}, and the result can be literally any expression, it can be assigned, or it can be arbitrary code that is run. Save ifelse (and if_else) for vectors as intended.

Problem with ifelse() or dplyr::if_else() for dates

mydates is a character vector (class(mydates)), so when you return mydates as it is from the else part you are returning character object and not date. You can use -

dplyr::if_else(is.na(mydates), Sys.Date(), as.Date(mydates))

Or without if_else -

mydates <- as.Date(mydates)
mydates[is.na(mydates)] <- Sys.Date()

Else part of an if_else condition dplyr

Three problems:

  1. Your t* columns in this sample data are of class logical, not character, so I'll coerce them in the first mutate. (This step may not be necessary with your real data.)

  2. You cannot use NULL in any portion of an ifelse/if_else. Since you want to retain the previous value if the condition is not met, then we'll use the . placeholder that across uses.

  3. Using any(.) inside the if_else collapses the length of the first argument condition= will always be seen as length-1; instead, we need to repeat the return from any(.) for as many rows as we have, using rep(.., n()).

df %>%
mutate(across(starts_with("t"), as.character)) %>%
group_by(v2) %>%
mutate(across(starts_with("t"), ~ if_else(rep(any(v3 == 1), n()), "NAv", .) )) %>%
ungroup()
# # A tibble: 6 x 6
# v1 v2 v3 t1 t2 t3
# <chr> <dbl> <dbl> <chr> <chr> <chr>
# 1 Fran 1201 1 NAv NAv NAv
# 2 Fran 1201 2 NAv NAv NAv
# 3 Fran 1202 1 NAv NAv NAv
# 4 Belg 1203 1 NAv NAv NAv
# 5 Belg 1204 3 NA NA NA
# 6 Belg 1205 1 NAv NAv NAv

R - Looking for a better syntax in a dplyr::mutate/if_else combination

ifelse/if_else are for vectors. You should continue using if as you have already identified.

library(dplyr)

setting1 = TRUE

iris %>%
slice_head(n=5) %>%
mutate( NewSL = if(setting1) Sepal.Length*10 else Sepal.Length/10)

# Sepal.Length Sepal.Width Petal.Length Petal.Width Species NewSL
#1 5.1 3.5 1.4 0.2 setosa 51
#2 4.9 3.0 1.4 0.2 setosa 49
#3 4.7 3.2 1.3 0.2 setosa 47
#4 4.6 3.1 1.5 0.2 setosa 46
#5 5.0 3.6 1.4 0.2 setosa 50

base::ifelse() within dplyr::arrange() for conditional arrangement of grouped rows

The idea to use ifelse(ori == "f", ite, desc(ite)) is basically good, unfortunately desc(ite) has a negative numeric vector as output, whereas the output of ite is a character vector.

ifelse(df$ori == "f", df$ite, dplyr::desc(df$ite))
#> [1] "A" "B" "C" "-1" "-3" "-5"

To bring the result of ite in reverse order using the same output as input we can write a function asc() which just does the opposite of desc():

asc <- function(x) {
xtfrm(x)
}

No we can use both inside ifelse():

library(dplyr)

df <- data.frame(grp = c(1,1,1,2,2,2),
ori = c("f","f","f","r","r","r"),
ite = c("A","B","C","A","B","C"))

df %>%
arrange(ori, ifelse(ori == "r", desc(ite), asc(ite)))

#> grp ori ite
#> 1 1 f A
#> 2 1 f B
#> 3 1 f C
#> 4 2 r C
#> 5 2 r B
#> 6 2 r A

Created on 2022-08-21 by the reprex package (v2.0.1)



Related Topics



Leave a reply



Submit