通过函数传递表达式

lev*_*ovd 5 expression r data.table

我正在使用data.table包并尝试编写一个函数(如下所示):

require(data.table)
# Function definition
f = function(path, key) {
  table = data.table(read.delim(path, header=TRUE))
  e = substitute(key)
  setkey(table, e) # <- Error in setkeyv(x, cols, verbose = verbose) : some columns are not in the data.table: e
  return(table)
}

# Usage
f("table.csv", ID)
Run Code Online (Sandbox Code Playgroud)

在这里,我尝试将表达式传递给函数.为什么这段代码不起作用?

我已经尝试了不同的组合substitute(),quote()eval().所以,如果你还能解释如何让它发挥作用,那就太棒了.

Aru*_*run 10

首先,让我们看看setkey函数如何从data.table包中执行操作:

# setkey function
function (x, ..., verbose = getOption("datatable.verbose")) 
{
    if (is.character(x)) 
        stop("x may no longer be the character name of the data.table. The possibility was undocumented and has been removed.")
    cols = getdots()
    if (!length(cols)) 
        cols = colnames(x)
    else if (identical(cols, "NULL")) 
        cols = NULL
    setkeyv(x, cols, verbose = verbose)
}
Run Code Online (Sandbox Code Playgroud)

所以,当你这样做时:

require(data.table)
dt <- data.table(ID=c(1,1,2,2,3), y = 1:5)
setkey(dt, ID)
Run Code Online (Sandbox Code Playgroud)

它调用getdots内部函数data.table(即,它不被导出).我们来看看这个功能:

# data.table:::getdots
function () 
{
    as.character(match.call(sys.function(-1), call = sys.call(-1), 
        expand.dots = FALSE)$...)
}
Run Code Online (Sandbox Code Playgroud)

那么,这有什么用呢?它采用您输入的参数,setkey并用于match.call分别提取参数.也就是说,match.call这个示例案例的论据是:

setkey(x = dt, ... = list(ID))
Run Code Online (Sandbox Code Playgroud)

并且由于它是一个列表,您可以访问...参数$...以获取包含其值的1个元素的列表,ID并将该列表转换为具有as.character结果"ID"的字符(字符向量).然后setkey将其传递给setkeyv内部以设置密钥.


现在为什么当你setkey(table, key)在函数内写时这不起作用?

这正是因为方式setkey/getdots.该setkey函数用于在第一个参数(即a data.table)之后获取任何参数,然后将...参数作为字符返回.

也就是说,如果你给setkey(dt, key)它然后它会返回cols <- "key".如果你给setkey(dt, e),它会回馈cols <- "e".它不会查找"key"是否为现有变量,如果是,则替换变量的值.它所做的只是将您提供的值(无论是符号还是字符)转换回字符.

当然,这不适用于您的情况,因为您希望key提供= ID中的值setkey.至少我想不出办法做到这一点.


怎么解决这个问题?

正如@agstudy已经提到的,最好/最简单的方法是通过"ID"和使用setkeyv.但是,如果你真的坚持使用f("table.csv", ID)那么,这就是你能做的:

f <- function(path, key) {
    table = data.table(read.delim(path, header=TRUE))
    e = as.character(match.call(f)$key)
    setkeyv(table, e)
    return(table)
}
Run Code Online (Sandbox Code Playgroud)

在这里,您首先使用match.call获取与参数对应的值key,然后将其转换为a然后将其character传递给setkeyv.

简而言之,setkey内部使用setkeyv.而且,当你已经知道data.table需要设置密钥的列名时,setkey是一个方便的函数.希望这可以帮助.

  • @agstudy,确切地说.这就是帖子的最后一段所说的:). (2认同)