編集: 新しいパッケージ text2vec は優れており、この問題 (および他の多くの問題) を非常にうまく解決します。
CRAN の text2vec github の text2vec 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, ' ')
})
次のように、この文字データを bag-of-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 は約 3 秒で 1,000,000 百万の短い文をバッグオブワード表現にベクトル化できます (悪くない!):
> 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に投入して、テキスト データの非常に優れた定量分析を行うことができます。万歳!
ここで、この分析を、bag-of-words 行列ではなく、bag-of-ngrams 行列に拡張したいと思います。これまでのところ、これを行うために私が見つけた最速の方法は次のとおりです (CRAN で見つけることができるすべての ngram 関数は、このデータセットで詰まっているため、 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 関数としては悪くありません) が、より高速に処理してより大きなデータセットに拡張したいと考えています。
テキストのn-gramベクトル化のためにRに本当に高速な関数はありますか? 理想的には、文字ベクトルを入力として受け取り、ドキュメント x ngrams のスパース行列を出力として返すRcpp関数を探していますが、Rcpp 関数を自分で作成するためのガイダンスもあれば幸いです。
それが主なボトルネックであるため、関数のより高速なバージョンでfind_ngrams
も役立ちます。R のトークン化は驚くほど高速です。
編集 1 別のサンプル データセットを次に示します。
sents2 <- sapply(rpois(100000, 500) + 1, samplefun, words, ' ')
この場合、bag-of-words 行列を作成する関数には約 30 秒かかり、bag-of-ngrams 行列を作成する関数には約 500 秒かかります。繰り返しになりますが、R の既存の n グラム ベクトライザーは、このデータセットを使用していないようです (ただし、間違っていることが証明されることを望みます!)
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