在R中的大矩阵中添加连续的四个/ n个数字

Posted

技术标签:

【中文标题】在R中的大矩阵中添加连续的四个/ n个数字【英文标题】:adding successive four / n numbers in large matrix in R 【发布时间】:2014-10-21 11:46:25 【问题描述】:

我有一个非常大的数据集,维度为60K x 4 K。我正在尝试在每一行列中连续添加每四个值。以下是较小的示例数据集。

    set.seed(123)
    mat <- matrix (sample(0:1, 48, replace = TRUE), 4)

   [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,]    0    1    1    1    0    1    1    0    1     1     0     0
[2,]    1    0    0    1    0    1    1    0    1     0     0     0
[3,]    0    1    1    0    0    1    1    1    0     0     0     0
[4,]    1    1    0    1    1    1    1    1    0     0     0     0

这是我要执行的操作:

mat[1,1] + mat[1,2] + mat[1,3] + mat[1,4] = 0 + 1 + 1 + 1 = 3

即每四个值相加并输出。

mat[1,5] + mat[1,6] + mat[1,7] + mat[1,8] = 0 + 1 + 1 + 0 = 2

继续到矩阵的末尾(这里到 12)。

mat[1,9] + mat[1,10] + mat[1,11] + mat[1,12] 

一旦第一行完成,将相同的内容应用到第二行,例如:

mat[2,1] + mat[2,2] + mat[2,3] + mat[2,4] 
mat[2,5] + mat[2,6] + mat[2,7] + mat[2,8]
mat[2,9] + mat[2,10] + mat[2,11] + mat[2,12] 

结果将是nrow x (ncol)/4 矩阵。

预期结果如下:

          col1-col4      col5-8   col9-12
row1        3              2        2
row2        2              2        1
row3        2              3        0
row4        3              4        0

第 3 行与矩阵中的行数类似。我怎样才能有效地循环这个。

【问题讨论】:

【参考方案1】:

这可能是最慢的:

set.seed(123)
mat <- matrix (sample(0:1, 48, replace = TRUE), 4)
mat

output <- sapply(seq(4,ncol(mat),4), function(i)  apply(mat,1,function(j)
      sum(j[c(i-3, i-2, i-1, i)], na.rm=TRUE)
))

output

     [,1] [,2] [,3]
[1,]    3    2    2
[2,]    2    2    1
[3,]    2    3    0
[4,]    3    4    0

也许嵌套for-loops 会更慢,但这个答案非常接近嵌套for-loops

【讨论】:

【参考方案2】:

虽然 Matthew 的回答非常酷(+1,顺便说一句),但如果您避免使用 apply 并使用 *Sums 函数(在本例中为 colSums),您可以获得更快(~100x)的解决方案,并且一些向量操作技巧:

funSums <- function(mat) 
  t.mat <- t(mat)                                    # rows become columns
  dim(t.mat) <- c(4, length(t.mat) / 4)              # wrap columns every four items (this is what we want to sum)
  t(matrix(colSums(t.mat), nrow=ncol(mat) / 4))      # sum our new 4 element columns, and reconstruct desired output format

set.seed(123)
mat <- matrix(sample(0:1, 48, replace = TRUE), 4)
funSums(mat)

产生所需的输出:

     [,1] [,2] [,3]
[1,]    3    2    2
[2,]    2    2    1
[3,]    2    3    0
[4,]    3    4    0

现在,让我们制作一个真正的大小并与其他选项进行比较:

set.seed(123)
mat <- matrix(sample(0:1, 6e5, replace = TRUE), 4)

funApply <- function(mat)    # Matthew's Solution
  apply(array(mat, dim=c(4, 4, ncol(mat) / 4)), MARGIN=c(1,3), FUN=sum)

funRcpp <- function(mat)     # David's Solution
  roll_sum(mat, 4, by.column = F)[, seq_len(ncol(mat) - 4 + 1)%%4 == 1]

library(microbenchmark)
microbenchmark(times=10,
  funSums(mat),
  funApply(mat),
  funRcpp(mat)
)

生产:

Unit: milliseconds
          expr        min         lq     median       uq       max neval
  funSums(mat)   4.035823   4.079707   5.256517   7.5359  42.06529    10
 funApply(mat) 379.124825 399.060015 430.899162 455.7755 471.35960    10
  funRcpp(mat)  18.481184  20.364885  38.595383 106.0277 132.93382    10

然后检查:

all.equal(funSums(mat), funApply(mat))
# [1] TRUE
all.equal(funSums(mat), funRcpp(mat))
# [1] TRUE

关键是*Sums 函数是完全“矢量化”的,就像所有计算都发生在 C 中一样。apply 仍然需要做一堆不严格矢量化的(以原始 C 函数方式) 在 R 中的东西,并且速度较慢(但更灵活)。

针对这个问题,它可能会使其速度提高 2-3 倍,因为大约一半的时间花在换位上,这只是必要的,以便 dim 的更改可以满足我对 colSums 的需要工作。

【讨论】:

【参考方案3】:

这是使用RcppRoll 包的另一种方法

library(RcppRoll) # Uses C++/Rcpp
n <- 4 # The summing range
roll_sum(mat, n, by.column = F)[, seq_len(ncol(mat) - n + 1) %% n == 1]

##      [,1] [,2] [,3]
## [1,]    3    2    2
## [2,]    2    2    1
## [3,]    2    3    0
#3 [4,]    3    4    0

【讨论】:

【参考方案4】:

将矩阵划分为 3D 数组是一种方法:

apply(array(mat, dim=c(4, 4, 3)), MARGIN=c(1,3), FUN=sum)

#      [,1] [,2] [,3]
# [1,]    3    2    2
# [2,]    2    2    1
# [3,]    2    3    0
# [4,]    3    4    0

【讨论】:

以上是关于在R中的大矩阵中添加连续的四个/ n个数字的主要内容,如果未能解决你的问题,请参考以下文章

在R中添加具有连续数字的列

四个基本空间

最多有多少个连续子数组。 n 个唯一编号

华为机试真题 C++ 实现最小传输时延

怎样用取尺法处理连续区间内数字同样

输入一个正整数n,计算出[0,n]这些整数中的二进制数没有连续3个1的数字有多少