web-dev-qa-db-fra.com

dplyr muter avec des valeurs conditionnelles

Dans une grande base de données ("monfichier") à quatre colonnes, je dois ajouter une cinquième colonne avec des valeurs conditionnellement basées sur les quatre premières colonnes.

Préférez les réponses avec dplyr et mutate, principalement en raison de sa rapidité dans les grands ensembles de données.

Mon cadre de données ressemble à ceci:

  V1 V2 V3 V4
1  1  2  3  5
2  2  4  4  1
3  1  4  1  1
4  4  5  1  3
5  5  5  5  4
...

Les valeurs de la cinquième colonne (V5) sont basées sur des règles conditionnelles:

if (V1==1 & V2!=4) {
  V5 <- 1
} else if (V2==4 & V3!=1) {
  V5 <- 2
} else {
  V5 <- 0
}

Maintenant, je veux utiliser la fonction mutate pour utiliser ces règles sur toutes les lignes (pour éviter les boucles lentes). Quelque chose comme ça (et oui, je sais que ça ne marche pas comme ça!):

myfile <- mutate(myfile, if (V1==1 & V2!=4){V5 = 1}
    else if (V2==4 & V3!=1){V5 = 2}
    else {V5 = 0})

Cela devrait être le résultat:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

Comment faire cela dans dplyr?

63
rdatasculptor

Essaye ça:

myfile %>% mutate(V5 = (V1 == 1 & V2 != 4) + 2 * (V2 == 4 & V3 != 1))

donnant:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

ou ca:

myfile %>% mutate(V5 = ifelse(V1 == 1 & V2 != 4, 1, ifelse(V2 == 4 & V3 != 1, 2, 0)))

donnant:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

Remarque

Vous suggérons d’obtenir un meilleur nom pour votre bloc de données. myfile donne l'impression qu'il contient un nom de fichier.

Ci-dessus utilisé cette entrée:

myfile <- 
structure(list(V1 = c(1L, 2L, 1L, 4L, 5L), V2 = c(2L, 4L, 4L, 
5L, 5L), V3 = c(3L, 4L, 1L, 1L, 5L), V4 = c(5L, 1L, 1L, 3L, 4L
)), .Names = c("V1", "V2", "V3", "V4"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5"))

Mise à jour 1 Depuis le premier post, dplyr a changé %.% à %>% ont donc modifié la réponse en conséquence.

Mise à jour 2 dplyr a maintenant case_when qui fournit une autre solution:

myfile %>% 
       mutate(V5 = case_when(V1 == 1 & V2 != 4 ~ 1, 
                             V2 == 4 & V3 != 1 ~ 2,
                             TRUE ~ 0))
79
G. Grothendieck

Avec dplyr 0.7.2, Vous pouvez utiliser la fonction très utile case_when:

x=read.table(
 text="V1 V2 V3 V4
 1  1  2  3  5
 2  2  4  4  1
 3  1  4  1  1
 4  4  5  1  3
 5  5  5  5  4")
x$V5 = case_when(x$V1==1 & x$V2!=4 ~ 1,
                 x$V2==4 & x$V3!=1 ~ 2,
                 TRUE ~ 0)

Exprimé avec dplyr::mutate, Il donne:

x = x %>% mutate(
     V5 = case_when(
         V1==1 & V2!=4 ~ 1,
         V2==4 & V3!=1 ~ 2,
         TRUE ~ 0
     )
)

Veuillez noter que NA ne sont pas traités spécialement, cela peut être trompeur. La fonction ne retournera NA que si aucune condition ne correspond. Si vous mettez une ligne avec TRUE ~ ..., Comme je l'ai fait dans mon exemple, la valeur de retour ne sera alors jamais NA.

Par conséquent, vous devez explicitement dire à case_when De mettre NA à quoi il appartient en ajoutant une instruction comme is.na(x$V1) | is.na(x$V3) ~ NA_integer_. Astuce: la fonction dplyr::coalesce() peut être très utile ici parfois!

De plus, notez que NA seul ne fonctionnera généralement pas, vous devez mettre des valeurs spéciales NA: NA_integer_, NA_character_ Ou NA_real_ .

18
Dan Chaltiel

Il semble que derivedFactor du package mosaic ait été conçu pour cela. Dans cet exemple, cela ressemblerait à quelque chose comme:

library(mosaic)
myfile <- mutate(myfile, V5 = derivedFactor(
    "1" = (V1==1 & V2!=4),
    "2" = (V2==4 & V3!=1),
    .method = "first",
    .default = 0
    ))

(Si vous voulez que le résultat soit numérique au lieu d’un facteur, enveloppez le derivedFactor avec un as.numeric.)

Notez que le .default option combinée avec .method = "first" définit la condition "else" - cette approche est décrite dans le fichier d'aide de derivedFactor.

11
Jake Fisher