【问题标题】:How to get the closest element in a vector for every element in another vector without duplicates?如何为另一个向量中的每个元素获取向量中最接近的元素而不重复?
【发布时间】:2014-09-24 10:30:57
【问题描述】:

我得到了这段代码,它创建了两个向量,对于来自a 的每个元素,我想在b 中获取最接近的元素:

a = rnorm(100)
b = rnorm(100)
c = vapply(a, function(x) which.min(abs(b - x)), 1)
table(duplicated(c))

FALSE  TRUE 
   61    39 

如您所见,此方法会提示提供大量重复项,这是正常的,但我希望没有重复项。一旦选择了索引,我就想从b 中删除出现,但我不知道如何在vapply 下执行此操作。

【问题讨论】:

  • 所以要清楚,您正在寻找一个排列,p,这样sum(abs(a-b[p])) 被最小化?
  • 好吧,我想你确实可以这么说
  • 我猜data.table的加入函数roll = "nearest"在这里会很有用,ala stackoverflow.com/questions/15712826/…
  • @Chase 这也是我的第一个想法。但我看不出这有助于避免比赛中的重复。他们希望在向量的值之间建立一对一的关系。
  • @Wicelo 请提供可重现的输入和输出,并说明如果b 的每个值只能作为匹配项出现一次,您希望如何确定匹配项。 b 值是与最接近匹配的第一个 a 值匹配,还是与最接近 b 匹配的 a 值匹配?

标签: r vector


【解决方案1】:

您将获得的最接近的匹配是对向量进行排序,然后将它们配对。 b 上的以下排列应该允许您这样做。

p <- order(b)[order(order(a))] # order on b and then back transform the ordering of a

sum(abs(a-b[p]))
[1] 20.76788

显然,允许重复确实使事情更接近:

sum(abs(a-b[c]))
[1] 2.45583

【讨论】:

    【解决方案2】:

    这是一个非常糟糕的编程,但可能会工作并且是矢量化的......

       a <- rnorm(100)
       b <- rnorm(100)
       #make a copy of b (you'll see why)
       b1<-b
       res<- vapply(a, function(x) {ret<-which.min(abs(b1 - x));b1[ret]<<-NA;return(ret)}, 1)
    

    【讨论】:

    • 很好,这正是我在发布之前所做的,但是使用= 而不是&lt;&lt; 它不起作用,这两个运营商之间有什么区别?还有为什么你称它为糟糕的编程?
    • 请参阅?"&lt;&lt;-",了解&lt;-&lt;&lt;- 运算符的含义。 &lt;&lt;- 很危险,因为它会在函数范围之外更改对象。
    【解决方案3】:

    我相信这是你能得到的最好的:sum(abs(sort(a) - sort(b)))

    我使用data.table来保留a的原始排序:

    require(data.table)
    
    set.seed(1)
    
    a <- rnorm(100)
    b <- rnorm(100)
    
    sum(abs(a - b))
    sum(abs(sort(a) - sort(b)))
    
    dt <- data.table(a = a, b = b)
    dt[, id := .I]
    
    # sort dt by a
    setkey(dt, a)
    
    # sort b
    dt[, b := sort(b)]
    
    # return to original order
    setkey(dt, id)
    
    dt
    dt[, sum(abs(a - b))]
    

    与 Chase 的解决方案相比,此解决方案提供了更好的结果:

    dt2 <- as.data.table(foo(a,b))
    dt2[, sum(abs(a - bval))]
    dt[, sum(abs(a - b))]
    

    结果:

    > dt2[, sum(abs(a - bval))]
    [1] 24.86731
    > dt[, sum(abs(a - b))]
    [1] 20.76788
    

    【讨论】:

    • 好的调用 - 按a 排序实际上更智能,并且会产生更好的结果。如果初始顺序不重要,只需 cbind(sort(a), sort(b) 即可。
    • 好吧,我非常喜欢 nicola 的解决方案,但您是对的,您的解决方案提供了更好的差异总和。我将不得不研究 data.format 以及 setkey:= 运算符,我不知道这些东西 edit :事实上,詹姆斯解决方案给出了相同的差异总和并且是班轮
    【解决方案4】:

    这几乎可以肯定可以通过矢量化得到改进,但似乎可以工作并且可能完成工作:

    set.seed(1)
    a = rnorm(5)
    b = rnorm(5)
    
    foo <- function(a,b) {
    
      out <- cbind(a, bval = NA)
    
      for (i in seq_along(a)) {
        #which value of B is closest?
        whichB <- which.min(abs(b - a[i]))
        #Assign that value to the bval column
        out[i, "bval"] <- b[whichB]
        #Remove that value of B from being chosen again
        b <- b[-whichB]
      }
    
      return(out)
    
    }
    
    #In action
    foo(a,b)
    ---
                  a       bval
    [1,] -0.6264538 -0.8204684
    [2,]  0.1836433  0.4874291
    [3,] -0.8356286 -0.3053884
    [4,]  1.5952808  0.7383247
    [5,]  0.3295078  0.5757814
    

    【讨论】:

    • 你知道矢量化解决方案是否可行吗?
    • @Wicelo 翻译成 Rcpp 很简单。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 2015-01-11
    相关资源
    最近更新 更多