可以使用dplyr包进行有条件的变异吗?

当突变是有条件的(取决于某些列值的值)时,可以使用mutate吗?

这个例子有助于展示我的意思。

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 

我希望find一个解决scheme,我的问题使用dplyr包(是的,我知道这不是代码,应该工作,但我想这使目的清楚)创build一个新的列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}) 

我正在寻找的代码的结果应该在这个特定的例子中得到这个结果:

  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 

有没有人有关于如何在dplyr中做到这一点的想法? 这个数据框只是一个例子,我处理的数据框要大得多。 由于它的速度,我试图使用dplyr,但也许还有其他更好的方法来处理这个问题?

使用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))) 

补充:请注意,在dplyr 0.5中定义了一个if_else函数,所以替代方法是用if_else代替if_else ; 但是请注意,由于if_elseifelse严格(条件的两条腿必须具有相同的types),所以在这种情况下NA必须用NA_real_replace。

 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_))) 

既然你要求其他更好的方法来处理这个问题,下面是使用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] 

注意条件语句的顺序是相反的,以正确地得到g 。 即使在第二次任务中,也没有g的副本 – 它被replace

在较大的数据上,这比使用嵌套的 if-else具有更好的性能,因为它可以评估“是”和“否”的情况 ,嵌套可以更难以阅读/维护恕我直言。


以下是相对较大数据的基准:

 # 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 

不知道这是否是你要求的select,但我希望它有帮助。

dplyr现在有一个函数case_when ,它提供了一个向量化的if。 与mosaic:::derivedFactor相比,语法有点奇怪,因为无法以标准dplyr方式访问variables,而需要声明NA的模式,但速度要比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))) 

编辑:如果您使用的是包的版本0.7.0之前的dplyr::case_when() ,那么您需要在variables名前添加' .$ '(例如,在case_when写入.$a == 1 )。

基准 :对于基准(从阿伦的职位重用function)和减less样本量:

 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 ) 

这给了:

  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 

mosaic包中的derivedFactor函数似乎被devise来处理这个问题。 使用这个例子,它看起来像:

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

(如果你想要结果是数字而不是一个因子,你可以用一个as.numeric调用来包装derivedFactor 。)

derivedFactor也可以用于任意数量的条件。

case_when现在是一个非常干净的SQL风格的情况下执行时:

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

使用dplyr 0.7.4

手册: http : //dplyr.tidyverse.org/reference/case_when.html