【问题标题】:variables not recognized in for-loop nested within lapply嵌套在 lapply 内的 for 循环中无法识别的变量
【发布时间】:2019-08-21 11:51:26
【问题描述】:

我有以下数据

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

【问题讨论】:

  • 我认为您的函数subs() 不知道对象cutoff,因为您没有通过参数传递它。 Substitute 返回解析树,但不创建截止?例如,subs = function(x, ..., verbose=FALSE, cutoff = cutoff) 和 res[[k]] &lt;- subs(g, 1 &gt; cutoff, cutoff = cutoff) 将工作编辑:在 lapply 之外,您可以在全局环境中创建它。哪个sub()可以访问那里?
  • 正确,subs() 传递x&gt;cutoff,因为它是 L 而不是x&gt;1,在subs() 的第一行插入browser() 并重新运行代码。
  • @Arcoutte,我不太明白。如果可能的话,我希望输入的条件尽可能灵活,以便我可以将subs() 应用于不同的情况。你是说我必须在函数的定义中定义变量?
  • 我检查并认为你是对的:它创建了一个全局变量。我仍然不明白如何在lapply() 中做到这一点。
  • 将subs 中的L 更改为L=list(...) 和res[[k]] 更改为res[[k]] &lt;- subs(g, substitute(x&gt;cutoff)),在subs 中适用于一个和两个条件,但我不知道它是否会扩展根据您的实际情况。

标签: r nested data.table lapply


【解决方案1】:

如果您使用非标准评估,您总是要付出代价。这是一个范围界定问题。

它是这样工作的:

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 变量可以作为函数参数传递,然后与条件一起替换。

我还没有做过比较这两种效率的基准测试。

【讨论】:

  • 您对如何使用[..., by=group] 和subs() 有什么建议吗?洗耳恭听!另外:drop=FALSE 有必要吗?我以为我读过 data.table 已经过时了。
  • 抱歉,来自测试的工件。
  • 你能改变subs吗?然后只需传递一个值,然后传递给by。
  • 背景:subs() 的重点是应用多个条件(如果将多个条件传递给它),除非一个会导致空子集。这是弗兰克对an earlier question of mine 的解决方案。只要它保留了这个特性,就可以改变它。我什至会欢迎这一点,因为组上的 for 循环并不理想。
  • 太棒了!为了改进您的编辑,我想知道subs = function(x, by, ..., verbose=FALSE){... grouping = as.character(by) ... by = as.character(grouping) ...}(by 获取分组变量的位置)是否有效。这有意义吗?
猜你喜欢
  • 2018-06-30
  • 1970-01-01
  • 2021-08-09
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
相关资源
最近更新 更多