La fin d'une longue chaîne de tuyaux dplyr est
mutate(n = if_else(FiscalYear == "FY2018" & Candy == "SNICKERS", n - 3, n))
ce qui donne cette erreur
Error in mutate_impl(.data, dots) : Evaluation error: `false` must be type double, not integer.
Ce qui disparaît si je fais l'un de ces deux à la place
mutate(n = ifelse(FiscalYear == "FY2018" & Candy == "SNICKERS", n - 3, n))
mutate(n = if_else(FiscalYear == "FY2018" & Candy == "SNICKERS", n - 3L, n))
Je pensais qu'il serait plus facile de faire une récréation reproductible simple, j'ai donc fait ce que vous voyez ci-dessous, mais je ne peux plus obtenir l'erreur. Une idée de ce qui se passe? Pourquoi ifelse
fonctionne là où if_else
Ne fonctionne pas, et pourquoi if_else
Fonctionne si je passe de 3 à 3L? Je comprends L
contraint 3 à être un entier, est-ce exact?
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
est de type stable, ce qui signifie qu'il vérifie si les conditions "true" et "false" sont du même type. S'ils ne le sont pas, if_else
renvoie une erreur. ifelse
dans Base R ne fait pas cela.
Lors de l'écriture:
mutate(n = if_else(FiscalYear == "FY2018" & Candy == "SNICKERS", n - 3, n))
Je suppose que n
était à l'origine un type entier, donc "false" serait de type entier, n-3
contraint "true" à un double, car 3
est double. "true" et "false" sont de types différents, donc if_else
renvoie une erreur.
Lors de l'écriture:
mutate(qty = if_else(name == "Bob" & fruit == "Apple", qty / 2, qty))
qty
est probablement déjà un double, donc en divisant un double par 2
(un double) donne toujours un double. "vrai" et "faux" sont du même type. D'où aucune erreur.
Cela étant dit, cela peut facilement être vérifié avec les typeof
s suivants:
> typeof(6)
[1] "double"
> typeof(6L)
[1] "integer"
> typeof(6L-3)
[1] "double"
> typeof(6L-3L)
[1] "integer"
> typeof(6/2)
[1] "double"
ifelse
de Base R fait une contrainte implicite, qui convertit tout au même type. Cela signifie qu'il ne génère pas d'erreur lorsque "vrai" et "faux" sont de types différents. C'est à la fois plus pratique et dangereux car il peut y avoir des résultats inattendus après une contrainte implicite.
Je recommande d'utiliser ifelse
pour les programmes ponctuels/adhoc et if_else
lorsque vous souhaitez profiter du test unitaire intégré.