#r #data.table #grepl
#регулярное выражение #r #data.table
Вопрос:
Допустим, у меня есть два столбца строк:
library(data.table)
DT <- data.table(x = c("a","aa","bb"), y = c("b","a","bbb"))
Для каждой строки я хочу знать, присутствует ли строка в x в столбце y. Циклический подход будет:
for (i in 1:length(DT$x)){
DT$test[i] <- DT[i,grepl(x,y) 0]
}
DT
x y test
1: a b 0
2: aa a 0
3: bb bbb 1
Существует ли векторизованная реализация этого? grep(DT$x,DT$y)
При использовании используется только первый элемент x.
Комментарии:
1. @LegalizeIt да, я не ищу ситуации, когда a == b, но когда у вас есть частичные совпадения строк по строкам. Вышеупомянутые тесты, если x находится в y, но вы можете повторно запустить другим способом, если это важно. Я не уверен, что вы подразумеваете под «Почему в столбце y нет «a»?»
2. @DavidArenburg хорошая мысль, но работает только тогда, когда x уникально. Я пытался
DT[, test := grepl(x, y) 0, by = .I]
, но получаю ту жеargument 'pattern' has length > 1 and only the first element will be used
ошибку. Тем не менее, может быть решение, в котором вы сначала вызываете DT[, RowI := .I], а затем используетеby = rowI
3. Это работает не только тогда, когда
x
уникально. Отлично работаетDT <- data.table(x = c("a","aa","aa","bb"), y = c("b","a","a", "bbb"))
, например, для.4. @DavidArenburg Вы правы… он будет передавать один и тот же аргумент в grepl для каждой группы x, что является одним и тем же вводом. Умно. Я сравниваю все ответы и отвечу лучшим.
5. О, я вижу, что вы там сделали. Вы задали это для вопроса о вознаграждении…
Ответ №1:
Вы можете просто сделать
DT[, test := grepl(x, y), by = x]
Ответ №2:
Или mapply
( Vectorize
на самом деле это просто оболочка для mapply
)
DT$test <- mapply(grepl, pattern=DT$x, x=DT$y)
Ответ №3:
Спасибо всем за ваши ответы. Я провел сравнительный анализ их всех и пришел к следующему:
library(data.table)
library(microbenchmark)
DT <- data.table(x = rep(c("a","aa","bb"),1000), y = rep(c("b","a","bbb"),1000))
DT1 <- copy(DT)
DT2 <- copy(DT)
DT3 <- copy(DT)
DT4 <- copy(DT)
microbenchmark(
DT1[, test := grepl(x, y), by = x]
,
DT2$test <- apply(DT, 1, function(x) grepl(x[1], x[2]))
,
DT3$test <- mapply(grepl, pattern=DT3$x, x=DT3$y)
,
{vgrepl <- Vectorize(grepl)
DT4[, test := as.integer(vgrepl(x, y))]}
)
Результаты
Unit: microseconds
expr min lq mean median uq max neval
DT1[, `:=`(test, grepl(x, y)), by = x] 758.339 908.106 982.1417 959.6115 1035.446 1883.872 100
DT2$test <- apply(DT, 1, function(x) grepl(x[1], x[2])) 16840.818 18032.683 18994.0858 18723.7410 19578.060 23730.106 100
DT3$test <- mapply(grepl, pattern = DT3$x, x = DT3$y) 14339.632 15068.320 16907.0582 15460.6040 15892.040 117110.286 100
{ vgrepl <- Vectorize(grepl) DT4[, `:=`(test, as.integer(vgrepl(x, y)))] } 14282.233 15170.003 16247.6799 15544.4205 16306.560 26648.284 100
Решение data.table не только наиболее синтаксически простое, но и самое быстрое.
Комментарии:
1. Я не уверен, какой здесь этикет (принять ответ Дэвида или мой). Независимо от того, 1 для всех, чтобы показать количество способов, которыми это можно сделать.
2. Для этого конкретного примера (с
x
повторением обоихy
значений и) я вижу небольшое улучшение по сравнению сby=.(x,y)
by=x
первым.
Ответ №4:
Вы можете передать grepl
функцию в функцию apply для работы с каждой строкой вашей таблицы данных, где первый столбец содержит строку для поиска, а второй столбец содержит строку для поиска. Это должно дать вам векторное решение вашей проблемы.
> DT$test <- apply(DT, 1, function(x) as.integer(grepl(x[1], x[2])))
> DT
x y test
1: a b 0
2: aa a 0
3: bb bbb 1
Ответ №5:
Вы можете использовать Vectorize
:
vgrepl <- Vectorize(grepl)
DT[, test := as.integer(vgrepl(x, y))]
DT
x y test
1: a b 0
2: aa a 0
3: bb bbb 1