是否可以在数据框中使用在该数据框中定义的变量调用 eval?

Can eval be called within a data frame with variables defined in that data frame?

考虑:

quotes<-alist(x, x+1, x+2)
data.frame(x=c(5, 10, 15), lapply(quotes, eval))

这不起作用,因为 eval 找不到 x。有没有办法让 eval 识别数据框中定义的变量?我知道我可以将数据框分配给一些变量,然后使用 transform 等函数构建它,但我希望在调用 data.frame.[=17= 时完成所有这些操作]

对于预期输出的可能版本,我们可以使用:

quotes<-alist(x, x+1, x+2)
x=c(5, 10, 15)
data.frame(x, lapply(quotes, eval))

调试 data.frame(x=c(5, 10, 15), lapply(quotes, eval)) 导致:

x <- list(...)
#Error in FUN(X[[i]], ...) : object 'x' not found

这是由于对第二个参数的评估:

lapply(quotes, eval)
#Error in FUN(X[[i]], ...) : object 'x' not found

如果不事先在 environment.

中定义 x,这里似乎很难找到解决方法

tibble 允许在定义更多变量时使用之前的变量,因为它依赖于使用 quos(...) 而不是 list(...) 的非标准评估来处理参数:

function (..., .rows = NULL, .name_repair = c("check_unique", 
  "unique", "universal", "minimal")) 
{
  xs <- quos(...)
  is.null <- map_lgl(xs, quo_is_null)
  tibble_quos(xs[!is.null], .rows, .name_repair)
}

tibble:

tibble::tibble(x=c(1,2,3), result= sapply(quotes,function(quo) eval(quo)))

# A tibble: 3 x 2
      x result[,1]  [,2]  [,3]
  <dbl>      <dbl> <dbl> <dbl>
1     1          1     2     3
2     2          2     3     4
3     3          3     4     5

然而,这还不是预期的结果,因为 quotes 需要使用 x 按行处理,例如 mapply:

tibble::tibble(x=c(1,2,3), result = mapply(function(quo,x) eval(quo),quotes,x))

# A tibble: 3 x 2
      x result
  <dbl>  <dbl>
1     1      1
2     2      3
3     3      5

您是否注意到更简单的调用 data.frame(x, y = x) 也不起作用?

data.frame() 使用标准评估,因此在您的情况下,它们将在全球环境中进行评估。

不过,如果您为引号元素命名,您将能够做到 tibble(x, !!!quotes),因为 tibble 的工作方式不同。

从技术上讲,您可能可以接受以下内容,我们通过在全局环境中创建一个临时值来作弊,然后在退出时将其删除。

(我用evalq只是为了能用on.exit

quotes<-alist(x, x+1, x+2)

df <- data.frame(
  x = c(5, 10, 15) ->> .t.e.m.p., 
  evalq({
    on.exit(rm(.t.e.m.p., envir = .GlobalEnv))
    lapply(quotes, eval, list(x= .t.e.m.p.))
  }))

df
#>    x c.5..10..15. c.6..11..16. c.7..12..17.
#> 1  5            5            6            7
#> 2 10           10           11           12
#> 3 15           15           16           17

ls(all.names = TRUE)
#> [1] "df"     "quotes"

reprex package (v0.3.0)

于 2021-05-11 创建

这看起来当然很糟糕,使用 transformwithintibble 可能是更明智的选择。

如果您不介意在 .GlobalEnv 中创建 x,并且可能会覆盖它,您可以在 data.frame.[=34 中使用 <- =]

quotes<-alist(x, x+1, x+2)

data.frame(x=x <- c(5, 10, 15), lapply(quotes, eval))
#   x c.5..10..15. c.6..11..16. c.7..12..17.
#1  5            5            6            7
#2 10           10           11           12
#3 15           15           16           17

rm(x)

您可以在 local 中调用它,但是 x 不会在 .GlobalEnv 中创建,并且 eval 需要被告知在当前 [=25= 中查找].

local(data.frame(x=x <- c(5, 10, 15), lapply(quotes, eval, environment())))
#   x c.5..10..15. c.6..11..16. c.7..12..17.
#1  5            5            6            7
#2 10           10           11           12
#3 15           15           16           17

也可以将值分配给不同的名称,但此处 eval 需要名称为 xlist。请注意,在这种情况下,TMP 将首先创建或覆盖,如果需要,然后从 .GlobalEnv.

中使用 rm 删除
data.frame(x=TMP <- c(5, 10, 15), lapply(quotes, eval, list(x=TMP)))
#   x c.5..10..15. c.6..11..16. c.7..12..17.
#1  5            5            6            7
#2 10           10           11           12
#3 15           15           16           17

rm(TMP)

其他(但不是 asked/wanted)的可能性可能正在使用 transform:

transform(data.frame(x=c(5, 10, 15)), y=lapply(quotes, eval, environment()))
#   x y.c.5..10..15. y.c.6..11..16. y.c.7..12..17.
#1  5              5              6              7
#2 10             10             11             12
#3 15             15             16             17

within:

within(data.frame(x=c(5, 10, 15)), y <- lapply(quotes, eval, environment()))
#   x         y
#1  5 5, 10, 15
#2 10 6, 11, 16
#3 15 7, 12, 17

tibble:

tibble::tibble(x=c(5, 10, 15), y=sapply(quotes, eval, environment()))
## A tibble: 3 x 2
#      x y[,1]  [,2]  [,3]
#  <dbl> <dbl> <dbl> <dbl>
#1     5     5     6     7
#2    10    10    11    12
#3    15    15    16    17

最后(供参考)问题的预期输出

x <- c(5, 10, 15)
data.frame(x, lapply(quotes, eval))
#   x c.5..10..15. c.6..11..16. c.7..12..17.
#1  5            5            6            7
#2 10           10           11           12
#3 15           15           16           17

rm(x)

与之前使用 tibble 的答案类似,但为了完整性添加了 data.table 解决方案:

data.table(x=c(5, 10, 15))[, lapply(quotes, eval, envir=environment())]
#    V1 V2 V3
# 1:  5  6  7
# 2: 10 11 12
# 3: 15 16 17

正如其他人所提到的,我认为最好的解决方案是使用 tibble() 而不是 data.frame()。但是,如果你想避免 tibble 依赖,我认为最好的解决方案是创建一个自定义函数 my_frame() 来实现你需要的功能。如果您只在第一列中设置 'defining' 值,这可能相当简单:

my_frame <- function(x, quotes) {
  data.frame(x, lapply(quotes, eval, environment()))
}

my_frame(c(5, 10, 15), quotes)
#>    x c.5..10..15. c.6..11..16. c.7..12..17.
#> 1  5            5            6            7
#> 2 10           10           11           12
#> 3 15           15           16           17