The end of a long string of dplyr pipes is
mutate(n = if_else(FiscalYear == "FY2018" & Candy == "SNICKERS", n - 3, n))
which gives this error
Error in mutate_impl(.data, dots) : Evaluation error: `false` must be type double, not integer.
Which goes away if I do either of these two instead
mutate(n = ifelse(FiscalYear == "FY2018" & Candy == "SNICKERS", n - 3, n))
mutate(n = if_else(FiscalYear == "FY2018" & Candy == "SNICKERS", n - 3L, n))
I thought it'd be easiest to make a simple reproducible recreation so I did what you see below, but I can't get the error anymore. Any idea what's going on? Why does ifelse
work where if_else
doesn't, and why does if_else
work if I change 3 to 3L? I understand L
coerces 3 to be an integer, is that correct?
library(tidyverse)
df <- tribble(
~name, ~fruit, ~qty,
"Bob", "apple", 10,
"Bill", "apple", 10
)
# THIS WORKS AGAIN AS IT SHOULD
df %>% mutate(qty = ifelse(name == "Bob" & fruit == "apple", qty / 2, qty))
# BUT IF_ELSE DOESN'T FAIL THIS TIME, WEIRD
df %>% mutate(qty = if_else(name == "Bob" & fruit == "apple", qty / 2, qty))
if_else
from dplyr
is type-stable, meaning that it checks whether the "true" and "false" conditions are the same type. If they aren't, if_else
throws an error. ifelse
in Base R does not do that.
When writing:
mutate(n = if_else(FiscalYear == "FY2018" & Candy == "SNICKERS", n - 3, n))
I assume n
was originally an integer type, so "false" would be of integer type, n-3
coerces "true" to a double, because 3
is double. "true" and "false" are of different types, so if_else
throws an error.
When writing:
mutate(qty = if_else(name == "Bob" & fruit == "apple", qty / 2, qty))
qty
is likely already a double, so dividing a double by 2
(a double) still yields a double. "true" and "false" are the same type. Hence no error.
With that being said, this can easily be checked with the following typeof
s:
> typeof(6)
[1] "double"
> typeof(6L)
[1] "integer"
> typeof(6L-3)
[1] "double"
> typeof(6L-3L)
[1] "integer"
> typeof(6/2)
[1] "double"
ifelse
from Base R does implicit coercing, which converts everything to the same type. This means that it doesn't throw an error when "true" and "false" are of different types. This is both more convenient and dangerous as there might be unexpected results after implicit coercing.
I recommend using ifelse
for one-off/adhoc programs, and if_else
for when you want to take advantage of the built-in unit test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With