通过函数传递表达式 [英] Passing expression through functions
问题描述
我使用 data.table
包并尝试编写一个函数(如下所示):
require(data.table)
/ pre>
#函数定义
f = function(path,key){
table = data.table(read.delim setkeyv(x,cols,verbose = verbose)中的错误:一些列不在数据表中(表格, :e
return(table)
}
#用法
f(table.csv,ID)
这里我尝试传递一个表达式到函数。为什么这个代码不工作?
我已经尝试过
substitute()
,quote / code>和
eval()
。解决方案
首先,让我们来看看如何
setkey
函数从data.table
包执行操作:
#setkey function
function(x,...,verbose = getOption(datatable.verbose))
{
if .character(x))
stop(x可能不再是data.table的字符名,可能是未记录的,已被删除。)
cols = getdots()
if(!length(cols))
cols = colnames(x)
else if(identical(cols,NULL))
cols = NULL
setkeyv(x,因此,当你这样做:require(data.table)
dt< - data.table(ID = c(1,1,2,2, 3),y = 1:5)
setkey(dt,ID)
data.table
(即不导出)内部的函数getdots
。让我们看一下这个函数:#data.table ::: getdots
function()
{
as.character(match.call(sys.function(-1),call = sys.call(-1),
expand.dots = FALSE)$ ...) b}
那么,它接受在
setkey
中输入的参数,并使用match.call
分别提取参数。也就是说,此示例案例的match.call
参数为:setkey(x = dt,... = list(ID))
列表中,您可以使用
$ ...
访问...
参数,值ID ,并将此列表转换为包含
as.character
的字符,结果为(字符向量)。然后
setkey
在内部将此传递给setkeyv
以设置键。
现在为什么在函数中写
setkey(table,key)
h3>
这正是因为
setkey / getdots
的方式。setkey
函数设计为在第一个参数(即data.table
)之后接受任何参数,然后返回...
参数作为字符。
也就是说,如果您给予
setkey(dt,key)
,则会返回cols < - key
。如果你给setkey(dt,e)
,它会返回cols< - e
。它不查找key是否为现有变量,如果是,则替换变量的值。它所做的一切就是将你提供的值(不管是符号还是字符)转换回一个字符。
当然这不会在你的情况下工作,因为你要在
setkey
中提供键
= ID中的值。至少我不能想到一个办法做到这一点。
如何解决这个问题?
如@agstudy已经提到,最好/最简单的方法是传递
ID
并使用setkeyv
。但是,如果你真的坚持使用f(table.csv,ID)
,那么,你可以这样做:f < - function(path,key){
pre>
table = data.table(read.delim(path,header = TRUE))
e = as.character(match.call(f)$ key)
setkeyv(table,e)
return(table)
}
这里,您首先使用
match.call
获取对应于参数键
,然后将其转换为字符
,然后将其传递给setkeyv
p>
总之,
setkey
在内部使用setkeyv
。和imho,setkey是一个方便的函数,当你已经知道要为其设置键的data.table
的列名时使用。希望这有帮助。I'm using
data.table
package and trying to write a function (shown below):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)
Here I try to pass an expression to the function. Why this code doesn't work?
I've already tried different combinations of
substitute()
,quote()
andeval()
. So, it'd be great if you could also explain how to get this to work.解决方案First, let's look at how the
setkey
function does things from thedata.table
package:# 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) }
So, when you do:
require(data.table) dt <- data.table(ID=c(1,1,2,2,3), y = 1:5) setkey(dt, ID)
It calls the function
getdots
which is internal todata.table
(that is, it's not exported). Let's have a look at that function:# data.table:::getdots function () { as.character(match.call(sys.function(-1), call = sys.call(-1), expand.dots = FALSE)$...) }
So, what does this do? It takes the parameter you entered in
setkey
and it usesmatch.call
to extract the arguments separately. That is, thematch.call
argument for this example case would be:setkey(x = dt, ... = list(ID))
and since it's a list, you can access the
...
parameter with$...
to get a list of 1 element with its valueID
and converting to this list to a character withas.character
results in"ID"
(a character vector). And thensetkey
passes this tosetkeyv
internally to set the keys.
Now why doesn't this work when you write
setkey(table, key)
inside your function?This is precisely because of the way
setkey/getdots
is. Thesetkey
function is designed to take any argument after the first argument (which is adata.table
) and then return the...
argument as a character.That is, if you give
setkey(dt, key)
then it'll returncols <- "key"
. If you givesetkey(dt, e)
, it'll give backcols <- "e"
. It doesn't look for if "key" is an existing variable and then if so substitute the value of the variable. All it does is convert the value you provide (whether it be a symbol or character) back to a character.Of course this won't work in your case because you want the value in
key
= ID to be provided insetkey
. At least I can't think of a way to do this.
How to get around this?
As @agstudy already mentions, the best/easiest way is to pass
"ID"
and usesetkeyv
. But, if you really insist on usingf("table.csv", ID)
then, this is what you could do:f <- function(path, key) { table = data.table(read.delim(path, header=TRUE)) e = as.character(match.call(f)$key) setkeyv(table, e) return(table) }
Here, you first use
match.call
to get the value corresponding to argumentkey
and then convert it to acharacter
and then pass that tosetkeyv
.In short,
setkey
internally usessetkeyv
. And imho, setkey is a convenient function to be used when you already know the column name of thedata.table
for which you need to set the key. Hope this helps.这篇关于通过函数传递表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!