All possible matrices of binary data on condition that row sums equals 1

Viewed 92

I am trying to generate matrices of m*n elements with binary data on the condition that the sum of the rows must equal 1.

For example, all the possible 2*2 matrices of binary data on condition that the row sums equal 1 are:

     [,1] [,2]
[1,]    1    0
[2,]    1    0

     [,1] [,2]
[1,]    0    1
[2,]    0    1

     [,1] [,2]
[1,]    0    1
[2,]    1    0

     [,1] [,2]
[1,]    1    0
[2,]    0    1

Can anyone help out with some neat code to achieve such an output? Or is there a function that can help with this?

2 Answers

With base R this can be a solution,

#    m : number of columns
#    n : number of rows

 my_fun <- function(m,n) {

    a <- max(m,n)

    mat <- diag(1, a, a)

    x <- 1:nrow(mat)

    y <- paste0(rep("x",n),collapse=",")

    exp <- paste0("expand.grid(",y,")")
    all_com <- eval(parse(text=exp ))


    out <- lapply(1:nrow(all_com),function(x){

    if(m>n) {
        mat[as.numeric(all_com[x,]),]


    }else{

      mat <- mat[as.numeric(all_com[x,]),][,1:m]
      mat <- mat[rowSums(mat)==1,]

    }
            })

    

    out <- out[lapply(out,length) == m*n]

      return(unique(out))

}
my_fun(2,2)

gives,

[[1]]
     [,1] [,2]
[1,]    1    0
[2,]    1    0

[[2]]
     [,1] [,2]
[1,]    0    1
[2,]    1    0

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

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

A straightforward approach involves generating all vectors of length n containing n - 1 zeros and 1 one. This is reduced to all permutations of the multiset {0, 0, ... ,0, 1}. Let's say that there are K such permutations.

Once we have all of these, we generate the permutations of K with repetition of size m, where m is the desired number of rows. We use each of these results to subset the permutations of the zeros and ones.

Below, we have implemented this using the library RcppAlgos (disclosure: I am the author). The first part (i.e. generating permutations of multisets) is accomplished using the freqs parameter. The second part is accomplished using the FUN parameter, which allows one to pass arbitrary functions that act on each permutation.

library(RcppAlgos)

binMat <- function(m, n, row_sum = 1) {
    perms <- if (n == row_sum) {
        permuteGeneral(1, n, repetition = TRUE)
    } else {
        permuteGeneral(0:1, n, freqs = c(n - row_sum, row_sum))
    }
    
    permuteGeneral(nrow(perms), m, repetition = TRUE, FUN = function(x) {
        perms[x, ]
    })
}

Note, in the above that one can generate matrices with different row sums using the row_sum parameter.

Here is an example:

binMat(3, 2)
[[1]]
     [,1] [,2]
[1,]    0    1
[2,]    0    1
[3,]    0    1

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

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

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

[[5]]
     [,1] [,2]
[1,]    1    0
[2,]    0    1
[3,]    0    1

[[6]]
     [,1] [,2]
[1,]    1    0
[2,]    0    1
[3,]    1    0

[[7]]
     [,1] [,2]
[1,]    1    0
[2,]    1    0
[3,]    0    1

[[8]]
     [,1] [,2]
[1,]    1    0
[2,]    1    0
[3,]    1    0

It's efficient as well:

system.time(testMany <- binMat(7, 7))
 user  system elapsed 
1.936   0.062   1.999

testMany[[1]]
     [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,]    0    0    0    0    0    0    1
[2,]    0    0    0    0    0    0    1
[3,]    0    0    0    0    0    0    1
[4,]    0    0    0    0    0    0    1
[5,]    0    0    0    0    0    0    1
[6,]    0    0    0    0    0    0    1
[7,]    0    0    0    0    0    0    1

length(testMany)
[1] 823543
Related