【问题标题】:Finding "similar" rows performing a conditional join with sqldf使用 sqldf 查找执行条件连接的“相似”行
【发布时间】:2016-05-17 08:14:36
【问题描述】:

假设我有一个 data.table(也可以是 data.frame,对我来说没关系),其中包含数字列 a、b、c、d 和 e。 表格的每一行代表一篇文章,a-e 是文章的数字特征。

我想根据 a、b 和 c 列找出哪些文章彼此相似。 我通过允许 a、b 和 c 最多变化 +/- 1 来定义“相似”。 也就是说,如果 a、b 和 c 的差值均不超过 1,则文章 x 与文章 y 相似。它们的 d 和 e 值无关紧要,可能会有很大差异。

我已经尝试了几种方法,但都没有得到想要的结果。我想要实现的是获得一个结果表,其中仅包含与至少另一行相似的那些行。另外,必须排除重复项。

特别是,我想知道这是否可以使用 sqldf 库。我的想法是在给定的条件下以某种方式将表格与自身连接起来,但我没有正确地将它组合在一起。任何想法(不一定使用 sqldf)?

【问题讨论】:

    标签: r sqldf


    【解决方案1】:

    假设我们的输入数据框是内置的 11x8 anscombe 数据框。它的前三列名称是x1x2x3。那么这里有一些解决方案。

    1) sqldf 这会返回相似行的行号对:

    library(sqldf)
    
    ans <- anscombe
    ans$id <- 1:nrow(ans)
    
    sqldf("select a.id, b.id 
           from ans a 
           join ans b on abs(a.x1 - b.x1) <= 1 and 
                         abs(a.x2 - b.x2) <= 1 and 
                         abs(a.x3 - b.x3) <= 1")
    

    添加另一个条件and a.id &lt; b.id,如果每行不应该与自身配对并且如果我们想要排除每对的反向或添加and not a.id = b.id 以仅排除自身对。

    2) dist 这将返回一个矩阵m,如果 i 和 j 行相似,则其第 i,j 个元素为 1,如果不基于第 1、2 和 3 列,则返回 0。

    # matrix of pairs (1 = similar, 0 = not)
    m <- (as.matrix(dist(anscombe[1:3], method = "maximum")) <= 1) + 0
    

    给予:

       1 2 3 4 5 6 7 8 9 10 11
    1  1 0 0 1 1 0 0 0 0  0  0
    2  0 1 0 1 0 0 0 0 0  1  0
    3  0 0 1 0 0 1 0 0 1  0  0
    4  1 1 0 1 0 0 0 0 0  0  0
    5  1 0 0 0 1 0 0 0 1  0  0
    6  0 0 1 0 0 1 0 0 0  0  0
    7  0 0 0 0 0 0 1 0 0  1  1
    8  0 0 0 0 0 0 0 1 0  0  1
    9  0 0 1 0 1 0 0 0 1  0  0
    10 0 1 0 0 0 0 1 0 0  1  0
    11 0 0 0 0 0 0 1 1 0  0  1
    

    如果需要,我们可以添加 m[lower.tri(m, diag = TRUE)] &lt;- 0 来排除自我配对和每对的反面,或者添加 diag(m) &lt;- 0 来排除自我配对。

    我们可以像这样创建相似行号对的数据框。为了保持输出简短,我们排除了自我对和每对的反向。

    # two-column data.frame of pairs excluding self pairs and reverses
    subset(as.data.frame.table(m), c(Var1) < c(Var2) & Freq == 1)[1:2]
    

    给予:

        Var1 Var2
    34     1    4
    35     2    4
    45     1    5
    58     3    6
    91     3    9
    93     5    9
    101    2   10
    106    7   10
    117    7   11
    118    8   11
    

    这是上面的网络图。请注意,答案在图表之后继续:

    # network graph
    library(igraph)
    g <- graph.adjacency(m)
    plot(g)
    

    # raster plot
    library(ggplot2)
    ggplot(as.data.frame.table(m), aes(Var1, Var2, fill = factor(Freq))) + 
           geom_raster()
    

    【讨论】:

    • 很好的答案,非常有帮助,尤其是 sqldf 示例。还有一件事困扰着我:在实现了 sqldf 解决方案之后,一些解决方案是模棱两可的,因为它们是另一个解决方案的子集。示例:a.id = 1 有 10、11 和 123 作为 b.id 条目。同时,a.id = 10 有 11 和 123 作为 b.id 条目。我不希望最后两行出现,因为它们被前三行隐式覆盖。有没有办法直接在 sql 语句中解决这个问题,还是我们之后需要这样做?可惜我只能想到后者
    • 仅仅因为第 1 行与第 10 行相似,而与第 11 行相似,并不意味着第 10 行与第 11 行相似。例如,考虑第 1 行 = (0, 0, 0 ),第 10 行 = (-1, 0, 0),第 11 行 = (1, 0, 0)。在这里,第 10 行和第 11 行并不相似,尽管它们都与第 1 行相似。
    • 你是对的。但是,案例 1 = (0, 0, 0)、第 10 行 = (1, 0, 0) 和第 11 行 = (0.5, 0, 0) 仍会导致我在上一条评论中描述的情况。我需要一种方法来删除这些解决方案的子集。
    • 就答案中的示例而言,请尝试:g.mst &lt;- minimum.spanning.tree(g); get.data.frame(g.mst, what = "edges")
    【解决方案2】:

    我对 R 很陌生,所以不要期望太多。

    如果您从您的值(基本上是向量)创建一个矩阵,该矩阵与这两个值的距离。因此,您可以找到彼此之间差异小于 1 的那些组合。通过这种方式,您可以找到匹配的 (a)-pairs。对 (b) 和 (c) 重复此操作,并找到所有对中包含的那些。

    或者,这也可以作为立方体来完成。

    只是作为一个思想提示。

    【讨论】:

    猜你喜欢
    • 2018-07-25
    • 1970-01-01
    • 2020-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多