pacote dplyr pode ser usado para mutação condicional?

O mutate pode ser usado quando a mutação é condicional (dependendo dos valores de certos valores de coluna)?

Este exemplo ajuda a mostrar o que quero dizer.

structure(list(a = c(1, 3, 4, 6, 3, 2, 5, 1), b = c(1, 3, 4, 2, 6, 7, 2, 6), c = c(6, 3, 6, 5, 3, 6, 5, 3), d = c(6, 2, 4, 5, 3, 7, 2, 6), e = c(1, 2, 4, 5, 6, 7, 6, 3), f = c(2, 3, 4, 2, 2, 7, 5, 2)), .Names = c("a", "b", "c", "d", "e", "f"), row.names = c(NA, 8L), class = "data.frame") abcdef 1 1 1 6 6 1 2 2 3 3 3 2 2 3 3 4 4 6 4 4 4 4 6 2 5 5 5 2 5 3 6 3 3 6 2 6 2 7 6 7 7 7 7 5 2 5 2 6 5 8 1 6 3 6 3 2 

Eu estava esperando encontrar uma solução para o meu problema usando o pacote dplyr (e sim eu sei que isso não é um código que deveria funcionar, mas eu acho que deixa claro o propósito) para criar uma nova coluna g:

  library(dplyr) df <- mutate(df, if (a == 2 | a == 5 | a == 7 | (a == 1 & b == 4)){g = 2}, if (a == 0 | a == 1 | a == 4 | a == 3 | c == 4){g = 3}) 

O resultado do código que estou procurando deve ter esse resultado neste exemplo específico:

  abcdefg 1 1 1 6 6 1 2 3 2 3 3 3 2 2 3 3 3 4 4 6 4 4 4 3 4 6 2 5 5 5 2 NA 5 3 6 3 3 6 2 NA 6 2 7 6 7 7 7 2 7 5 2 5 2 6 5 2 8 1 6 3 6 3 2 3 

Alguém tem uma idéia sobre como fazer isso no dplyr? Esse quadro de dados é apenas um exemplo, os frameworks de dados com os quais estou lidando são muito maiores. Devido a sua velocidade, tentei usar o dplyr, mas talvez haja outras maneiras melhores de lidar com esse problema?

Use ifelse

 df %>% mutate(g = ifelse(a == 2 | a == 5 | a == 7 | (a == 1 & b == 4), 2, ifelse(a == 0 | a == 1 | a == 4 | a == 3 | c == 4, 3, NA))) 

Adicionado – if_else: Observe que no dplyr 0.5 existe uma function if_else definida, portanto, uma alternativa seria replace ifelse por if_else ; no entanto, note que desde if_else é mais rigoroso do que ifelse (ambas as pernas da condição deve ter o mesmo tipo) para que o NA , nesse caso, teria que ser substituído por NA_real_ .

 df %>% mutate(g = if_else(a == 2 | a == 5 | a == 7 | (a == 1 & b == 4), 2, if_else(a == 0 | a == 1 | a == 4 | a == 3 | c == 4, 3, NA_real_))) 

Adicionado – case_when Desde que esta questão foi publicada dplyr adicionou case_when assim outra alternativa seria:

 df %>% mutate(g = case_when(a == 2 | a == 5 | a == 7 | (a == 1 & b == 4) ~ 2, a == 0 | a == 1 | a == 4 | a == 3 | c == 4 ~ 3, TRUE ~ NA_real_)) 

Já que você pede outras maneiras melhores de lidar com o problema, aqui está outra maneira de usar o data.table :

 require(data.table) ## 1.9.2+ setDT(df) df[a %in% c(0,1,3,4) | c == 4, g := 3L] df[a %in% c(2,5,7) | (a==1 & b==4), g := 2L] 

Observe que a ordem das instruções condicionais é revertida para obter corretamente g . Não há cópia de g feita, mesmo durante a segunda tarefa – ela é substituída no local .

Em dados maiores, isso teria um desempenho melhor do que usar o if-else nested , já que ele pode avaliar os casos “sim” e “não” , e o aninhamento pode ficar mais difícil de ler / manter o IMHO.


Aqui está uma referência em dados relativamente maiores:

 # R version 3.1.0 require(data.table) ## 1.9.2 require(dplyr) DT < - setDT(lapply(1:6, function(x) sample(7, 1e7, TRUE))) setnames(DT, letters[1:6]) # > dim(DT) # [1] 10000000 6 DF < - as.data.frame(DT) DT_fun <- function(DT) { DT[(a %in% c(0,1,3,4) | c == 4), g := 3L] DT[a %in% c(2,5,7) | (a==1 & b==4), g := 2L] } DPLYR_fun <- function(DF) { mutate(DF, g = ifelse(a %in% c(2,5,7) | (a==1 & b==4), 2L, ifelse(a %in% c(0,1,3,4) | c==4, 3L, NA_integer_))) } BASE_fun <- function(DF) { # R v3.1.0 transform(DF, g = ifelse(a %in% c(2,5,7) | (a==1 & b==4), 2L, ifelse(a %in% c(0,1,3,4) | c==4, 3L, NA_integer_))) } system.time(ans1 <- DT_fun(DT)) # user system elapsed # 2.659 0.420 3.107 system.time(ans2 <- DPLYR_fun(DF)) # user system elapsed # 11.822 1.075 12.976 system.time(ans3 <- BASE_fun(DF)) # user system elapsed # 11.676 1.530 13.319 identical(as.data.frame(ans1), as.data.frame(ans2)) # [1] TRUE identical(as.data.frame(ans1), as.data.frame(ans3)) # [1] TRUE 

