将功能应用于所有成对的行(或列)的有效方法

时间:2019-08-22 19:30:47

标签: r matrix function

给出一个矩阵(可能是一个很大的维),我如何有效地计算结果矩阵d,对于某些给定的函数d[i,j] = fun(x[, c(i,j)]),每个值都定义为fun

以下是示例

x = matrix(1:30, 5)
d = matrix(,ncol(x), ncol(x)) ## the output matrix
for(i in 1:ncol(x)) ## I use a for loop here, should find a more efficient way
for(j in 1:ncol(x)) 
d[i,j] = sum(apply(x[,c(i,j)], 1, min))

3 个答案:

答案 0 :(得分:1)

sapply循环会稍快

sapply(1:NCOL(x), function(i) sapply(1:NCOL(x), function(j){
    sum(apply(x[, c(i, j)], 1, min))
}))
#     [,1] [,2] [,3] [,4] [,5] [,6]
#[1,]   15   15   15   15   15   15
#[2,]   15   40   40   40   40   40
#[3,]   15   40   65   65   65   65
#[4,]   15   40   65   90   90   90
#[5,]   15   40   65   90  115  115
#[6,]   15   40   65   90  115  140

答案 1 :(得分:1)

考虑工作地点。

您要检查x的所有成对的列。对于每一对,您将创建一个n x 2矩阵,并对其应用一些函数。在许多情况下,例如所示的情况,一些工作将用于移动数据以挑选出那些列并创建那些新矩阵。 (循环开销很小。)其余的工作将用于应用该函数。 R提供了提高两者速度的机会:

  1. 当仅读取数据而不由函数修改数据时,R具有一些内置的自动优化功能,可使用指针而不是完整副本来引用它们。

  2. 某些函数在应用于简单(一维)数组时会固有地进行矢量化,但在使用apply*函数或通过循环调用时可能会变慢。

这些为我们提供了一些有关在提高数组操作速度时应在何处查看的指导。详细信息取决于fun的工作,因此让我们考虑问题中的示例:计算n by 2数组的每一行中的较小者,并对这些结果求和。 R支持内置的矢量化(非常快)功能pmin,用于计算行的最小值。这表明了以下解决方案:

n <- 50
m <- 100
x <- matrix(runif(n*m), n)
system.time({
  y <- matrix(NA_real_, NCOL(x), NCOL(x))
  for (i in seq_len(NCOL(y)))
    for (j in seq_len(NCOL(y)))
     y2[i,j] <- sum(pmin(x[, i], x[, j]))
})

在最好的情况下,我们知道时序最终将在n中为线性,在m中为二次。这是此解决方案相对于该线程的另一个答案中推荐的sapply方法所提供的加速效果的实证研究。

Figure: raster graphic of speedup as a function of m and n

此研究是在四个Xeon内核上使用Microsoft R Open(3.5.1)进行的。对于较小的m,相对时间是不确定的,因为此解决方案几乎不需要可测量的时间。请注意,显示的值是倍数,而不是百分比:因此,例如,n = 400列的30+的典型倍数意味着该解决方案花费的时间少于sapply解决方案时间的1/30。

模式很清楚pmin的向量化对于大量行(n)取得了很大的成就,而R中的基础优化最初对于少量的列(m)(少于40个左右)会有很大的影响,但是对于较大的m几乎没有任何影响。

课程是,您应该通过向量化来努力改善fun的时序,而不必担心循环开销。

答案 2 :(得分:0)

这是基本的R解决方案,仅计算一半的值。这是因为两个for循环的编码方式,结果矩阵是对称的。
我已经定义了要应用的函数fun

fun <- function(x, i, j) sum(apply(x[, c(i, j)], 1, min))

f1 <- function(x){
  d = matrix(NA, ncol(x), ncol(x))
  for(i in 1:ncol(x)){ ## I use a for loop here, should find a more efficient way
    for(j in 1:ncol(x)) 
      d[i, j] = fun(x, i, j)
  }
  d
}

f2 <- function(x){
  d = matrix(NA, ncol(x), ncol(x))
  for(i in 1:ncol(x)) {
    for(j in i:ncol(x)) d[i, j] = fun(x, i, j)
  }
  d[lower.tri(d)] <- t(d)[lower.tri(t(d))]
  d
}


library(microbenchmark)

n <- 1e3
x = matrix(1:n, 125)

mb <- microbenchmark(
  f1 = f1(x),
  f2 = f2(x)
)
mb
#Unit: milliseconds
# expr       min        lq      mean    median        uq      max neval cld
#   f1 14.117403 14.365764 15.297683 14.633804 15.202872 22.57475   100   b
#   f2  7.964885  8.113796  8.650553  8.252852  8.399395 17.33304   100  a 

这是平均43%的时间增加。