在 R 中有条件地添加列元素

Adding column elements conditionally in R

可重现的例子:

set.seed(1)
testMat <- matrix(round(runif(3*6,1,5)), nrow = 3, ncol = 6)

输出:

     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    2    5    5    1    4    3
[2,]    2    2    4    2    3    4
[3,]    3    5    4    2    4    5

此处前 3 列 (1,2,3) 属于一组,接下来的 3 列 (4,5,6) 属于另一组。我想从每组中添加一列,并且需要为所有可能的组合添加一列。

对于这个例子,我应该得到 9 个结果向量,因为有 9 种组合:

Combination 1: (1,4) = (3,4,5)
Combination 2: (1,5) = (6,5,7)
Combination 3: (1,6) = (.,.,.)
Combination 4: (2,4) = (.,.,.)
Combination 5: (2,5) = (.,.,.)
Combination 6: (2,6) = (.,.,.)
Combination 7: (3,4) = (.,.,.) 
Combination 8: (3,5) = (.,.,.)
Combination 9: (3,6) = (.,.,.)

有什么优雅的方法可以做到这一点,尤其是当列数可以更高时?例如 9、12 等将分别产生 27 和 81 的组合。

编辑:更多说明:每 3 列(例如 1:3、4:6、7:9、10:12、...等)构造一组,然后objective是每组取1列相加。例如,如果我们在 testMat 中有 6 列,我们从 1:3 中取 1 列,从 4:6 中取另一列,将 tese 2 列加在一起。类似地,对于 9,我们添加 3,对于 12,我们从每个集合中添加 4 列。

expand.grid 会出奇地有用:


nc <- ncol( testMat )

if( nc %% 3 != 0 ) {
  stop( "Your data's number of columns should be a multiple of 3!")
}

n <- ncol( testMat ) / 3

args <- sapply( 1:n, function(i) (i*3-2):(i*3), simplify=FALSE )

combs <- do.call( expand.grid, args ) %>% arrange( Var1 )

combs %>% apply( 1, function(r) {
              rowSums( testMat[, r] )
          })



输出:


> combs %>% apply( 1, function(r) {
+               rowSums( testMat[, r ] )
+           })
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,]    3    6    5    6    9    8    6    9    8
[2,]    4    5    6    4    5    6    6    7    8
[3,]    5    7    8    7    9   10    6    8    9

这是一种方法:

#Create a sequence from 1 to number of columns in the data
cols <- seq(ncol(testMat))
n <- 3
#Create a list of every n columns and use expand.grid 
#to create all possible combinations
vals <- expand.grid(split(cols, ceiling(cols/n)))
#RowSums every combination using `sapply`.
sapply(asplit(vals, 1), function(x) rowSums(testMat[, x]))

#     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#[1,]    3    6    6    6    9    9    5    8    8
#[2,]    4    4    6    5    5    7    6    6    8
#[3,]    5    7    6    7    9    8    8   10    9

这是一种使用 生成排列的方法:

library(RcppAlgos)

my_vals = function (...) {
    dots = list(...)
    ncols = ...length()
    cols = permuteGeneral(3L, ncols, TRUE)
    
    Reduce(`+`, Map(`[`, dots, asplit(cols,2L)))
}

do.call('my_fun', asplit(array(testMat, c(3L, 3L, ncol(testMat) / 3L)), 3L))

这种方法将矩阵转换为子矩阵列表,然后通过排列对子矩阵进行子矩阵化。