如何编写一个函数来调用一个调用data.table的函数?

时间:2021-01-30 14:30:49

The package data.table has some special syntax that requires one to use expressions as the i and j arguments.

包data.table有一些特殊的语法,需要使用表达式作为i和j参数。

This has some implications for how one write functions that accept and pass arguments to data tables, as is explained really well in section 1.16 of the FAQs.

这对于一个接受并将参数传递给数据表的写函数的方式有一些影响,正如常见问题解答第1.16节中所解释的那样。

But I can't figure out how to take this one additional level.

但我无法弄清楚如何采取这一个额外的水平。

Here is an example. Say I want to write a wrapper function foo() that makes a specific summary of my data, and then a second wrapper plotfoo() that calls foo() and plots the result:

这是一个例子。假设我想编写一个包装函数foo(),它会对我的数据进行特定的汇总,然后是第二个封装plotfoo(),它调用foo()并绘制结果:

library(data.table)


foo <- function(data, by){
  by <- substitute(by)
  data[, .N, by=list(eval(by))]
}

DT <- data.table(mtcars)
foo(DT, gear)

OK, this works, because I get my tabulated results:

好的,这很有效,因为我得到了我的表格结果:

   by  N
1:  4 12
2:  3 15
3:  5  5

Now, I try to just the same when writing plotfoo() but I fail miserably:

现在,我在编写plotfoo()时尝试一样,但我失败了:

plotfoo <- function(data, by){
  by <- substitute(by)
  foo(data, eval(by))
}
plotfoo(DT, gear)

But this time I get an error message:

但这次我收到一条错误消息:

Error: evaluation nested too deeply: infinite recursion / options(expressions=)?

OK, so the eval() is causing a problem. Let's remove it:

好的,所以eval()导致了问题。我们将其删除:

plotfoo <- function(data, by){
  by <- substitute(by)
  foo(data, by)
}
plotfoo(DT, gear)

Oh no, I get a new error message:

哦不,我收到一条新的错误信息:

Error in `[.data.table`(data, , .N, by = list(eval(by))) : 
  column or expression 1 of 'by' or 'keyby' is type symbol. Do not quote column names. Useage: DT[,sum(colC),by=list(colA,month(colB))]

And here is where I remain stuck.

而这里我仍然被卡住了。

Question: How to write a function that calls a function that calls data.table?

问题:如何编写一个调用调用data.table的函数的函数?

2 个解决方案

#1


13  

This will work:

这将有效:

plotfoo <- function(data, by) {
  by <- substitute(by)
  do.call(foo, list(quote(data), by))
}

plotfoo(DT, gear)
#    by  N
# 1:  4 12
# 2:  3 15
# 3:  5  5

Explanation:

说明:

The problem is that your call to foo() in plotfoo() looks like one of the following:

问题是你在plotfoo()中调用foo()看起来像下面之一:

foo(data, eval(by))
foo(data, by)

When foo processes those calls, it dutifully substitutes for the second formal argument (by) getting as by's value the symbols eval(by) or by. But you want by's value to be gear, as in the call foo(data, gear).

当foo处理这些调用时,它会尽职尽责地替换第二个形式参数(通过)获得符号eval(by)或by的值。但是你希望by的值是齿轮,就像调用foo(数据,齿轮)一样。

do.call() solves this problem by evaluating the elements of its second argument before constructing the call that it then evaluates. As a result, when you pass it by, it evaluates it to its value (the symbol gear) before constructing a call that looks (essentially) like this:

do.call()通过在构造然后计算的调用之前评估其第二个参数的元素来解决此问题。因此,当您传递它时,它会在构造一个看起来(基本上)如下的调用之前将其计算为其值(符号齿轮):

foo(data, gear)

#2


5  

I think you might be tieing yourself up in knots. This works:

我想你可能会把自己捆绑在一起。这有效:

library(data.table)
foo <- function(data, by){
  by <- by
  data[, .N, by=by]
}

DT <- data.table(mtcars)
foo(DT, 'gear')

plotfoo <- function(data, by){
  foo(data, by)
}
plotfoo(DT, 'gear')

And that method supports passing in character values:

并且该方法支持传入字符值:

> gg <- 'gear'
> plotfoo <- function(data, by){
+   foo(data, by)
+ }
> plotfoo(DT, gg)
   gear  N
1:    4 12
2:    3 15
3:    5  5

#1


13  

This will work:

这将有效:

plotfoo <- function(data, by) {
  by <- substitute(by)
  do.call(foo, list(quote(data), by))
}

plotfoo(DT, gear)
#    by  N
# 1:  4 12
# 2:  3 15
# 3:  5  5

Explanation:

说明:

The problem is that your call to foo() in plotfoo() looks like one of the following:

问题是你在plotfoo()中调用foo()看起来像下面之一:

foo(data, eval(by))
foo(data, by)

When foo processes those calls, it dutifully substitutes for the second formal argument (by) getting as by's value the symbols eval(by) or by. But you want by's value to be gear, as in the call foo(data, gear).

当foo处理这些调用时,它会尽职尽责地替换第二个形式参数(通过)获得符号eval(by)或by的值。但是你希望by的值是齿轮,就像调用foo(数据,齿轮)一样。

do.call() solves this problem by evaluating the elements of its second argument before constructing the call that it then evaluates. As a result, when you pass it by, it evaluates it to its value (the symbol gear) before constructing a call that looks (essentially) like this:

do.call()通过在构造然后计算的调用之前评估其第二个参数的元素来解决此问题。因此,当您传递它时,它会在构造一个看起来(基本上)如下的调用之前将其计算为其值(符号齿轮):

foo(data, gear)

#2


5  

I think you might be tieing yourself up in knots. This works:

我想你可能会把自己捆绑在一起。这有效:

library(data.table)
foo <- function(data, by){
  by <- by
  data[, .N, by=by]
}

DT <- data.table(mtcars)
foo(DT, 'gear')

plotfoo <- function(data, by){
  foo(data, by)
}
plotfoo(DT, 'gear')

And that method supports passing in character values:

并且该方法支持传入字符值:

> gg <- 'gear'
> plotfoo <- function(data, by){
+   foo(data, by)
+ }
> plotfoo(DT, gg)
   gear  N
1:    4 12
2:    3 15
3:    5  5