Как применить разные функции с несколькими аргументами к каждой строке 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

Я пытался использовать подход mapply(do.call, list(sub), ...) с оператором присваивания в data.table, но аргументы sub (шаблон, замена, строка) должны быть вложенным списком для do.call, и я застрял в том, как правильно написать это.


person Naumz    schedule 13.02.2017    source источник
comment
почему бы не просто dt[, Map(sub, l, '', n)]? РЕДАКТИРОВАТЬ: это даст мне именованный вектор/список, поэтому мы действительно хотим обернуть возврат в список dt[, o := (Map(sub, l, '', n))]   -  person Shape    schedule 13.02.2017
comment
@Форма, спасибо! мой подход сейчас кажется излишне сложным. Я пытался придумать метод, в котором вместо sub у меня мог бы быть список отдельных функций. Однако ваш код работает в моем случае. Если вы хотите опубликовать это как ответ, пожалуйста, продолжайте.   -  person Naumz    schedule 13.02.2017
comment
@Shape Я согласен, что вы должны опубликовать это. Я бы выбрал dt[, mapply(sub, sprintf(" ?%s ?", l), "", n, USE.NAMES = FALSE)] - решает проблему с именами, а также тот факт, что вокруг шаблонов могут быть пробелы (которые, по-видимому, OP также хочет удалить).   -  person Frank    schedule 13.02.2017
comment
Должен ли я отредактировать заголовок вопроса, чтобы он больше соответствовал тому, что я спрашиваю? Я думаю, что то, как я это сформулировал, может сбить с толку, поскольку в моем случае это не другая функция...   -  person Naumz    schedule 13.02.2017
comment
Мы можем сделать dt[, o := trimws(sub(paste(l, collapse="|"), "", n))]   -  person akrun    schedule 13.02.2017
comment
@akrun, хотя технически это работает в его примере, если в его столбце подстановки есть какие-либо специальные регулярные выражения, это может повлиять на каждую строку. Я бы сказал, что регулярное выражение |, вероятно, небезопасно. Даже не обязательно быть спецом, а вдруг он там другое слово ищет?   -  person Shape    schedule 13.02.2017
comment
@Shape Да, это может быть правдой, но здесь ОП дал нам пример для проверки   -  person akrun    schedule 13.02.2017
comment
@akrun - я считаю, что весь смысл вопроса в том, что аргументы функции поступают построчно. В таких обстоятельствах действительно нет способа избежать медлительности.   -  person thelatemail    schedule 13.02.2017
comment
спасибо за ответы! Я искал что-то вроде построчного подхода, но подход collapse был бы хорош, если бы мои реальные данные его поддерживали.   -  person Naumz    schedule 13.02.2017


Ответы (2)


Итак, мы хотим выполнить расчет по строкам и вернуть его, определенный как новый столбец o.

mapply определенно является правильным семейством функций, но mapplysapply) упростит их вывод из списка, прежде чем они его вернут. data.table любит списки. Map — это просто выразительный ярлык для mapply(..., simplify = FALSE), который не изменяет возврат.

Следующее делает вычисление, которое мы ищем, но это все еще не совсем правильно. (data.table интерпретирует вывод списка как отдельные столбцы)

> 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 является столбцом списка.

Мы можем избежать этого, используя стандартный mapply, хотя я предпочитаю универсальный подход Map (каждая строка создает один вывод, который идет в список. Независимо от того, как выглядит этот вывод, он всегда будет работать, а затем мы можем преобразовать тип в конце.)

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

person Shape    schedule 13.02.2017
comment
Это сделает dt$o встроенным списком, поэтому я думаю, что mapply на самом деле более подходит. - person thelatemail; 13.02.2017
comment
правда, нужно было проверить классы столбцов, мне все еще нравится Map, а затем преобразовать в символ. Я обычно не доверяю упрощениям. Поведение Map всегда одинаково. Я добавлю другой способ, хотя - person Shape; 13.02.2017
comment
Вы ошибаетесь в том, что data.table любит списки. Data.table любит список столбцов и разрешает столбцы, которые являются списками, но с последними неудобно работать, и так будет всегда. - person Frank; 13.02.2017
comment
@Frank Я больше думал о списках, а не о матрицах ... что может быть реальной опасностью при возврате из семейства упрощенных приложений. И если вы знаете, что ваши данные защищены от возврата этих типов, то это намного лучше, чем столбец списка. Однако я почти всегда сначала делал это с Map, а затем смотрел, смогу ли я оптимизировать с помощью упрощенного mapply. - person Shape; 13.02.2017

Мы можем использовать векторизованный подход, paste изменяя содержимое 'l', используя это как pattern аргумент в sub, чтобы удалить подстроку и создать новый столбец '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
person akrun    schedule 13.02.2017