【问题标题】:dplyr mutate with conditional valuesdplyr 使用条件值进行变异
【发布时间】:2014-04-15 18:05:17
【问题描述】:

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

首选dplyrmutate 的答案,主要是因为它在大型数据集中的速度。

我的数据框如下所示:

  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 是否都是整数(不是因子、逻辑、字符串或浮点数)是否有用?你关心正确处理NA, (NaN, +Inf, -Inf)吗?
  • 如果速度似乎是首选dplyr的问题,那么我最好使用data.table

标签: r dplyr


【解决方案1】:

试试这个:

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 已将 %.% 更改为 %&gt;%,因此相应修改了答案。

Update 2 dplyr 现在有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() 只能应用于“数字”,而不是“逻辑”你知道出了什么问题吗?
  • 我发现了一种不嵌套ifelse 语句的方法:myfile %&gt;% mutate(V5 = ifelse(V1 == 1 &amp; V2 != 4, 1, 0), V5 = ifelse(V2 == 4 &amp; V3 != 1, 2, V5))
【解决方案2】:

看起来mosaic 包中的derivedFactor 就是为此而设计的。在这个例子中,它看起来像:

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

(如果您希望结果是数字而不是因子,请将 derivedFactor 包装为 as.numeric。)

请注意,.default 选项与.method = "first" 结合使用会设置“else”条件——derivedFactor 的帮助文件中描述了这种方法。

【讨论】:

  • 您还可以使用.asFactor = F 选项或在同一包中使用(类似的)derivedVariable 函数来防止结果成为因素。
  • 看起来像来自 dplyr 0.5 的 recode 会这样做。不过我还没有调查过。见blog.rstudio.org/2016/06/27/dplyr-0-5-0
  • 这对于我的 1e6 行数据来说很慢。
  • @Fato39 是的,mosaic::derivedFactor 系列函数非常慢。如果您知道原因,请回答我的 SO 问题:stackoverflow.com/questions/33787691/…。我很高兴从您的其他评论中看到 dplyr::case_when 更快——我必须改用它。
  • 我正在尝试以下命令,library(mosaic) VENEZ.FINAL2
【解决方案3】:

通过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

因此,您必须通过添加类似is.na(x$V1) | is.na(x$V3) ~ NA_integer_ 的语句来明确地告诉case_whenNA 放在它所属的位置。提示:dplyr::coalesce() 函数有时在这里非常有用!

此外,请注意,单独使用 NA 通常是行不通的,您必须输入特殊的 NA 值:NA_integer_NA_character_NA_real_

【讨论】:

  • 这比 derivedFactor 快得多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-24
相关资源
最近更新 更多