【问题标题】:Adding column elements conditionally in R在 R 中有条件地添加列元素
【发布时间】:2021-06-30 22:53:48
【问题描述】:

可重现的例子:

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 等)构成一组,目标是从每组中取出 1 列,将它们加在一起。例如,如果我们在 testMat 有 6 列,我们在 1:3 中取 1 列,从 4:6 中取另一列,将 tese 2 列加在一起。类似地,对于 9,我们添加 3,对于 12,我们添加 4 列,每列一列。

【问题讨论】:

  • 12 列应该是 81 而不是 64。
  • 是的,我的错。

标签: r combinations permutation calculated-columns


【解决方案1】:

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

【讨论】:

  • 您的解决方案也适用于 9 列。但我必须手动更改扩展网格的函数参数。如何根据 testMat 上可用的列数自动化它?
  • 前 3 列总是固定的吗? expand.grid( 1:3, 4:ncol(testMat) ) # 如果是的话
  • 比如9列的时候应该是expand.grid(1:3,4:6,7:9)。
  • 现在我重新检查了您的 9 列答案,但似乎并没有产生我期望的结果。每 3 列(例如 1:3、4:6、7:9、10:12 等)构成一组,目标是从每组中取出 1 列并将它们加在一起。对于 6 列,我们添加 2 列,对于 9 列,我们添加 3 列,对于 12 列,我们添加 4 列,依此类推。
  • 看看它是否适用于更新。它创建了 1:3、4:6 等的序列。它在使用 do.call 时运行 expand.grid。
【解决方案2】:

这是一种方法:

#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

【讨论】:

  • 这很优雅,但只有在 testMat 中有 6 列时才有效。超过六列(如9、12、15等),如何泛化?
  • 哎呀..你是对的。我更新了答案,因此它适用于任意数量的列。 @Rel_Ai
  • 如果这里的 testMat 是向量而不是矩阵,那我该怎么办?在这种情况下,结果当然是长度为 9 的向量。
  • 在这种情况下,我认为cols &lt;- seq(length(testMat))sapply 中的sapply(asplit(vals, 1), function(x) sum(testMat[x])) 应该可以工作。 `
  • 这似乎有效。谢谢。这是一个很大的帮助。
【解决方案3】:

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

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))

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

【讨论】:

    猜你喜欢
    • 2021-11-18
    • 2012-10-09
    • 2012-11-07
    • 1970-01-01
    • 2016-11-18
    • 2019-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多