编辑:新的text2vec软件包非常出色,很好地解决了这个问题(以及许多其他问题)。
text2vec在CRAN上的页面 text2vec在github上的页面 演示ngram标记化的vignette
我有一个相当大的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可以在大约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中,对文本数据进行一些很棒的量化分析。太好了!
现在我想将这个分析扩展到 n-gram 的矩阵上,而不是单词矩阵。到目前为止,我发现最快的方法是按照以下步骤操作(我找到的所有n-gram函数都无法处理这个数据集,所以我从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-gram文本向量化?理想情况下,我正在寻找一种Rcpp函数,它以字符向量作为输入,并将文档x ngrams返回为稀疏矩阵输出,但也很乐意获得编写Rcpp函数的指导。
即使是find_ngrams
函数的更快版本也会很有帮助,因为那是主要的瓶颈。在分词方面,R表现出惊人的速度。
编辑1: 这里是另一个示例数据集:
sents2 <- sapply(rpois(100000, 500) + 1, samplefun, words, ' ')
在这种情况下,我的创建词袋矩阵的函数大约需要30秒,而创建n-gram词袋矩阵的函数则需要大约500秒。同样,在R中现有的n-gram向量化程序似乎无法处理此数据集(虽然我很希望被证明是错误的!)
编辑2
时间 vs tau: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
tau::textcnt(as.list(sents), n = 2L, method = "string", recursive = TRUE)
替代find_ngrams
吗?它只需要一半的时间,但是只提供二元组(n=2)。 - lukeAstringdist::qgrams
可以非常快速地生成字符 qgrams。作者目前正在努力支持单词(ints)。 - Jan van der Laantau_t1 / zach_t1
=649.48
/675.82
。差别不大了。 - lukeA