在R中对距离矩阵应用函数

7
今天 manipulatr 邮件列表中出现了这个问题。
http://groups.google.com/group/manipulatr/browse_thread/thread/fbab76945f7cba3f

我将进行重新表述。

给定一个距离矩阵(使用dist计算),对距离矩阵的行应用函数。

代码:

library(plyr)
N <- 100
a <- data.frame(b=1:N,c=runif(N))
d <- dist(a,diag=T,upper=T)
sumd <- adply(as.matrix(d),1,sum)

问题在于按行应用函数需要存储整个矩阵(而不仅仅是下三角部分)。因此,对于大型矩阵来说,它使用了太多的内存。对于维度约为10000的矩阵,在我的计算机上运行失败。
有任何想法吗?
3个回答

6

首先,对于还没有看过的人,我强烈推荐阅读关于代码优化的这篇文章

这里有另一个版本,它不使用ifelse(这是一个相对较慢的函数):

noeq.2 <- function(i, j, N) {
    i <- i-1
    j <- j-1
    x <- i*(N-1) - (i-1)*((i-1) + 1)/2 + j - i
    x2 <- j*(N-1) - (j-1)*((j-1) + 1)/2 + i - j
    idx <- i < j
    x[!idx] <- x2[!idx]
    x[i==j] <- 0
    x
}

我的笔记本电脑的时间设置为:

> N <- 1000
> system.time(sapply(1:N, function(i) sapply(1:N, function(j) noeq(i, j, N))))
   user  system elapsed 
  51.31    0.10   52.06 
> system.time(sapply(1:N, function(j) noeq.1(1:N, j, N)))
   user  system elapsed 
   2.47    0.02    2.67 
> system.time(sapply(1:N, function(j) noeq.2(1:N, j, N)))
   user  system elapsed 
   0.88    0.01    1.12 
比更快:
> system.time(do.call("rbind",lapply(1:N, function(j) noeq.2(1:N, j, N))))
   user  system elapsed 
   0.67    0.00    0.67 

2
你好,看起来你的链接已经失效了,你能修复一下吗? - jay.sf

4

这是函数noeq(参数ij)的矢量化版本:

noeq.1 <- function(i, j, N) {
    i <- i-1
    j <- j-1
    ifelse(i < j,
           i*(N-1) - ((i-1)*i)/2 + j - i,
           j*(N-1) - ((j-1)*j)/2 + i - j) * ifelse(i == j, 0, 1)
}   

> N <- 4
> sapply(1:N, function(i) sapply(1:N, function(j) noeq(i, j, N)))
     [,1] [,2] [,3] [,4]
[1,]    0    1    2    3
[2,]    1    0    4    5
[3,]    2    4    0    6
[4,]    3    5    6    0
> sapply(1:N, function(i) noeq.1(i, 1:N, N))
     [,1] [,2] [,3] [,4]
[1,]    0    1    2    3
[2,]    1    0    4    5
[3,]    2    4    0    6
[4,]    3    5    6    0

测试时间是在一台2.4 GHz英特尔Core 2 Duo处理器的电脑上进行的(Mac OS 10.6.1):

> N <- 1000
> system.time(sapply(1:N, function(j) noeq.1(1:N, j, N)))
   user  system elapsed 
  0.676   0.061   0.738 
> system.time(sapply(1:N, function(i) sapply(1:N, function(j) noeq(i, j, N))))
   user  system elapsed 
 14.359   0.032  14.410

R 可以快速运行的好例子:提升了20倍! - Eduardo Leoni

2

我的解决方案是获取距离向量的索引,给定一行和矩阵的大小。我从codeguru得到了这个方法。

int Trag_noeq(int row, int col, int N)
{
   //assert(row != col);    //You can add this in if you like
   if (row<col)
      return row*(N-1) - (row-1)*((row-1) + 1)/2 + col - row - 1;
   else if (col<row)
      return col*(N-1) - (col-1)*((col-1) + 1)/2 + row - col - 1;
   else
      return -1;
}

翻译成R语言后,假设索引从1开始,并且假设是下三角矩阵,我得到了以下结果。
编辑:使用rcs贡献的向量化版本。

noeq.1 <- function(i, j, N) {
    i <- i-1
    j <- j-1
    ix <- ifelse(i < j,
                 i*(N-1) - (i-1)*((i-1) + 1)/2 + j - i,
                 j*(N-1) - (j-1)*((j-1) + 1)/2 + i - j) * ifelse(i == j, 0, 1)
    ix
}

## To get the indexes of the row, the following one liner works:

getrow <- function(z, N) noeq.1(z, 1:N, N)

## to get the row sums

getsum <- function(d, f=sum) {
    N <- attr(d, "Size")
    sapply(1:N, function(i) {
        if (i%%100==0) print(i)
        f(d[getrow(i,N)])
    })
}

所以,以这个例子为例:
sumd2 <- getsum(d)

在向量化之前,这比使用as.matrix慢得多。但是向量化后,只比as.matrix慢约3倍。在Intel Core2Duo 2GHz上,对大小为10000的矩阵按行求和需要100多秒时间。as.matrix方法失效了。感谢rcs!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接