簡體   English   中英

如何正確使用 function 中 data.tables 的 env 變量

[英]How do I correctly use the env variable for data.tables within a function

讓我們舉一個簡單的例子

data <- data.table::data.table(a = 1:10, b = 2:11)
j <- quote(c("c") := list(a + 1))
data[, j, env = list(j = j)][]
#        a     b     c
#    <int> <int> <num>
# 1:     1     2     2
# 2:     2     3     3
# 3:     3     4     4
# 4:     4     5     5
# 5:     5     6     6

以上工作並產生正確的 output。 但是,如果我將它放在 function 中,我會得到一個非常不同的 output。

data <- data.table::data.table(a = 1:5, b = 2:6)
test <- function(data, ...) {
  dots <- eval(substitute(alist(...)))
  j <- call(":=", call("c", names(dots)), unname(dots))
  print(j)
  data[, j, env = list(j = j)][]
}
test(data = data, c = a + 1)
# `:=`(c("c"), list(a + 1))
#        a     b         c
#    <int> <int>    <list>
# 1:     1     2 <call[3]>
# 2:     2     3 <call[3]>
# 3:     3     4 <call[3]>
# 4:     4     5 <call[3]>
# 5:     5     6 <call[3]>

假設c = a + 1只是沒有在正確的環境中進行評估(即data.table本身)。

編輯:我正在使用 data.table 1.14.3

和...之間的不同

j_quote <- quote(c("c") := list(a + 1))

j_call <- call(":=", call("c", names(dots)), unname(dots))

非常微妙:

j_call
#`:=`(c("c"), list(a + 1))
j_quote
#`:=`(c("c"), list(a + 1))

all.equal(j_call,j_quote)
[1] TRUE

identical(j_call,j_quote)
[1] FALSE

這種區別在於列表結構:

# j_quote
as.list(j_quote)
[[1]]
`:=`

[[2]]
c("c")

[[3]]
list(a + 1)

# j_call
as.list(j_call)
[[1]]
`:=`

[[2]]
c("c")

[[3]]
[[3]][[1]]
a + 1

嘗試:

library(data.table)
#> Warning: le package 'data.table' a été compilé avec la version R 4.1.2
data <- data.table::data.table(a = 1:5, b = 2:6)
test <- function(data, ...) {
  dots <- eval(substitute(alist(...)))
  j_call <- call(":=", call("c", names(dots)), call("list",unname(dots)[[1]]))
  j_quote <- quote(c("c") := list(a + 1))
  cat("j_quote",deparse(j_quote),"\n")
  cat("j_call ",deparse(j_call),"\n")
  cat("identical:",identical(j_quote,j_call),"\n")
  cat("all.equal:",all.equal(j_quote,j_call),"\n")
    data[, j, env = list(j = j_call)][]
}
test(data = data, c = a + 1)
#> j_quote `:=`(c("c"), list(a + 1)) 
#> j_call  `:=`(c("c"), list(a + 1)) 
#> identical: TRUE 
#> all.equal: TRUE
#>        a     b     c
#>    <int> <int> <num>
#> 1:     1     2     2
#> 2:     2     3     3
#> 3:     3     4     4
#> 4:     4     5     5
#> 5:     5     6     6

這是因為dots不是調用,而是調用列表。 因此,當 data.table 評估j時,它試圖將該列表插入到新列中。

要解決此問題,您需要將調用列表拼接成一個調用。 您可以在直接調用':='()時執行此操作(下面的選項 1),但您也可以將其分解為多個步驟,通過將dots轉換為對list()的調用來反映您在上面所做的操作(選項2)。

library(data.table)

data <- data.table::data.table(a = 1:5, b = 2:6)

# Option 1 - call to ':='
test <- function(data, ...) {
  dots <- eval(substitute(alist(...)))
  j <- bquote(':='(..(dots)), splice = TRUE)
  print(j)
  data[, j, env = list(j = j)][]
}

# # Option 2 - convert dots to a call to a list
# test <- function(data, ...) {
#   dots <- eval(substitute(alist(...)))
#   dots_names <- names(dots)
#   dots <- bquote(list(..(unname(dots))), splice = TRUE)
#   j <- call(":=", dots_names, dots)
#   print(j)
#   data[, j, env = list(j = j)][]
# }

test(data = data, c = a + 1, double_b = b * 2)
#> `:=`(c = a + 1, double_b = b * 2)
#>        a     b     c double_b
#>    <int> <int> <num>    <num>
#> 1:     1     2     2        4
#> 2:     2     3     3        6
#> 3:     3     4     4        8
#> 4:     4     5     5       10
#> 5:     5     6     6       12

編輯:如果您希望能夠編輯同一列或使用新創建的列,也可以使用test2()

test2 <- function(data, ...) {
  dots <- eval(substitute(alist(...)))
  dots_names <- names(dots)
  for (i in seq_along(dots)) {
    dot_name <- dots_names[[i]]
    dot <- dots[[i]]
    j <- call(":=", dot_name, dot)
    print(j)
    data[, j, env = list(j = j)]
  }
  data[]
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM