Действительно быстрая векторизация слова ngram в R

редактировать: новый пакет text2vec отлично, и решает эту проблему (и многие другие) очень хорошо.

text2vec на CRAN text2vec на github виньетка, иллюстрирующая токенизацию ngram

У меня есть довольно большой набор текстовых данных в R, который я импортировал как символьный вектор:

#Takes about 15 seconds
system.time({
  set.seed(1)
  samplefun <- function(n, x, collapse){
    paste(sample(x, n, replace=TRUE), collapse=collapse)
  }
  words <- sapply(rpois(10000, 3) + 1, samplefun, letters, '')
  sents1 <- sapply(rpois(1000000, 5) + 1, samplefun, words, ' ')
})

Я могу преобразовать эти символьные данные в представление пакета слов следующим образом:

library(stringi)
library(Matrix)
tokens <- stri_split_fixed(sents1, ' ')
token_vector <- unlist(tokens)
bagofwords <- unique(token_vector)
n.ids <- sapply(tokens, length)
i <- rep(seq_along(n.ids), n.ids)
j <- match(token_vector, bagofwords)
M <- sparseMatrix(i=i, j=j, x=1L)
colnames(M) <- bagofwords

Таким образом, R может векторизовать 1 000 000 миллионов коротких предложений в представление пакета слов за 3 секунды (неплохо!):

> M[1:3, 1:7]
10 x 7 sparse Matrix of class "dgCMatrix"
      fqt hqhkl sls lzo xrnh zkuqc mqh
 [1,]   1     1   1   1    .     .   .
 [2,]   .     .   .   .    1     1   1
 [3,]   .     .   .   .    .     .   .

Я могу бросить эту разреженную матрицу вglmnet или жеirlba и сделайте некоторый довольно удивительный количественный анализ текстовых данных. Ура!

Теперь я хотел бы расширить этот анализ на матрицу мешков с грамматикой, а не на матрицу мешков с словами. До сих пор самый быстрый способ, который я нашел для этого, заключается в следующем (все функции ngram, которые я мог найти в CRAN, перекрыли этот набор данных, поэтомуЯ получил небольшую помощь от SO):

find_ngrams <- function(dat, n, verbose=FALSE){
  library(pbapply)
  stopifnot(is.list(dat))
  stopifnot(is.numeric(n))
  stopifnot(n>0)
  if(n == 1) return(dat)
  pblapply(dat, function(y) {
    if(length(y)<=1) return(y)
    c(y, unlist(lapply(2:n, function(n_i) {
      if(n_i > length(y)) return(NULL)
      do.call(paste, unname(as.data.frame(embed(rev(y), n_i), stringsAsFactors=FALSE)), quote=FALSE)
    })))
  })
}

text_to_ngrams <- function(sents, n=2){
  library(stringi)
  library(Matrix)
  tokens <- stri_split_fixed(sents, ' ')
  tokens <- find_ngrams(tokens, n=n, verbose=TRUE)
  token_vector <- unlist(tokens)
  bagofwords <- unique(token_vector)
  n.ids <- sapply(tokens, length)
  i <- rep(seq_along(n.ids), n.ids)
  j <- match(token_vector, bagofwords)
  M <- sparseMatrix(i=i, j=j, x=1L)
  colnames(M) <- bagofwords
  return(M)
}

test1 <- text_to_ngrams(sents1)

Это займет около 150 секунд (неплохо для чистой функции r), но я бы хотел пойти быстрее и расширить наборы данных большего размера.

Есть лидействительно быстро функции в R для n-граммовой векторизации текста? В идеале я ищуRcpp функция, которая принимает символьный вектор в качестве входных данных и возвращает разреженную матрицу документов x нграмм в качестве выходных данных, но также была бы рада получить некоторое руководство по написанию самой функции Rcpp.

Даже более быстрая версияfind_ngrams функция будет полезна, так как это является основным узким местом. R удивительно быстр при токенизации.

Редактировать 1 Вот еще один пример набора данных:

sents2 <- sapply(rpois(100000, 500) + 1, samplefun, words, ' ')

В этом случае мои функции для создания матрицы мешков слов занимают около 30 секунд, а мои функции для создания матрицы мешков слов - около 500 секунд. Опять же, существующие n-грамм-векторизаторы в R, похоже, душат этот набор данных (хотя я бы хотел, чтобы меня ошиблись!)

Редактировать 2 Сроки против тау:

zach_t1 <- system.time(zach_ng1 <- text_to_ngrams(sents1))
tau_t1 <- system.time(tau_ng1 <- tau::textcnt(as.list(sents1), n = 2L, method = "string", recursive = TRUE))
tau_t1 / zach_t1 #1.598655

zach_t2 <- system.time(zach_ng2 <- text_to_ngrams(sents2))
tau_t2 <- system.time(tau_ng2 <- tau::textcnt(as.list(sents2), n = 2L, method = "string", recursive = TRUE))
tau_t2 / zach_t2 #1.9295619

Ответы на вопрос(2)

Ваш ответ на вопрос