R 中字母的术语文档矩阵

Term Document Matrix for Letters in R

我想构建一个 n-gram 'letter document matrix',它基本上使用最多 n 个字母的字母序列而不是典型的单词。这是我想要实现的目标的简化示例:

> letterDocumentMatrix(c('ea','ab','ca'), c('sea','abs','cab'))
    [,sea] [,abs] [,cab]
[ea,] TRUE   FALSE  FALSE  
[ab,] FALSE  TRUE   TRUE   
[ca,] FALSE  FALSE  TRUE

这种操作有名称吗?是否有任何预建函数可以处理此问题?

最后,我用 grepl 尝试了 outer 但无济于事:

> outer(c('ea','ab','ca'), c('sea','abs','cab'), grepl)
          [,1]  [,2]  [,3]
     [1,] TRUE  FALSE FALSE  
     [2,] TRUE  FALSE FALSE
     [3,] TRUE  FALSE FALSE  
     Warning message:
     In FUN(X, Y, ...) :
       argument 'pattern' has length > 1 and only the first element will be used

似乎 outer 将整个第一个参数传递给 grepl,而不是一次传递一个条目,导致 grepl 只搜索第一个词,在本例中为 'a'。

grepl() 未对其 pattern 参数进行矢量化,这就是您无法从 outer() 获得正确结果的原因。这是使用 vapply().

的可能解决方案
vec <- c("sea", "abs", "cab") ## vector to search
pat <- c("ea", "ab", "ca")    ## patterns we are searching for
"rownames<-"(vapply(pat, grepl, NA[seq_along(pat)], vec, fixed = TRUE), vec)
#        ea    ab    ca
# sea  TRUE FALSE FALSE
# abs FALSE  TRUE FALSE
# cab FALSE  TRUE  TRUE

这显然会产生您想要的转置版本。为了得到你想要的矩阵,我们可以使用 lapply(), rbind() 结果,然后设置名称。

xx <- do.call(rbind, lapply(pat, grepl, x = vec, fixed = TRUE))
dimnames(xx) <- list(pat, vec)
#      sea   abs   cab
# ea  TRUE FALSE FALSE
# ab FALSE  TRUE  TRUE
# ca FALSE FALSE  TRUE

我会说在 vapply() 结果上使用 t() 来转置它,但在大矩阵上它可能会很慢。

我们可以 Vectorize outer

中的乐趣
outer(c('ea','ab','ca'), c('sea','abs','cab'), Vectorize(grepl))
#     [,1]  [,2]  [,3]
#[1,]  TRUE FALSE FALSE
#[2,] FALSE  TRUE  TRUE
#[3,] FALSE FALSE  TRUE

用于文本分析的 quanteda 包中有一个预建函数可以处理此问题,这需要您将字母序列视为 "dictionary: of regular expressions and building a document-feature matrix where those regular expressions are identified in each " 文档”。作者使用字典整理对 dfm() 函数的调用,您将获得确切的 return 对象。这里我已经按照您的问题转置了它。

letterDocumentMatrix <- function(txts, pats) {
    # create a dictionary in which the key is the same as the entry
    pats <- quanteda::dictionary(sapply(pats, list))
    # name each "document" which is the text string to be searched
    names(txts) <- txts
    # interpret dictionary entries as regular expressions
    ret <- quanteda::dfm(txts, dictionary = pats, valuetype = "regex", verbose = FALSE)
    # transpose the matrix, coerce to dense logical matrix, remove dimnames
    ret <- t(as.matrix(ret > 0))
    names(dimnames(ret)) <- NULL
    ret
}

texts <- c('sea','abs','cab')
patterns <- c('ea','ab','ca')

letterDocumentMatrix(texts, patterns)
##      sea   abs   cab
## ea  TRUE FALSE FALSE
## ab FALSE  TRUE  TRUE
## ca FALSE FALSE  TRUE

如果您希望它在大型数据集上快速运行,我建议从函数中删除第三行和倒数第二行。