【问题标题】:Adding matrices based on row and column designation根据行和列指定添加矩阵
【发布时间】:2012-07-20 18:47:33
【问题描述】:

我确信这个问题的答案在某个地方,但我认为我没有使用正确的搜索词。

这是我的问题。我有多个矩阵(在这里我将简化为两个),其中每一行都是一个唯一标记的个体(其中一些在矩阵之间共享,而另一些则不是),以及共享的常见列标题。

例如:

first<-matrix(rbinom(20,1,.5),4,5)
first[,1]=c(122,145,186,199)
colnames(first)<-c("ID",901,902,903,904)
first
      ID 901 902 903 904
[1,] 122   1   0   0   0
[2,] 145   0   0   0   1
[3,] 186   0   0   1   1
[4,] 199   1   0   0   0

second<-matrix(rbinom(30,1,.5),6,5)
second[,1]=c(122,133,142,151,186,199)
colnames(second)<-c("ID",901,902,903,904)
second
      ID 901 902 903 904
[1,] 122   0   1   1   1
[2,] 133   0   0   0   1
[3,] 142   1   1   0   1
[4,] 151   0   1   0   0
[5,] 186   1   0   1   1
[6,] 199   1   0   0   0

我想根据“ID”和列名一起添加“第一”和“第二”。这将产生一个包含 7 行的矩阵(因为“第一个”矩阵中有 4 个 ID,“第二个”矩阵中有 3 个新 ID 和 3 个旧 ID:“122,133,142,145,151,186,199”)和相同的列数。

在这个例子中,我想要的结果是:

      ID 901 902 903 904
[1,] 122   1   1   1   1
[2,] 133   0   0   0   1
[3,] 142   1   1   0   1
[4,] 145   0   0   0   1
[5,] 151   0   1   0   0
[6,] 186   1   0   2   2
[7,] 199   2   0   0   0

【问题讨论】:

    标签: r matrix


    【解决方案1】:

    原答案

    基于@RYogi 的方法,您使用行名和列名来描述您的矩阵,我提出以下建议:

    res <- rbind(first,second)
    res <- tapply(res, expand.grid(dimnames(res)), sum)
    

    具有相同行名的所有行将被求和。

    使用数据框时

    如果您的输入是data.frame,上述方法将不起作用,因为data.frame 不能有任何重复的行名。另一种方法也适用于此:

    rowsum(rbind(first, second), c(rownames(first), rownames(second)))
    

    这种方法也适用于矩阵。由于它只需要一行,您可能会认为它更简单。我想它也可能更有效,因为它不如tapply 通用。您可以将此解决方案调整为问题中的数据格式,其中标识符位于单独的列中:

    rowsum(rbind(first, second)[,-1], c(first[,1], second[,1]))
    

    请注意,结果仍然会有命名行,而不是包含这些名称的列。

    有趣的是,我在寻找rowSums 时无意中读到了rowsum 的内容,这里使用了一个相当复杂的方法来解决这个问题的data.frame 版本。祝我好运。

    其他提示

    如果您发现生成的尺寸名称 Var1Var2 令人困惑,您可以使用

    names(dimnames(res)) <- NULL
    

    如果您的数据确实是您描述的格式,在第一个数据列中使用行名称,您可以使用以下命令将它们更改为正确的行名称:

    rownames(first) <- first[,1]
    first <- first[,-1]
    

    【讨论】:

    • 我不知道为什么,但是当我在我的真实数据集(我使用 ID 作为行名)上使用 rbind 时,重复的行名在它们的末尾附加了一个数字。例如,如果 ID# 165320128 出现 3 次,则一行将是“165320128”,下一个是“1653201281”,最后一个是“1653201282”
    • @user1399311,您的原始数据是否存储在数据帧而不是矩阵中?它们似乎表现出您描述的行为,因为 data.frame 不允许重复的行名。您可以将它们转换为矩阵,但我会编辑我的答案以提供更好的解决方案。
    【解决方案2】:

    我一直在寻找一个没有“for”循环的解决方案,使用内置函数但没有成功。 所以这是我的方法

    set.seed(1) # make it reproducible
    first <- matrix(rbinom(20,1,.5),4,5)
    first[ ,1] <- c(122, 145, 186, 199)
    colnames(first) <- c("ID", 901, 902, 903, 904)
    
    second <- matrix(rbinom(30, 1, .5), 6, 5)
    second[ ,1] <- c(122, 133, 142, 151, 186, 199)
    colnames(second) <- c("ID", 901, 902, 903, 904)
    
    first
    
          ID 901 902 903 904
    [1,] 122   0   1   1   1
    [2,] 145   1   0   0   1
    [3,] 186   1   0   1   0
    [4,] 199   1   0   0   1
    
    second
          ID 901 902 903 904
    [1,] 122   0   0   1   1
    [2,] 133   0   0   0   1
    [3,] 142   1   1   1   0
    [4,] 151   0   1   1   0
    [5,] 186   0   1   1   1
    [6,] 199   1   0   1   1
    
    ## stack them rowise
    mat <- rbind(first, second)
    
    ind <- unique(mat[,"ID"])
    
    result <- matrix(nrow = length(ind), ncol = 5)
    result[,1] <- ind
    
    for (i in seq_along(ind)) {
        result[i,-1] <- colSums(mat[mat[ ,"ID"] == ind[i], -1, drop = FALSE])
    }
    colnames(result) <- colnames(mat)
    
    result
          ID 901 902 903 904
    [1,] 122   0   1   2   2
    [2,] 145   1   0   0   1
    [3,] 186   1   1   2   1
    [4,] 199   2   0   1   2
    [5,] 133   0   0   0   1
    [6,] 142   1   1   1   0
    [7,] 151   0   1   1   0
    

    【讨论】:

      【解决方案3】:

      我对你的问题的设置略有不同:

      first <- matrix(rbinom(16,1,.5),4,4)
      rownames(first) <- c(122,145,186,199)
      colnames(first) <- c(901,902,903,904)
      
      second <- matrix(rbinom(24,1,.5),6,4)
      rownames(second) <- c(122,133,142,151,186,199)
      colnames(second) <- c(901,902,903,904)
      

      矩阵现在已命名为行名

      > first
          901 902 903 904
      122   1   0   0   1
      145   1   0   0   0
      186   0   0   1   1
      199   1   0   1   1
      > second
          901 902 903 904
      122   1   1   0   0
      133   0   0   1   1
      142   1   0   1   0
      151   1   0   1   1
      186   0   1   0   1
      199   0   0   0   0
      

      现在对行名进行设置操作很容易:

      SumOnID <- function(A, B){
        rnA <- rownames(A)
        rnB <- rownames(B)
      
        ls.id <- list(ids = intersect(rnA, rnB), #shared indices
                      idA = setdiff(rnA, rnB),   #only in A
                      idB = setdiff(rnB, rnA))   #only in B
      
        do.call(rbind, 
          lapply(names(ls.id), function(x){
            if (x == "ids") return(A[x,, drop = F] + B[x,, drop = F])
            if (x == "idA") return(A[x,, drop = F])
            if (x == "idB") return(B[x,, drop = F])
          }))
      }
      

      让我们试试吧:

      > SumOnID(first, second)
          901 902 903 904
      122   2   1   1   1
      186   1   1   0   1
      199   2   1   1   0
      145   1   1   0   1
      133   1   0   1   1
      142   1   0   1   0
      151   1   1   1   1
      

      【讨论】:

        猜你喜欢
        • 2014-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-22
        • 2020-07-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多