带条件值的dplyr变异


87

在具有四列的大型数据帧(“ myfile”)中,我必须添加第五列,其值有条件地基于前四列。

首选使用dplyr和的答案mutate,主要是因为它在大型数据集中的速度很高。

我的数据框如下所示:

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

第五列(V5)的值基于一些条件规则:

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

现在,我想使用该mutate函数在所有行上使用这些规则(以避免慢循环)。这样的事情(是的,我知道它不能这样工作!):

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

结果应该是:

  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

如何做到这一点dplyr


声明V1..4是否都是整数(不是因数,逻辑,字符串或浮点数)是否有用?您是否关心正确处理NANaN, +Inf, -Inf)?
smci

如果速度似乎是优先考虑的问题dplyr,那么我最好使用data.table
Valentin

Answers:


105

试试这个:

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

给予:

  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

或这个:

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

给予:

  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

注意

建议您为数据框取一个更好的名称。myfile使其看起来好像拥有一个文件名。

上面使用了此输入:

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

更新1 由于最初发布的dplyr已更改%.%为,%>%因此已相应修改了答案。

dplyr Update 2现在case_when提供了另一个解决方案:

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

我尝试了您的第二个解决方案。我收到此错误:mutate_impl(.data,named_dots(...),environment())中的错误:REAL()仅适用于“数字”,而不是“逻辑”,您知道出了什么问题吗?
rdatasculptor 2014年

5
我发现了一种不让您嵌套ifelse语句的方法:myfile %>% mutate(V5 = ifelse(V1 == 1 & V2 != 4, 1, 0), V5 = ifelse(V2 == 4 & V3 != 1, 2, V5))
亚历克斯

31

使用dplyr 0.7.2,您可以使用非常有用的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)

用表示dplyr::mutate,它给出:

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

请注意,NA未将其特别对待,因为这可能会误导您。NA仅当没有条件匹配时,该函数才会返回。如果TRUE ~ ...像我在示例中所做的那样,用放一行,则返回值永远不会为NA

因此,您必须通过添加类似的语句来表达性地告诉它case_when应归于NA何处is.na(x$V1) | is.na(x$V3) ~ NA_integer_。提示:该dplyr::coalesce()功能有时在这里真的很有用!

此外,请注意,NA单独通常不工作,你必须把特殊NA值:NA_integer_NA_character_NA_real_


1
这比派生因子快得多。
Fato39

12

看起来是derivedFactormosaic包装中为此设计的。在此示例中,它将类似于:

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

(如果您想要的结果是数字,而不是一个因素,敷derivedFactoras.numeric。)

请注意,与.default选项组合使用可.method = "first"设置“其他”条件-的帮助文件中描述了此方法derivedFactor


您还可以.asFactor = F通过使用选项或derivedVariable在同一程序包中使用(类似)函数来防止结果成为因素。
杰克·费舍尔

看起来recode从dplyr 0.5开始会执行此操作。我还没有调查。参见blog.rstudio.org/2016/06/27/dplyr-0-5-0
杰克·费舍尔

对于具有1e6行的数据,这很慢。
Fato39

3
@ Fato39是的,mosaic::derivedFactor功能族非常慢。如果您知道原因,请回答关于此的我的特殊问题:stackoverflow.com/questions/33787691/…。我很高兴从您的其他评论中看到dplyr::case_when更快-我将不得不切换到该评论。
杰克·费舍尔

我正在尝试以下命令,库(马赛克)VENEZ.FINAL2 <-mutate(VENEZ,SEX = namedFactor(“ M” =(CATEGORY ==“ BULL”&CATEGORY!=“ SIRE”),“ F” =( CATEGORY ==“ COW”&CATEGORY!=“ HEIFER”),.method =“ first”,.default =“ NA”)),但是它不起作用,只需解决条件VENEZ.FINAL2 <-mutate(VENEZ,性别=派生因子(“ M” =(类别==“牛您能帮我吗?非常感谢!
约翰娜·拉米雷斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.