使用dplyr非标准评估创建多个函数

3
我试图使用mutate_()创建多个列,每个列都基于一个名为"custom function"的自定义函数并传入不同的输入参数。我可以使用paste()函数创建多个带引号的函数调用,但这种方法行不通,因为dplyr的NSE需要公式(~)而不是带引号的字符串才能找到该函数。我应该如何编写下面的"dots = "行,以便能够找到该函数?我尝试使用 ~、as.formula() 和lazyeval::interp(),但都无法正常工作。我的实际"prefixes"是一个很长的向量,所以我不想为每个新列单独编写函数调用。谢谢!
library(dplyr)
library(lazyeval)
library(nycflights13)

myfunc = function(x, y) { x - y }

# this works
flights1 <- mutate(flights, dep_time_sched = myfunc(dep_time, dep_delay), 
                            arr_time_sched = myfunc(arr_time, arr_delay))

# this doesn't - Error: could not find function "myfunc"
prefixes <- c('dep', 'arr')
dots = as.list(paste0('myfunc(', 
                       paste0(prefixes, '_time'), ', ', 
                       paste0(prefixes, '_delay)')))
flights2 <- mutate_(flights, .dots = setNames(dots, paste0(prefixes, '_time_sched')))
2个回答

5
您可以使用interplapply来循环处理您的前缀,以获得所需格式的列表,以便用于mutate_
    dots = lapply(prefixes, function(var) interp(~myfunc(x, y), 
                                        .values = list(x = as.name(paste0(var, "_time")),
                                                    y = as.name(paste0(var, "_delay")))))
    dots

[[1]]
~myfunc(dep_time, dep_delay)
<environment: 0x0000000019e51f00>

[[2]]
~myfunc(arr_time, arr_delay)
<environment: 0x0000000019f1e5b0>

这将得到与你的flights1相同的结果。
flights2 = mutate_(flights, .dots = setNames(dots, paste0(prefixes, '_time_sched')))
identical(flights1, flights2)
[1] TRUE

3
我的实际“前缀”是一个很长的向量,所以我不想为每个新列单独编写函数调用。
如果是这种情况,你应该真正将你的数据转换成长格式。为了澄清我的意思,让我们看一个更小的例子:
mydat <- flights[1:5, c(paste0(prefixes,"_time"), paste0(prefixes,"_delay"))]
#   dep_time arr_time dep_delay arr_delay
#      (int)    (int)     (dbl)     (dbl)
# 1      517      830         2        11
# 2      533      850         4        20
# 3      542      923         2        33
# 4      544     1004        -1       -18
# 5      554      812        -6       -25

library(data.table)

longdat <- setDT(mydat)[, .( 
    pref  = rep(prefixes, each=.N), 
    time  = unlist(mget(paste0(prefixes,"_time"))),
    delay = unlist(mget(paste0(prefixes,"_delay")))
)]

longdat[, time_sched := myfunc(time, delay) ]
#     pref time delay time_sched
#  1: dep_  517     2        515
#  2: dep_  533     4        529
#  3: dep_  542     2        540
#  4: dep_  544    -1        545
#  5: dep_  554    -6        560
#  6: arr_  830    11        819
#  7: arr_  850    20        830
#  8: arr_  923    33        890
#  9: arr_ 1004   -18       1022
# 10: arr_  812   -25        837

除了更简单之外,一次调用该函数可以利用其矢量化。虽然我使用data.table构建了longdat,但我确信在tidyr包中有一个工具可以完成相同的任务(与dplyr相伴)。同样,添加time_sched列只是一个mutate。
重塑的替代方法:感谢@akrun,这里有另一种到达longdat的方法,使用melt函数语法,将在下一个版本的data.table(1.9.8,尚未发布)中提供。
longdat <- melt(mydat, 
    measure        = patterns('time$','delay$'), 
    variable.name  = "pref", 
    value.name     = c('time', 'delay')
)[, pref := prefixes[pref]]

或者,也感谢 @akrun 的帮助,这里有一种自动构建前缀的重塑方式,给定后缀 (timedelay),使用 @AnandaMahto 的 splitstackshape 包:

library(splitstackshape)
longdat <- merged.stack(transform(mydat, ind=1:nrow(mydat)), 
  var.stubs = c('_time', '_delay'), 
  sep = 'var.stubs', 
  atStart = FALSE)

1
再次感谢@akrun。我已经添加了它。 - Frank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接