web-dev-qa-db-fra.com

if_else () `false` doit être de type double, pas entier - dans R

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))
8
stackinator

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

18
avid_useR