使用 dplyr 中的数字索引替换变量的部分内容。我需要创建索引列并使用 ifelse 吗?

标签 r dplyr

dplyr 较长链的一个阶段函数中,我需要使用数字索引替换变量的一部分来指定要替换的元素。

我的数据如下所示:

df1 <- data.frame(grp = rep(1:2, each = 3),
                  a = 1:6,
                  b = rep(c(10, 20), each = 3))
df1   
#   grp a  b
# 1   1 1 10
# 2   1 2 10
# 3   1 3 10
# 4   2 4 20
# 5   2 5 20
# 6   2 6 20

假设我们在每个组中希望替换变量 a 中的元素与 b 中的相应元素,在一个或多个位置。在这个简单的示例中,我使用单个索引( id ),但这可能是索引向量。首先,我将如何使用 ddply 来做到这一点:

library(plyr)
id <- 2    
ddply(.data = df1, .variables = .(grp), function(x){
  x$a[id] <- x$b[id]
  x
})

#   grp  a  b
# 1   1  1 10
# 2   1 10 10
# 3   1  3 10
# 4   2  4 20
# 5   2 20 20
# 6   2  6 20

dplyr我可以想出一些不同的方法来执行替换。 (1)使用do使用匿名函数,类似于 ddply 中使用的函数。 (2)使用mutate :使用数字索引连接一个向量,其中替换是“插入”的。这可能只对单个索引有效。 (3)使用mutate :创建索引向量并使用 ifelse 进行条件替换(例如,参见 herehereherehere )。

detach("package:plyr", unload = TRUE)
library(dplyr)

# (1)
fun_do <- function(df){
  l <- df %.%
    group_by(grp) %.%
    do(function(dat){
      dat$a[id] <- dat$b[id]
      dat
    })
  do.call(rbind, l)
}

# (2)
fun_mut <- function(df){
  df %.%
  group_by(grp) %.%
  mutate(
    a = c(a[1:(id - 1)], b[id], a[(id + 1):length(a)])
    )
}

# (3)
fun_mut_ifelse <- function(df){
  df %.%
    group_by(grp) %.%
    mutate(
      idx = 1:n(),
      a = ifelse(idx %in% id, b, a)) %.%
    select(-idx)
}

fun_do(df1)
fun_mut(df1)
fun_mut_ifelse(df1)

在数据集稍大的基准测试中,“拼图插入”速度最快,但同样,此方法可能只适合单个替换。而且看起来不太干净......

set.seed(123)
df2 <- data.frame(grp = rep(1:200, each = 3),
                  a = rnorm(600),
                  b = rnorm(600))

library(microbenchmark)
microbenchmark(fun_do(df2),
               fun_mut(df2),
               fun_mut_ifelse(df2),
               times = 10)

# Unit: microseconds
#                expr       min        lq    median        uq       max neval
#         fun_do(df2) 48443.075 49912.682 51356.631 53369.644 55108.769    10
#        fun_mut(df2)   891.420   933.996  1019.906  1066.663  1155.235    10
# fun_mut_ifelse(df2)  2503.579  2667.798  2869.270  3027.407  3138.787    10

只是为了检查 do.call(rbind 的影响力参与do函数,尝试不使用它:

fun_do2 <- function(df){
  df %.%
    group_by(grp) %.%
    do(function(dat){
      dat$a[2] <- dat$b[2]
      dat
    })
}
fun_do2(df1)

然后是更大数据集上的新基准:

df3 <- data.frame(grp = rep(1:2000, each = 3),
                  a = rnorm(6000),
                  b = rnorm(6000))

microbenchmark(fun_do(df3),
               fun_do2(df3),
               fun_mut(df3),
               fun_mut_ifelse(df3),
               times = 10)

同样,简单的“插入”速度最快,而 do功能正在失势。在帮助文本中do被描述为另一个 dplyr 的“通用补充”功能。对我来说,这似乎是匿名函数的自然选择。然而,令我惊讶的是do速度慢得多,当非 dplyr 时也是如此。 rbind ing 部分被跳过。目前,do文档相当稀缺,所以我想知道我是否滥用了该功能,并且可能有更合适的(未记录的?)方法 do它?

当我搜索 dplyr help text 时,我没有找到任何索引。或vignette 。所以现在我想知道:
还有其他的dplyr使用我忽略的数字索引替换变量部分的方法?具体来说就是结合ifelse创建索引列该怎么走,或者有没有更直接的a[i] <- b[i] -类似的替代品?

<小时/>

编辑以下来自 @G.Grothendieck 的评论(谢谢!)。已添加replace替代方案(?[ 中“另请参阅”的候选者)。

fun_replace <- function(df){
  df %.%
    group_by(grp) %.%
    mutate(
      a = replace(a, id, b[id]))
}
fun_replace(df1)

microbenchmark(fun_do(df3),
               fun_do2(df3),
               fun_mut(df3),
               fun_mut_ifelse(df3),
               fun_replace(df3),
               times = 10)

# Unit: milliseconds
#                expr        min         lq     median         uq        max neval
#         fun_do(df3) 685.154605 693.327160 706.055271 712.180410 851.757790    10
#        fun_do2(df3) 291.787455 294.047747 297.753888 299.624730 302.368554    10
#        fun_mut(df3)   5.736640   5.883753   6.206679   6.353222   7.381871    10
# fun_mut_ifelse(df3)  24.321894  26.091049  29.361553  32.649924  52.981525    10
#    fun_replace(df3)   4.616757   4.748665   4.981689   5.279716   5.911503    10

replace函数速度最快,而且肯定比 fun_mut 更容易使用当有多个索引时。

编辑 2 fun_dofun_do2不再适用于 dplyr 0.2 ; Error: Results are not data frames at positions:

最佳答案

这是一种更快的就地修改方法:

library(data.table)

# select rows we want, then assign b to a for those rows, in place
fun_dt = function(dt) dt[dt[, .I[id], by = grp]$V1, a := b]

# benchmark
df4 = data.frame(grp = rep(1:20000, each = 3),
                 a = rnorm(60000),
                 b = rnorm(60000))
dt4 = as.data.table(df4)

library(microbenchmark)

# using fastest function from OP
microbenchmark(fun_dt(dt4), fun_replace(df4), times = 10)
#Unit: milliseconds
#             expr      min        lq    median        uq       max neval
#      fun_dt(dt4) 15.62325  17.22828  18.42445  20.83768  21.25371    10
# fun_replace(df4) 99.03505 107.31529 116.74830 188.89134 286.50199    10

关于使用 dplyr 中的数字索引替换变量的部分内容。我需要创建索引列并使用 ifelse 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23642811/

相关文章:

r - 用分类变量拟合 nls 模型

r - 在 R 图中用百分比标记

RGB 到十六进制转换器

r - 使用 dplyr 连接组内的所有行

根据前一个变量的值重命名变量

r - 当存在应保留 NA 的 NA 时,在数据框中填充数据

r - 每小时时间序列绘图

r - 列表值的全部与全部集合交集

rvest:for循环/映射使用html_node和html_table拉取多个表

r - 从基于向量的数据框中访问变量列表(的属性)