如何对 data.table 的每一行应用不同的多参数函数?

How to apply a different multi-argument function to each row of a data.table?

我有一个 data.table 以下示例格式。

dt <- data.table(l = c("apple","ball","cat"),
                 m = c(1,2,3),
                 n = c("I ate apple", "I played ball", "cat ate pudding"))

我想将 sub 应用于每一行的列 (n),其模式来自另一列 (l)。我该怎么做?

我正在寻找的输出是,

              l m             n    o
       1: apple 1     I ate apple       I ate
       2:  ball 2   I played ball    I played
       3:   cat 3 cat ate pudding ate pudding

我已经尝试在 data.table 中使用方法 mapply(do.call, list(sub), ...) 和赋值运算符,但是 sub (模式、替换、字符串)的参数需要是一个嵌套列表do.call 我一直在思考如何正确地编写它。

所以我们想做一个按行计算,return它定义为一个新列o

mapply 绝对是正确的函数族,但是 mapply(和 sapply)会在 return 之前简化列表的输出。 data.table 喜欢列表。 Map 只是 mapply(..., simplify = FALSE) 的表达快捷方式,它不会修改 return.

下面是我们要计算的结果,但还是不太对。 (data.table 将 list-output 解释为单独的列)

> dt[, Map(sub, l, '', n)]
    apple      ball          cat
1: I ate  I played   ate pudding

所以我们想更进一步,将它包装在一个列表中以获得我们想要的输出:

>dt[, .(Map(sub, l, '', n))]
             V1
1:       I ate 
2:    I played 
3:  ate pudding

现在我们可以使用 :=

分配它
> dt[, o := Map(sub, l, '', n)]
> dt
       l m               n            o
1: apple 1     I ate apple       I ate 
2:  ball 2   I played ball    I played 
3:   cat 3 cat ate pudding  ate pudding

编辑:正如所指出的,这导致 o 成为 list-column。

我们可以通过使用标准 mapply 来避免这种情况,尽管我更喜欢 Map 的 one-size-fits-all 方法(每一行创建一个输出,它进入一个列表。不管输出结果如何,这总是有效的,然后我们可以在最后 type-convert。)

dt[, o := mapply(sub, l, '', n)]

我们可以通过 pasteing 'l' 的内容来实现向量化方法,将其用作 sub 中的 pattern 参数来删除子字符串并创建新的列 'o'

dt[, o := trimws(sub(paste(l, collapse="|"), "", n))]
dt
#       l m               n           o
#1: apple 1     I ate apple       I ate
#2:  ball 2   I played ball    I played
#3:   cat 3 cat ate pudding ate pudding