在lapply中嵌套的for循环中无法识别的变量

时间:2019-08-21 11:51:26

标签: r nested data.table lapply

我有以下数据

set.seed(42)
dat <- list(data.table(id=1:10, group=rep(1:2, each=5), x=rnorm(10)), 
            data.table(id=1:10, group=rep(1:2, each=5), x=rnorm(10)))

我想将此功能逐个元素地应用,并逐个组地应用。

subs = function(x, ..., verbose=FALSE){
  L   = substitute(list(...))[-1]
  mon = data.table(cond = as.character(L))[, skip := FALSE]

  for (i in seq_along(L)){
    d = eval( substitute(x[cond, verbose=v], list(cond = L[[i]], v = verbose)) )
    if (nrow(d)){
      x = d
    } else {
      mon[i, skip := TRUE]
    }    
  }
  #print(mon)
  return(x)
}

但是,当我运行这段代码时

# works
out <- lapply(1:2, function(h){
    res <- list()
    d <- dat[[h]] 
    for(k in 1:2){
        g <- d[group==k]
        cutoff <- 1
        print(cutoff)
        res[[k]] <- subs(g, x>cutoff)
    }
    res
})

我收到错误消息,尽管正确打印了对象cutoff。但是,当我在lapply()之外应用相同的for循环时,它似乎可以工作。

d1 <- dat[[1]]
s <- list()
for(k in 1:2){
    g <- d1[group==k]
    cutoff <- 1
    s[[k]] <- subs(g, x>cutoff)
}

> s
[[1]]
   id group        x
1:  1     1 1.370958

[[2]]
   id group        x
1:  7     2 1.511522
2:  9     2 2.018424

这使我怀疑是由lapply()中的内容引起的错误,但是我发现很难知道错误是什么,以及如何解决它。

修改

具有两个变量的数据:

set.seed(42)
dat <- list(data.table(id=1:10, group=rep(1:2, each=5), x=rnorm(10), y=11:20), 
            data.table(id=1:10, group=rep(1:2, each=5), x=rnorm(10), y=11:20))

预期结果

[[1]]
   id group          x   y
1:  9     2  2.0184237  19
2:  1     1  1.3709584  11
3:  2     1 -0.5646982  12
4:  3     1  0.3631284  13
5:  4     1  0.6328626  14
6:  5     1  0.4042683  15

[[2]]
   id group          x   y
1:  2     1  2.2866454  12
2: 10     2  1.3201133  20

1 个答案:

答案 0 :(得分:3)

如果您使用非标准评估,则您始终会付出代价。这是一个范围问题。

它是这样的:

subs = function(x, ..., verbose=FALSE){
  L   = substitute(list(...))[-1]
  mon = data.table(cond = as.character(L))[, skip := FALSE]

  for (i in seq_along(L)){
    d = eval( substitute(x[cond,, #needed to add this comma, don't know why
                           verbose=v], list(cond = L[[i]], v = verbose)))
    if (nrow(d)){
      x = d
    } else {
      mon[i, skip := TRUE]
    }    
  }
  #print(mon)
  return(x)
}

out <- lapply(1:2, function(h){
  res <- list()
  d <- dat[[h]] 
  for(k in 1:2){
    g <- d[group==k]

    cutoff <- 1
    res[[k]] <- eval(substitute(subs(g, x>cutoff), list(cutoff = cutoff)))
  }
  res
})
#works

是否有不使用data.table的by参数的特殊原因?

编辑:

  

背景:subs()的要点是要应用多个条件(如果   多个)传递给它),除非会导致一个空子集。

然后我会使用其他方法:

subs = function(x, ..., verbose=FALSE){
  L   = substitute(list(...))[-1]

  for (i in seq_along(L)){
    d = eval( substitute(x[cond, , verbose=v], list(cond = L[[i]], v = verbose)))
    x <- rbind(d, x[!d, on = "group"]) 
  }

  return(x)
}

out <- lapply(dat, function(d){

  cutoff <- 2 #to get empty groups

  eval(substitute(subs(d, x>cutoff), list(cutoff = cutoff)))

})

#[[1]]
#   id group          x
#1:  9     2  2.0184237
#2:  1     1  1.3709584
#3:  2     1 -0.5646982
#4:  3     1  0.3631284
#5:  4     1  0.6328626
#6:  5     1  0.4042683
#
#[[2]]
#   id group          x
#1:  2     1  2.2866454
#2:  6     2  0.6359504
#3:  7     2 -0.2842529
#4:  8     2 -2.6564554
#5:  9     2 -2.4404669
#6: 10     2  1.3201133

请注意,这不会保留顺序。

另一个保留顺序的选项:

subs = function(x, ..., verbose=FALSE){
  L   = substitute(list(...))[-1]

  for (i in seq_along(L)){
    x = eval( substitute(x[, {
      res <- .SD[cond];
      if (nrow(res) > 0) res else .SD 
    }, by = "group", verbose=v], list(cond = L[[i]], v = verbose)))
  }

  return(x)
}

by变量可以作为函数参数传递,然后与条件一起替换。

我还没有比较这两个效率的基准。