去除相似度高的文档的重复文档

Remove duplicating docs of docs with high similarity

下载lexisnexis报纸文章时,语料库中经常出现大量重复文章。我想删除它们并且我正在考虑通过使用余弦相似性统计来这样做,但我不确定如何自动执行此操作。有任何想法吗?

您的问题在细节上相当薄弱 - 例如 reproducible example - 但这是一个有趣的问题和挑战。就这样吧。

假设我们有一个由两组相似文档组成的语料库,{ (a1, a2, a3), (b1, b2) } 其中字母表示相似性。当其他文档 "duplicates" 定义为相似度超过阈值(例如 0.80)时,我们希望只保留一个文档。

我们可以使用textstat_simil()生成相似度矩阵,然后直接从返回的dist对象中形成成对的集合,然后只保留一个相似的集合。

library("quanteda")
# Loading required package: quanteda
# Package version: 1.3.14

mydocs <- c(a1 = "a a a a a b b c d w g j t",
            b1 = "l y y h x x x x x y y y y",
            a2 = "a a a a a b c s k w i r f",
            b2 = "p q w e d x x x x y y y y",
            a3 = "a a a a a b b x k w i r f")

mydfm <- dfm(mydocs)

(sim <- textstat_simil(mydfm))
#             a1          b1          a2          b2
# b1 -0.22203788                                    
# a2  0.80492203 -0.23090513                        
# b2 -0.23427416  0.90082239 -0.28140219            
# a3  0.81167608 -0.09065452  0.92242890 -0.12530944

# create a data.frame of the unique pairs and their similarities
sim_pair_names <- t(combn(docnames(mydfm), 2))
sim_pairs <- data.frame(sim_pair_names,
                        sim = as.numeric(sim), 
                        stringsAsFactors = FALSE)
sim_pairs
#    X1 X2         sim
# 1  a1 b1 -0.22203788
# 2  a1 a2  0.80492203
# 3  a1 b2 -0.23427416
# 4  a1 a3  0.81167608
# 5  b1 a2 -0.23090513
# 6  b1 b2  0.90082239
# 7  b1 a3 -0.09065452
# 8  a2 b2 -0.28140219
# 9  a2 a3  0.92242890
# 10 b2 a3 -0.12530944

根据我们的阈值条件对其进行子集化,我们可以提取要删除的不幸文档的名称,并将其提供给 dfm_subset() 中的逻辑条件。

# set the threshold for similarity
threshold <- 0.80

# discard one of the pair if similarity > threshold
todrop <- subset(sim_pairs, select = X1, subset = sim > threshold, drop = TRUE)
todrop
# [1] "a1" "a1" "b1" "a2"

# then subset the dfm, keeping only the "keepers"
dfm_subset(mydfm, !docnames(mydfm) %in% todrop)
# Document-feature matrix of: 2 documents, 20 features (62.5% sparse).
# 2 x 20 sparse Matrix of class "dfm"
#     features
# docs a b c d w g j t l y h x s k i r f p q e
#   b2 0 0 0 1 1 0 0 0 0 4 0 4 0 0 0 0 0 1 1 1
#   a3 5 2 0 0 1 0 0 0 0 0 0 1 0 1 1 1 1 0 0 0

针对类似文档这一问题的其他解决方案是将它们形成集群,或者按照潜在语义分析的思路使用主成分分析来减少文档矩阵。

如果你有成千上万的文档,你的 RAM 中需要大量 space 来保存所有的相似度分数,但是你可以在 textstat_proxy() 中设置一个最小阈值,底层函数textstat_simil()

本例中余弦相似度小于0.9的全部忽略。

library("quanteda")
mydocs <- c(a1 = "a a a a a b b c d w g j t",
            b1 = "l y y h x x x x x y y y y",
            a2 = "a a a a a b c s k w i r f",
            b2 = "p q w e d x x x x y y y y",
            a3 = "a a a a a b b x k w i r f")
mydfm <- dfm(mydocs)

(sim <- textstat_proxy(mydfm, method = "cosine", min_proxy = 0.9))
# 5 x 5 sparse Matrix of class "dsTMatrix"
#    a1        b1        a2        b2        a3    
# a1  1 .         .         .         .        
# b1  . 1.0000000 .         0.9113423 .        
# a2  . .         1.0000000 .         0.9415838
# b2  . 0.9113423 .         1.0000000 .        
# a3  . .         0.9415838 .         1.0000000

matrix2list <- function(x) {
    names(x@x) <- rownames(x)[x@i + 1]
    split(x@x, factor(x@j + 1, levels = seq(ncol(x)), labels = colnames(x)))
}

matrix2list(sim)
# $a1
# a1 
#  1 
# 
# $b1
# b1 
#  1 
# 
# $a2
# a2 
#  1 
# 
# $b2
#        b1        b2 
# 0.9113423 1.0000000 
# 
# $a3
#        a2        a3 
# 0.9415838 1.0000000 

有关性能差异,请参阅 https://koheiw.net/?p=839

您已经收到了一些很好的答案。但是,如果您更喜欢针对您的特定用例的更自动化的方法,您可以使用包 LexisNexisTools(我写的)。它带有一个名为 lnt_similarity() 的函数,它完全可以满足您的需求。我用模拟数据写了一个快速教程 here.

此处的解决方案与 lnt_similarity() 中的解决方案之间的主要区别在于,我还考虑了词序,这在极少数情况下会产生很大差异 (see this blog post)。

我还建议您仔细考虑阈值,否则您可能会错误地删除一些文章。我包含了一个函数来可视化两篇文章之间的差异,这样您就可以更好地掌握要删除的数据,称为 lnt_diff()