【问题标题】:Apply function to data.table using function's character name and arguments as character vector使用函数的字符名称和参数作为字符向量将函数应用于 data.table
【发布时间】:2016-02-27 13:45:54
【问题描述】:

我想通过 data.table 上的字符名称调用函数。每个函数还有一个参数向量(因此有一长串函数可应用于 data.table)。参数是 data.table 列。我的第一个想法是 do.call 将是该任务的好方法。下面是一个简单的示例,其中包含一个要运行的函数名称以及要传递的列向量:

# set up dummy data 
set.seed(1)
DT <- data.table(x = rep(c("a","b"),each=5), y = sample(10), z = sample(10))
# columns to use as function arguments
mycols <- c('y','z')
# function name 
func <- 'sum'
# my current solution:
DT[, do.call(func, list(get('y'), get('z'))), by = x]
#    x V1
# 1: a 47
# 2: b 63  

我对此并不满意,因为它需要具体命名每一列。我只想传递一个字符向量mycols

在这种情况下,我需要的其他解决方案是:

DT[, do.call(func, .SD), .SDcols = mycols, by = x]

但是自定义函数存在问题,唯一适合我的解决方案是第一个:

#own dummy function    
myfunc <- function(arg1, arg2){
  arg1+arg2
}
func <- 'myfunc'
DT[, do.call(func, list(get('y'), get('z'))), by = x] 
#   x V1
#  1: a  6
#  2: a  6
#  3: a 11
#  4: a 17
#  5: a  7
#  6: b 15
#  7: b 17
#  8: b 10
#  9: b 11
# 10: b 10
# second solution does not work 
DT[, do.call(func, .SD), .SDcols = mycols, by = x]
# Error in myfunc(y = c(3L, 4L, 5L, 7L, 2L), z = c(3L, 2L, 6L, 10L, 5L)) : 
#  unused arguments (y = c(3, 4, 5, 7, 2), z = c(3, 2, 6, 10, 5))

据我了解,它假定 myfunc 具有参数 y, z,这是不正确的。应该有变量y,z 应该传递给参数arg1, arg2

我也试过mget功能,但也没有成功:

DT[, do.call(func, mget(mycols)), by = x] 
# Error: value for ‘y’ not found

我可能遗漏了一些相当明显的东西,在此先感谢您的指导。

【问题讨论】:

  • 我会使用callas.call 而不是do.call,它需要包装到eval 中,但提供了更多的灵活性。类似eval(as.call(list(func, as.name("y"), as.name("z")))).
  • @jangorecki 是的,这个DT[, eval(call(func, as.name("y"), as.name("z"))), by = x] 也适用于我。但我想使用字符向量mycols 而不是明确命名yz
  • 你在寻找类似DT[, Reduce(func, mget(mycols)), by = x]的东西吗?
  • @AnandaMahto 这与我正在寻找的非常接近。出于某种原因,如果您定义了具有超过 2 个参数的自定义函数,您的解决方案将引发错误 Error in f(init, x[[i]]) : argument "arg3" is missing, with no default。有什么想法吗?
  • Unused arguments in R的可能重复

标签: r data.table do.call


【解决方案1】:

是的,您遗漏了一些东西(嗯,这不是很明显,但仔细调试错误可以识别问题)。您的函数需要命名参数arg1arg2。您正在通过do.call(您已经注意到)向它传递参数y = ...z = ...。解决方案是传递不带名称的列表:

> DT[, do.call(func, unname(.SD[, mycols, with = F])), by = x]
    x V1
 1: a  6
 2: a  6
 3: a 11
 4: a 17
 5: a  7
 6: b 15
 7: b 17
 8: b 10
 9: b 11
10: b 10

【讨论】:

    【解决方案2】:

    这是一个帮助我实现我想要的解决方案。

    func <- 'sum'
    mycols <- c('y','z')
    DT[, do.call(func, lapply(mycols, function(x) get(x))), by = x]
    #    x V1
    # 1: a 47
    # 2: b 63
    

    可以将base 函数或自定义函数传递给它(不像Reduce 解决方案那样具体)。

    【讨论】:

    • 你在这里所做的只是返回一个没有名字的列表......你最好使用unname
    【解决方案3】:

    这可能取决于您要使用的函数类型,但您似乎对Reduce 感兴趣。

    这是你的两个例子:

    mycols <- c('y','z')
    func <- 'sum'
    
    DT[, Reduce(func, mget(mycols)), by = x]
    #    x V1
    # 1: a 47
    # 2: b 63
    
    myfunc <- function(arg1, arg2){
      arg1+arg2
    }
    func <- 'myfunc'
    
    DT[, Reduce(func, mget(mycols)), by = x]
    #     x V1
    #  1: a  6
    #  2: a  6
    #  3: a 11
    #  4: a 17
    #  5: a  7
    #  6: b 15
    #  7: b 17
    #  8: b 10
    #  9: b 11
    # 10: b 10
    

    【讨论】:

      猜你喜欢
      • 2021-12-17
      • 1970-01-01
      • 1970-01-01
      • 2019-11-23
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 2014-06-28
      • 1970-01-01
      相关资源
      最近更新 更多