去除相似度高的文档的重复文档
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()
。
下载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()
。