найти соответствие для строки в векторе регулярных выражений

#r

#r

Вопрос:

Предположим, у меня есть вектор символов

 vals <- c("hello","goodbye","junk")
  

и вектор целей регулярных выражений

 targets <- c("(hello|goodbye)","^j","other")
  

(Я готов оговорить, что каждый элемент в vals соответствует ровно одному элементу в targets ). Существует ли существующий, эффективный / компактный / векторизованный способ нахождения индекса соответствия каждого элемента в vals in targets ? ( match не работает: он соответствует таблице строк, а не регулярным выражениям.) Таким образом, желаемый результат c(1,1,2) для этого примера. Приветствуются Base-R или tidyverse / stringr решения.

Комментарии:

1. Это слишком мило? x <- outer(vals,targets,stringr::str_detect); col(x)[x] ? Я чувствую, что в некоторых случаях это может сломаться.

2. Я сейчас нахожусь на своем телефоне, но я думаю, что stringi::stri_match_all_regex должен быть в состоянии это сделать.

Ответ №1:

Одним из подходов было бы задать имена последовательности list with и stack для двух столбцов data.frame. NULL Элементы будут удалены с stack помощью . Теперь мы извлекаем второй столбец, чтобы получить list индекс

 as.integer(stack(setNames(m, seq_along(m)))[,2])
#[1] 1 1 2
  

ПРИМЕЧАНИЕ: Вот m вывод индекса @BenBolker list из grep output


Или с помощью tidyverse

 library(tidyverse)
crossing(targets, vals) %>%
    mutate(ind = group_indices(., targets)) %>%
    filter(str_detect(vals, targets)) %>%
    pull(ind)
#[1] 1 1 2
  

Комментарии:

1. на первый взгляд это совершенно загадочно. Я подумаю об этом …

2. @BenBolker Да, вы ищете решение без lapply .

3. Нет, я просто был в замешательстве — думал, что это должно быть полное решение, и не мог понять, где происходит сопоставление.

Ответ №2:

Лучший способ, который я могу придумать, чтобы сделать это: инвертировать соответствие, то есть: выполнить итерацию по целевым объектам, затем заполнить совпадения в векторе, соответствующем значениям.

 ## find positions in `vals` that match each target
m <- lapply(targets,grep,x=vals)
## set up response vector
res <- rep(NA,length(vals))
## fill in matching positions for each target
for (i in seq_along(m)) {
    res[m[[i]]] <- i
}
  

Комментарии:

1. Аналогичный фрагмент этой логики, просто заменяющий grep на grepl max.col(sapply(targets,grepl,x=vals), "first") или даже max.col(vapply(targets, grepl, x=vals, FUN.VALUE=logical(length(vals))), "first") поскольку мы знаем тип вывода.

Ответ №3:

Используя str_detect in stringr , перебирайте каждое значение, чтобы найти целевой индекс.

 library(stringr)

# Data
vals <- c("hello","goodbye","junk")
targets <- c("(hello|goodbye)","^j","other")

# create empty vector to assign matched value later
v  <- c()

for (i in 1:length(vals)){

  # value to be looked up against target
  stg_i <- vals[i]

  # min to get first matched target
  stg_v <- min(which(str_detect(stg_i, targets)))

  # update the value in vector with matched one
  v[i] <- stg_v

}

v
[1] 1 1 2