通过函数传递表达式

时间:2013-06-11 05:01:04

标签: r expression 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)

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

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

2 个答案:

答案 0 :(得分: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)
}

所以,当你这样做时:

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

它调用getdots内部的函数data.table(也就是说,它不会被导出)。我们来看看这个功能:

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

那么,这是做什么的?它采用您在setkey中输入的参数,并使用match.call分别提取参数。也就是说,此示例案例的match.call参数将为:

setkey(x = dt, ... = list(ID))

由于它是一个列表,您可以使用...访问$...参数,以获取包含其值ID的1个元素的列表,并将此列表转换为包含{的字符{1}}会产生as.character(字符向量)。然后"ID"在内部将此传递给setkey以设置密钥。


现在为什么在你的函数中写setkeyv时这不起作用?

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

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

当然,这不适用于您的情况,因为您希望在cols <- "e"中提供key = ID中的值。至少我想不出办法做到这一点。


如何解决这个问题?

正如@agstudy已经提到的,最好/最简单的方法是通过setkey并使用"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) } 获取与参数match.call对应的值,然后将其转换为key,然后将其传递给character

简而言之,setkeyv内部使用setkey。而且,当您已经知道需要设置密钥的setkeyv的列名时,setkey是一个方便的函数。希望这会有所帮助。

答案 1 :(得分:2)

我无法从你的代码中看出你想要实现的目标,所以我会回答标题要求的问题; “如何通过函数传递表达式?”

如果您想这样做(应尽可能避免这种情况),您可以执行以下操作:

f <- function(expression) {
  return(eval(parse(text=expression)))
}

例如:

f("a <- c(1,2,3); sum(a)")
# [1] 6