Я ищу наиболее эффективное решение проблемы, поставленной в этом вопросе : скажем, у вас есть вектор v
строк:
set.seed(314159)
library(stringi)
library(stringr)
v <- stringi::stri_rand_strings(10000, 4, pattern = "[A-Z]")
head(v)
#> [1] "FQGK" "YNQH" "IMNJ" "WUFU" "BBAR" "BZUH"
Я хочу эффективно возвращает один логический элемент, представляющий, соответствует ли данный шаблон, скажем "FOO"
, какой-либо из строк в v
.Предполагаемая функция будет работать следующим образом:
detect("FOO")
#> FALSE
detect("BAR")
#> TRUE
Есть несколько способов сделать это с базовыми grep
функциями или с использованием stringr::str_detect
, но каждый из них включает в себя сначала сопоставление регулярного выражения для каждого элемента v
В моем примере выполняется до 9999 ненужных тестов.Эффективное решение остановит оценку после того, как будет найдено одно совпадение.
Для каждого решения detect.#
я тестирую его, применяя его ко всем трем буквенным комбинациям c
:
c <- combn(LETTERS,3, FUN = function(x){paste(x, collapse = '')})
head(c)
#> [1] "ABC" "ABD" "ABE" "ABF" "ABG" "ABH"
Возможные решения
Существует несколько возможныхрешения, которые я придумал.Для начала зацикливание на v
, чтобы ненужное сопоставление с образцом не было выполнено после того, как совпадение найдено.Как вы увидите, это ужасная идея с большим количеством накладных расходов:
detect.1 <- function(pattern){
for (i in 1:length(v)){
if (length(grep(pattern, v[i]))){return(TRUE)}
}
return(FALSE)
}
Далее, мы можем использовать комбинации any()
и grepl()
или stringr::str_detect()
, но тогда мы сделаем ненужное совпадениетесты:
#str_detect() from stringr
detect.2 <- function(pattern){
any(str_detect(v, pattern) )
}
# any() and grepl()
detect.3 <- function(pattern){
any(grepl(pattern, v))
}
Наконец, если мы знаем, что символ никогда не появляется в pattern
, мы можем свернуть v
в одну строку с компонентами, разделенными этим символом.Тогда достаточно одного grep
:
#collapse to long string
v_pasted <- paste(v, collapse = '_')
detect.4 <- function(pattern){
isTRUE(as.logical(grep(pattern, v_pasted)))
}
Тесты
(обновлено для использования bench::mark()
)
det1 <- expression(data.frame(c, "inV" = I(lapply(c, FUN = detect.1))))
det2 <- expression(data.frame(c, "inV" = I(lapply(c, FUN = detect.2))))
det3 <- expression(data.frame(c, "inV" = I(lapply(c, FUN = detect.3))))
det4 <- expression({
v_pasted <- paste(v, collapse = '_')
data.frame(c, "inV" = I(lapply(c, FUN = detect.4)))
})
bench::mark(
eval(det1),
eval(det2),
eval(det3),
eval(det4),
iterations = 5,
relative = TRUE
)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 4 x 10
#> expression min mean median max `itr/sec` mem_alloc n_gc n_itr
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 eval(det1) 76.9 77.0 76.8 77.2 1 1 Inf 1
#> 2 eval(det2) 4.02 4.03 4.04 4.05 19.1 735. Inf 1
#> 3 eval(det3) 2.77 2.79 2.79 2.80 27.6 735. Inf 1
#> 4 eval(det4) 1 1 1 1 77.0 1.22 NaN 1
grepl
заметно быстрее, чем str_detect
.Метод вставки является самым быстрым, но требует наличия символа разделения, который не отображается в возможных шаблонах поиска.Есть ли какая-то более быстрая альтернатива, которую мне не хватает?