【问题标题】:Calculating all distances between one point and a group of points efficiently in R在R中有效地计算一个点和一组点之间的所有距离
【发布时间】:2010-06-12 18:12:46
【问题描述】:

首先,我是 R 新手(我是昨天开始的)。

我有两组点,datacenters,第一个大小为 n,第二个大小为 K(例如,n = 3823K = 10),并且对于每个第一组i,我需要找到第二组j,距离最小。

我的想法很简单:对于每个i,让dist[j]ij 之间的距离,我只需要使用which.min(dist) 来查找我要查找的内容。

每个点都是64双精度数组,所以

> dim(data)
[1] 3823   64
> dim(centers)
[1] 10 64

我试过了

for (i in 1:n) {
  for (j in 1:K) {
    d[j] <- sqrt(sum((centers[j,] - data[i,])^2))
  }
  S[i] <- which.min(d)
}

这非常慢(n = 200,它需要超过 40 秒!!)。我写的最快的解决方案是

distance <- function(point, group) {
  return(dist(t(array(c(point, t(group)), dim=c(ncol(group), 1+nrow(group)))))[1:nrow(group)])
}

for (i in 1:n) {
  d <- distance(data[i,], centers)
  which.min(d)
}

即使它做了很多我不使用的计算(因为dist(m) 计算m 的所有行之间的距离),它也比另一个快得多(谁能解释为什么?) ,但它不够快,无法满足我的需要,因为它不会只使用一次。而且,distance 代码非常难看。我尝试将其替换为

distance <- function(point, group) {
  return (dist(rbind(point,group))[1:nrow(group)])
}

但这似乎慢了两倍。我也尝试对每一对使用dist,但速度也较慢。

我现在不知道该怎么办。好像我做错了什么。关于如何更有效地做到这一点的任何想法?

ps:我需要这个来手动实现 k-means(我需要这样做,这是作业的一部分)。我相信我只需要欧几里得距离,但我还不确定,所以我更喜欢有一些可以轻松替换距离计算的代码。 stats::kmeans 在不到一秒的时间内完成所有计算。

【问题讨论】:

  • 这里的人都在做一些不喜欢做的任务...所以试着专注于一个特定的问题。

标签: r performance distance


【解决方案1】:

您可以将其浓缩为矩阵运算,而不是遍历数据点,这意味着您只需遍历K

# Generate some fake data.
n <- 3823
K <- 10
d <- 64
x <- matrix(rnorm(n * d), ncol = n)
centers <- matrix(rnorm(K * d), ncol = K)

system.time(
  dists <- apply(centers, 2, function(center) {
    colSums((x - center)^2)
})
)

运行于:

utilisateur     système      écoulé 
      0.100       0.008       0.108 

在我的笔记本电脑上。

【讨论】:

  • +1 击败了我计算 dists 矩阵的方法。这是在矩阵中添加或减去自动复制向量的好技巧。
  • 我正在尝试使用您的解决方案,但您的矩阵已转置。有没有办法像用列一样减去行?
  • 我尝试使用 apply 用行进行减法,但它没有您的解决方案那么快。我现在正在转置矩阵并使用您的代码,它真的很快!非常感谢!!!另外,感谢您通过一个小示例和 system.time 的使用提供完整的答案。谢谢美人 :)
【解决方案2】:

rdist() 是来自 {fields} 包的 R 函数,它能够以矩阵格式快速计算两组点之间的距离。

https://www.image.ucar.edu/~nychka/Fields/Help/rdist.html

用法:

library(fields)
#generating fake data
n <- 5
m <- 10
d <- 3

x <- matrix(rnorm(n * d), ncol = d)
y <- matrix(rnorm(m * d), ncol = d)

rdist(x, y)
          [,1]     [,2]      [,3]     [,4]     [,5]
 [1,] 1.512383 3.053084 3.1420322 4.942360 3.345619
 [2,] 3.531150 4.593120 1.9895867 4.212358 2.868283
 [3,] 1.925701 2.217248 2.4232672 4.529040 2.243467
 [4,] 2.751179 2.260113 2.2469334 3.674180 1.701388
 [5,] 3.303224 3.888610 0.5091929 4.563767 1.661411
 [6,] 3.188290 3.304657 3.6668867 3.599771 3.453358
 [7,] 2.891969 2.823296 1.6926825 4.845681 1.544732
 [8,] 2.987394 1.553104 2.8849988 4.683407 2.000689
 [9,] 3.199353 2.822421 1.5221291 4.414465 1.078257
[10,] 2.492993 2.994359 3.3573190 6.498129 3.337441

【讨论】:

    【解决方案3】:

    您可能想查看apply 函数。

    比如这段代码

    for (j in 1:K)
        {
        d[j] <- sqrt(sum((centers[j,] - data[i,])^2))
        }
    

    可以很容易地用类似的东西代替

    dt <- data[i,]
    d <- apply(centers, 1, function(x){ sqrt(sum(x-dt)^2)})
    

    你当然可以进一步优化它,但我希望你明白了

    【讨论】:

    • 谢谢...它比我编写的第一个代码要快,但甚至比不上使用distance 的奇怪代码。
    • @dbarbosa:好吧,显然stats::kmeans 包使用了明显更快的编译代码。只需输入kmeans,您就会看到它的源代码。 :)
    【解决方案4】:

    dist 工作速度很快,因为没有矢量化并调用内部 C 函数。
    您可以通过多种方式对循环中的代码进行矢量化。

    例如,要计算datacenters 之间的距离,您可以使用outer

    diff_ij <- function(i,j) sqrt(rowSums((data[i,]-centers[j,])^2))
    X <- outer(seq_len(n), seq_len(K), diff_ij)
    

    这为您提供n x K 距离矩阵。而且应该比循环快。

    然后您可以使用max.col 在每一行中找到最大值(请参阅帮助,当有很多最大值时会有一些细微差别)。 X 必须是否定的,因为我们搜索最小值。

    CL <- max.col(-X)
    

    为了在 R 中高效,您应该尽可能矢量化。在许多情况下,循环可以被矢量化替换所取代。查看rowSums 的帮助(还描述了rowMeanscolSumsrowSums)、pmaxcumsum。您可以搜索 SO,例如 https://stackoverflow.com/search?q=[r]+avoid+loop (复制并粘贴此链接,我不知道如何使其可点击)以获取一些示例。

    【讨论】:

    • 嗨,我正在尝试使用您的代码,但它不起作用。我尝试将它与@Jonathan Chang 编写的相同代码一起使用,并添加:system.time(outer(seq_len(n), seq_len(K), function(i,j) sqrt(rowSums((x[,i]-centers[,j])^2)))),但我收到此错误:Error in dim(robj) &lt;- c(dX, dY) : dims [product 38230] do not match the length of object [64] 你看到有什么问题吗?
    • 其实我不理解outer(我以为每对调用一次函数)。现在我明白了,谢谢,它很有用!另外,感谢您告知max.col
    【解决方案5】:

    我的解决方案:

    # data is a matrix where each row is a point
    # point is a vector of values
    euc.dist <- function(data, point) {
      apply(data, 1, function (row) sqrt(sum((point - row) ^ 2)))
    }
    

    你可以试试,比如:

    x <- matrix(rnorm(25), ncol=5)
    euc.dist(x, x[1,])
    

    【讨论】:

      猜你喜欢
      • 2018-03-23
      • 1970-01-01
      • 1970-01-01
      • 2019-03-23
      • 2019-02-22
      • 1970-01-01
      • 2019-10-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多