如何使用新(测试)数据重现相同的DocumentTermMatrix

12
假设我有基于文本的训练数据和测试数据。更具体地说,我有两个数据集 - 训练和测试 - 两个数据集都有一个包含文本并与手头的工作相关的列。
我使用R中的tm软件包处理训练数据集中的文本列。在去除空格、标点符号和停用词后,我对语料库进行了词干提取,并最终创建了一个包含每个文档中单词频率/计数的1元文档术语矩阵。然后,我采取了预先确定的50个截止值,并仅保留那些计数大于50的术语。
接下来,我使用DTM和因变量(在训练数据中存在)训练一个GLMNET模型。到目前为止,一切都很顺利和容易。
但是,当我想在测试数据或未来可能出现的任何新数据上进行评分/预测时,我该怎么做呢?
具体来说,我想知道如何在新数据上创建完全相同的DTM结构。
如果新数据集没有任何与原始训练数据相似的单词,则所有术语的计数应为零(这没问题)。但我希望能够在任何新语料库上复制完全相同的DTM(以结构为准)。
有任何想法/思路吗?

如果我正确理解了你的问题(但我不确定是否有,因为没有一个可重现的示例来展示你想要做什么),似乎最简单的方法是使用所有数据创建术语-文档矩阵,然后将该矩阵分成测试集和训练集。这样,即使一个矩阵只有几个术语的零,所有术语也都在两个矩阵中表示。你遇到麻烦是因为你在创建术语-文档矩阵之前就把数据分开了。 - SchaunW
2
我同意Schaun的观点,但是我只能使用当前拥有的测试数据来做到这一点。我正在寻找一种解决方案,可以在明天获得新数据时仍然有效。否则,每次添加新数据到现有数据中、重新创建DTM并重新训练模型都会很麻烦。 - Godel
1
再次强调,如果有示例数据将更容易回答您的问题。您可以将新数据转换为术语-文档矩阵,然后使用plyr包的rbind.fill函数将其与旧数据绑定,这将创建一个新矩阵,其中包含两个原始矩阵的所有列。您的训练数据将具有任何在新数据中但不在训练数据中的术语的NA列。然后,您可以删除那些列。您的新数据将具有任何在训练数据中但不在新数据中的术语的NA列,您可以用零替换这些NA。 - SchaunW
谢谢Schaun,这确实有所帮助。不幸的是,我没有任何样本数据。基本上,我试图弄清楚是否存在任何包中应该能够整洁地完成此操作的函数。例如,当创建一个dtm时,它使用现有的结构来创建一个新的,当提供新数据时。在网上搜索没有发现任何东西,所以我想在这里发布它。 - Godel
2个回答

16

tm有许多陷阱...看看更高效的text2vec向量化文献,其中完全回答了这个问题。

对于tm,这里可能还有一种更简单的方法来重构第二语料库的DTM矩阵:

crude2.dtm <- DocumentTermMatrix(crude2, control = list
               (dictionary=Terms(crude1.dtm), wordLengths = c(3,10)) )

4
那个方法也很好用,但是Dictionary()不再被支持了,你需要使用Terms() - Khozzy
1
很棒的例子,一定会尝试,一直在寻找本地实现这个的方法。我猜我应该阅读一下我使用的包的文档 :) - Tetlanesh
2
我认为这个解决方案更好,因为它不需要将稀疏矩阵转换。 - Johannes Jasper
这是最佳/最短的解决方案。谢谢!有很多教程讨论R中的文本分析,但是我查看的每一个教程都没有讨论如何在新文档上进行"外样本"预测。 - Peter

12

如果我理解正确,您已经创建了一份文档词条矩阵(dtm),并且想要从新的文档中创建一个新的文档词条矩阵,该矩阵应该与第一个文档词条矩阵具有相同的列(即术语)。如果情况是这样的话,那么应该将第二个文档词条矩阵按照第一个矩阵中的术语进行子集化处理,可能类似于以下内容:

首先设置一些可再现的数据...

这是您的训练数据...

library(tm)
# make corpus for text mining (data comes from package, for reproducibility) 
data("crude")
corpus1 <- Corpus(VectorSource(crude[1:10]))    
# process text (your methods may differ)
skipWords <- function(x) removeWords(x, stopwords("english"))
funcs <- list(tolower, removePunctuation, removeNumbers,
              stripWhitespace, skipWords)
crude1 <- tm_map(corpus1, FUN = tm_reduce, tmFuns = funcs)
crude1.dtm <- DocumentTermMatrix(crude1, control = list(wordLengths = c(3,10))) 

这是您的测试数据...

corpus2 <- Corpus(VectorSource(crude[15:20]))  
# process text (your methods may differ)
skipWords <- function(x) removeWords(x, stopwords("english"))
funcs <- list(tolower, removePunctuation, removeNumbers,
              stripWhitespace, skipWords)
crude2 <- tm_map(corpus2, FUN = tm_reduce, tmFuns = funcs)
crude2.dtm <- DocumentTermMatrix(crude2, control = list(wordLengths = c(3,10))) 

这是实现你想要的功能的部分:

现在我们只保留测试数据中与训练数据相同的项...

# convert to matrices for subsetting
crude1.dtm.mat <- as.matrix(crude1.dtm) # training
crude2.dtm.mat <- as.matrix(crude2.dtm) # testing

# subset testing data by colnames (ie. terms) or training data
xx <- data.frame(crude2.dtm.mat[,intersect(colnames(crude2.dtm.mat),
                                           colnames(crude1.dtm.mat))])

最后,将训练数据中不在测试数据中的术语的所有空列添加到测试数据中...

# make an empty data frame with the colnames of the training data
yy <- read.table(textConnection(""), col.names = colnames(crude1.dtm.mat),
                 colClasses = "integer")

# add incols of NAs for terms absent in the 
# testing data but present # in the training data
# following SchaunW's suggestion in the comments above
library(plyr)
zz <- rbind.fill(xx, yy)

所以zz是测试文档的数据框,但与训练文档具有相同的结构(即相同的列,尽管其中许多包含NA,如SchaunW所指出的)。

这符合您的要求吗?


是的,Ben。这非常有帮助。非常感谢。 :) - Godel
没问题!现在你知道如何制作示例数据并包含在你想要提出的任何相关问题中了。 - Ben
我已经寻找这个问题的解决方案有一段时间了,谢谢,虽然我也会尝试 Dmitriy 提供的一个解决方案。 - Tetlanesh
1
这需要对您的稀疏矩阵进行分解,考虑使用 dictionary=Terms(crude1.dtm) 的解决方案。 - Johannes Jasper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接