Não tenho certeza se esta é uma alternativa que você pediu, mas espero que ajude.

dplyr agora tem uma function case_when que oferece um case_when se. A syntax é um pouco estranha comparada com o mosaic:::derivedFactor já que você não pode acessar variables ​​no modo padrão dplyr, e precisa declarar o modo de NA, mas é consideravelmente mais rápido que o mosaic:::derivedFactor .

 df %>% mutate(g = case_when(a %in% c(2,5,7) | (a==1 & b==4) ~ 2L, a %in% c(0,1,3,4) | c == 4 ~ 3L, TRUE~as.integer(NA))) 

EDIT: Se você estiver usando dplyr::case_when() de antes da versão 0.7.0 do pacote, então você precisa preceder nomes de variables ​​com ‘ .$ ‘ (Por exemplo, escrever .$a == 1 inside case_when ).

Benchmark : Para o benchmark (reutilização de funções do post de Arun) e redução do tamanho da amostra:

 require(data.table) require(mosaic) require(dplyr) require(microbenchmark) DT < - setDT(lapply(1:6, function(x) sample(7, 10000, TRUE))) setnames(DT, letters[1:6]) DF <- as.data.frame(DT) DPLYR_case_when <- function(DF) { DF %>% mutate(g = case_when(a %in% c(2,5,7) | (a==1 & b==4) ~ 2L, a %in% c(0,1,3,4) | c==4 ~ 3L, TRUE~as.integer(NA))) } DT_fun < - function(DT) { DT[(a %in% c(0,1,3,4) | c == 4), g := 3L] DT[a %in% c(2,5,7) | (a==1 & b==4), g := 2L] } DPLYR_fun <- function(DF) { mutate(DF, g = ifelse(a %in% c(2,5,7) | (a==1 & b==4), 2L, ifelse(a %in% c(0,1,3,4) | c==4, 3L, NA_integer_))) } mosa_fun <- function(DF) { mutate(DF, g = derivedFactor( "2" = (a == 2 | a == 5 | a == 7 | (a == 1 & b == 4)), "3" = (a == 0 | a == 1 | a == 4 | a == 3 | c == 4), .method = "first", .default = NA )) } microbenchmark( DT_fun(DT), DPLYR_fun(DF), DPLYR_case_when(DF), mosa_fun(DF), times=20 ) 

Isto dá:

  expr min lq mean median uq max neval DT_fun(DT) 1.503589 1.626971 2.054825 1.755860 2.292157 3.426192 20 DPLYR_fun(DF) 2.420798 2.596476 3.617092 3.484567 4.184260 6.235367 20 DPLYR_case_when(DF) 2.153481 2.252134 6.124249 2.365763 3.119575 72.344114 20 mosa_fun(DF) 396.344113 407.649356 413.743179 412.412634 416.515742 459.974969 20 

A function derivedFactor do pacote mosaic parece ser projetada para lidar com isso. Usando este exemplo, seria parecido com:

 library(dplyr) library(mosaic) df < - mutate(df, g = derivedFactor( "2" = (a == 2 | a == 5 | a == 7 | (a == 1 & b == 4)), "3" = (a == 0 | a == 1 | a == 4 | a == 3 | c == 4), .method = "first", .default = NA )) 

(Se você quiser que o resultado seja numérico em vez de um fator, você pode envolver o derivedFactor em uma chamada as.numeric .)

derivedFactor pode ser usado para um número arbitrário de condicionais.

case_when agora é uma implementação bem limpa do caso do estilo SQL quando:

 structure(list(a = c(1, 3, 4, 6, 3, 2, 5, 1), b = c(1, 3, 4, 2, 6, 7, 2, 6), c = c(6, 3, 6, 5, 3, 6, 5, 3), d = c(6, 2, 4, 5, 3, 7, 2, 6), e = c(1, 2, 4, 5, 6, 7, 6, 3), f = c(2, 3, 4, 2, 2, 7, 5, 2)), .Names = c("a", "b", "c", "d", "e", "f"), row.names = c(NA, 8L), class = "data.frame") -> df df %>% mutate( g = case_when( a == 2 | a == 5 | a == 7 | (a == 1 & b == 4 ) ~ 2, a == 0 | a == 1 | a == 4 | a == 3 | c == 4 ~ 3 )) 

Usando dplyr 0.7.4

O manual: http://dplyr.tidyverse.org/reference/case_when.html