【问题标题】:Create a distance matrix in R using parallelization使用并行化在 R 中创建距离矩阵
【发布时间】:2013-06-16 12:14:31
【问题描述】:

我有 N 个包含推文累积频率的向量,为澄清起见,这些向量之一是 (0, 0, 1, 1, 2, 3, 4, 4, 5, 5, 6, 6, ... )

我想通过创建热图来可视化这些频率的差异。为此,我首先想创建一个包含推文之间欧几里德距离的 NxN 矩阵。我的第一种方法很像 Java,看起来像这样:

create_dist <- function(x){
  n <- length(x)                             #number of tweets
  xy <- matrix(nrow=n, ncol=n)               #create NxN matrix
  colnames(xy) <- names(x)                   #set column
  rownames(xy) <- names(x)                   #and row names

  for(i in 1:n) {
    for(j in 1:n){
      xy[i,j] <- distance(x[[i]], x[[1]])    #calculate euclidean distance for now, but should be interchangeable 
    }
  }

  xy
}

我测量了创建这个距离矩阵所需的时间,对于一个小样本(大约 2000 条推文),它已经花费了大约 35 秒。

> system.time(create_dist(cumFreqs))
user  system elapsed 
34.572   0.000  34.602 

现在我考虑如何稍微加快计算速度,因为我的计算机有 8 个内核,我想如果我使用并行化可能会更快。

像我这个 R 新手一样,我将内部 for 循环更改为 foreach 循环。

#libraries
library(foreach)
library(doMC)
registerDoMC(4)

create_dist <- function(x){
  n <- length(x)                                #number of tweets
  xy <- matrix(nrow=n, ncol=n)                  #create NxN matrix
  colnames(xy) <- names(x)                      #set column
  rownames(xy) <- names(x)                      #and row names

  for(i in 1:n) {
    xy[i,] <- unlist(foreach(j=1:n) %dopar% {   #set each row of the matrix
      distance(x[[i]], x[[j]])
    })
  }

  xy
}

再次,我想测量使用 system.time() 为两千条推文样本创建距离矩阵所需的时间,但我在 10 分钟后取消了执行,因为显然根本没有加速.

我搜索了解决方案,但不幸的是我没有找到任何解决方案。现在我想问你是否有更好的方法来创建这个距离矩阵,也许是一个应用函数,我没有羞耻地承认仍然让我感到困惑。

【问题讨论】:

  • 为什么不使用?dist?应该比您的解决方案快很多。
  • 我相信如果你并行化外循环而不是内循环,你会获得更好的性能。为了获得好处,即使存在并行化开销,每次迭代都需要性能密集型。但是,我相信您可以摆脱代码中所有显式的 R 循环(请参阅@sgibb 的评论)。
  • 或者,您可以用 C++ 编写距离计算,并使用 inline 包将其合并到 R 中。
  • 我也想过用dist,但是我用的距离函数以后应该可以互换的。
  • 也许你想看看proxy 包。它支持 48 种不同的距离测量。计算基于矩阵,而且速度非常快。

标签: r parallel-processing


【解决方案1】:

如前所述,您可以使用dist 函数。这里是一个如何使用dist 的结果创建热图的示例。

nn <- paste0('row',1:5)
x <- matrix(rnorm(25), nrow = 5,dimnames=list(nn))
distObj <- dist(x)
cols <- c("#D33F6A", "#D95260", "#DE6355", "#E27449", 
            "#E6833D", "#E89331", "#E9A229", "#EAB12A", "#E9C037", 
            "#E7CE4C", "#E4DC68", "#E2E6BD")
## mandatory coercion
distObj <- as.matrix(distObj)
## hetamap
image(distObj[order(nn), order(nn)], col = cols, 
      xaxt = "n", yaxt = "n")
## axes labels
axis(1, at = seq(0, 1, length.out = dim(distObj)[1]), labels = nn, 
     las = 2)
axis(2, at = seq(0, 1, length.out = dim(distObj)[1]), labels = nn, 
     las = 2)

【讨论】:

  • 因此,使用您的cumFreqs 向量列表,您可以执行以下操作:x &lt;- do.call(rbind, cumFreqs),然后是distObj &lt;- dist(x)。对于 2000 个长度为 100 的向量,这只需几秒钟。
【解决方案2】:

就像 'agstudy' 建议的那样,使用内置的 'dist' 函数。

为了将来参考,R 中的嵌套 for 循环非常慢。由于 R 是一种函数式语言,因此请尝试将矢量化操作与 apply 系列(apply、lapply、sapply、tapply)等函数一起使用。当您习惯了类似 C 的范例时,需要一些时间来考虑以函数式方式编写任务。

这里是关于 for 循环和应用风格之间基准的有用讨论:Is R's apply family more than syntactic sugar?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-23
    • 2015-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多