Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

if_else() `false` must be type double, not integer - in R

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))
like image 893
stackinator Avatar asked Jun 05 '18 20:06

stackinator


1 Answers

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 typeofs:

> 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.

like image 129
acylam Avatar answered Nov 13 '22 22:11

acylam