Векторизация матричной операции в R совпадающих шаблонах строк - PullRequest
0 голосов
/ 29 октября 2018

Я использую код ниже, чтобы создать матрицу, которая сравнивает все строки в одном векторе, чтобы увидеть, содержат ли они какие-либо шаблоны во втором векторе:

strngs <- c("hello there", "welcome", "how are you")
pattern <- c("h", "e", "o")

M <- matrix(nrow = length(strngs), ncol = length(pattern))

for(i in 1:length(strngs)){
  for(j in 1:length(pattern)){
    M[i, j]<-str_count(strngs[i], pattern[j])
  }
}

M

Отлично работает и возвращает искомую матрицу:

      [,1] [,2] [,3]

[1,]    2    3    1

[2,]    0    2    1

[3,]    1    1    2

Тем не менее, мой реальный набор данных огромен, и подобные циклы плохо масштабируются до матрицы с 117, 746, 754 значениями. Кто-нибудь знает, как я мог бы векторизовать это или иначе ускорить это? Или я должен просто выучить C ++? ;)

Спасибо!

Ответы [ 3 ]

0 голосов
/ 29 октября 2018

Вы можете использовать outer и stri_count_fixed, как предложено @ snoram.

outer(strngs, pattern, stringi::stri_count_fixed)
#     [,1] [,2] [,3]
#[1,]    2    3    1
#[2,]    0    2    1
#[3,]    1    1    2
0 голосов
/ 29 октября 2018

Еще одно решение, с sapply. В основном раствор снорама .

t(sapply(strngs, stringi::stri_count_fixed, pattern))
#            [,1] [,2] [,3]
#hello there    2    3    1
#welcome        0    2    1
#how are you    1    1    2

Тесты.

Так как есть всего 4 способа, вот несколько тестов скорости.

f0 <- function(){
  M<-matrix(nrow=length(strngs),ncol=length(pattern))
  for(i in 1:length(strngs)){
    for(j in 1:length(pattern)){
      M[i,j]<-stringr::str_count(strngs[i],pattern[j])
    }
  }
  M
}

f1 <- function(){
  M <- matrix(0L, nrow = length(strngs), ncol = length(pattern), )
  for(i in 1:length(strngs)) {
    M[i, ] <- stringi::stri_count_fixed(strngs[i], pattern)
  }
  M
}

f2 <- function() outer(strngs, pattern, stringi::stri_count_fixed)

f3 <- function() t(sapply(strngs, stringi::stri_count_fixed, pattern))

r0 <- f0()
r1 <- f1()
r2 <- f2()
r3 <- f3()

identical(r0, r1)
identical(r0, r2)
identical(r0, r3)  # FALSE, the return has rownames


library(microbenchmark)
library(ggplot2)

mb <- microbenchmark(
  op = f0(),
  snoram = f1(),
  markus = f2(),
  rui = f3()
)

mb
#Unit: microseconds
#   expr     min       lq      mean   median       uq     max
#     op 333.425 338.8705 348.23310 341.7700 345.8060 542.699
# snoram  47.923  50.8250  53.96677  54.8500  56.3870  69.903
# markus  27.502  29.8005  33.17537  34.3670  35.7490  54.095
#    rui  68.994  72.3020  76.77452  73.4845  77.1825 215.328

autoplot(mb)

enter image description here

0 голосов
/ 29 октября 2018

Вот небольшое улучшение, удалив внутренний цикл и переключившись на stringi (на котором построен stringr).

M <- matrix(0L, nrow = length(strngs), ncol = length(pattern))
for(i in 1:length(strngs)) {
  M[i, ] <- stringi::stri_count_fixed(strngs[i], pattern)
}

А затем более стандартный способ R:

t(sapply(strngs, stringi::stri_count_fixed, pattern))
...