【问题标题】:Applying a function to a distance matrix in R将函数应用于R中的距离矩阵
【发布时间】:2010-12-14 02:55:21
【问题描述】:

这个问题今天出现在 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 的矩阵.

有什么想法吗?

【问题讨论】:

    标签: algorithm r


    【解决方案1】:

    首先,对于还没看过这个的人,我强烈推荐reading this article on the r-wiki关于代码优化。

    这是另一个没有使用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 
    

    而且 lapply 比 sapply 快:

    > 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】:

    这是函数noeq 的矢量化版本(参数i 或j):

    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 Intel 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 倍改进!
    【解决方案3】:

    我的解决方案是在给定行和矩阵大小的情况下获取距离向量的索引。我是从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 开始,并假设我得到的是下 tri 而不是上 tri 矩阵。
    编辑:使用 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 慢很多。但是矢量化后的速度只有大约 3 倍。在 Intel Core2Duo 2ghz 中,按行对大小为 10000 的矩阵求和只需要 100 多秒。 as.matrix 方法失败。谢谢rcs!

    【讨论】:

      猜你喜欢
      • 2022-01-12
      • 1970-01-01
      • 1970-01-01
      • 2016-12-29
      • 1970-01-01
      • 2013-06-20
      • 2013-06-12
      • 1970-01-01
      相关资源
      最近更新 更多