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:
Your
t*
columns in this sample data are of classlogical
, notcharacter
, so I'll coerce them in the firstmutate
. (This step may not be necessary with your real data.)You cannot use
NULL
in any portion of anifelse
/if_else
. Since you want to retain the previous value if the condition is not met, then we'll use the.
placeholder thatacross
uses.Using
any(.)
inside theif_else
collapses the length of the first argumentcondition=
will always be seen as length-1; instead, we need torep
eat the return fromany(.)
for as many rows as we have, usingrep(.., 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
Why Would R Use the "L" Suffix to Denote an Integer
"Correct" Way to Specifiy Optional Arguments in R Functions
Convert Data.Frame Column to a Vector
Use Trycatch Skip to Next Value of Loop Upon Error
How to Use R with Google Colaboratory
Check for Installed Packages Before Running Install.Packages()
How to Use Objects from Global Environment in Rstudio Markdown
Remove Multiple Objects with Rm()
Creating a Prompt/Answer System to Input Data into R
Split Code Over Multiple Lines in an R Script
How to Check If Object (Variable) Is Defined in R
R Error in X$Ed:$ Operator Is Invalid for Atomic Vectors
Position of the Sun Given Time of Day, Latitude and Longitude