R:Binary Matrix for All Possible Unique Results

All possible combination of binary matrix with constraint

We want to describe all possible combinations of mappings (interactions) between stores (rows) and products (columns). In the interaction matrix, an interaction is encoded by 1, its absence by 0. The constraint is that the column and row sum are greater equal 1.

This is an interesting combinatorics question; my approach by enumerating all possible combinations is not very efficient; I'm sure there are much more efficient implementations that take into account possible symmetries of the matrices. It'd be interesting to see other solutions...

In the following, I make use of function gtools::permutations.

matrixCombn <- function(nrow, ncol) {
# All possible column combinations for a matrix of dimension dim
# Here we assume two different values c(1, 0)
require(gtools);
columns <- t(permutations(n = 2, r = nrow, v = c(1, 0), repeats.allowed = TRUE));
columns <- columns[, colSums(columns) > 0];

# Construct all possible combinations of dim column vectors and
# impose constraint that row and column sum >= 1
ret <- lapply(as.data.frame(t(permutations(ncol(columns), ncol, repeats.allowed = TRUE))), function(x) {
m.cand <- columns[, x];
if (all(rowSums(m.cand) > 0) & all(colSums(m.cand) > 0)) m.cand else NULL;
})
ret <- Filter(Negate(is.null), ret);
return(ret);
}

# Example for 3x3 interaction matrix design
ret <- matrixCombn(3, 3);

This gives us 265 unique combinations

length(ret);
# [1] 265

For example, here are the first 3

ret[1:3];
#$V6
# [,1] [,2] [,3]
#[1,] 0 0 1
#[2,] 0 0 1
#[3,] 1 1 0

#$V7
# [,1] [,2] [,3]
#[1,] 0 0 1
#[2,] 0 0 1
#[3,] 1 1 1

#$V11
# [,1] [,2] [,3]
#[1,] 0 0 1
#[2,] 0 1 0
#[3,] 1 0 0

For a 3x2 interaction matrix design we get

ret <- matrixCombn(3, 2);
length(ret);
# [1] 25

with the first 3 combinations

ret[1:3];
#$V6
# [,1] [,2]
#[1,] 0 1
#[2,] 0 1
#[3,] 1 0

#$V7
# [,1] [,2]
#[1,] 0 1
#[2,] 0 1
#[3,] 1 1

#$V12
# [,1] [,2]
#[1,] 0 1
#[2,] 1 0
#[3,] 0 1

Random unique binary matrices

I suggest you just generate the matrices completely random and check for each new matrix if it already has been created. If not, discard it.

As long as the number of possible matrices is a lot bigger than the number of matrices needed this works well, otherwise it may be slow.

Example code:

randomMatrix <- function(n) matrix(as.numeric(runif(n^2) > 0.5), ncol = n)  # generates random binary square matrix of size n x n

m <- 1000 # number of matrices needed

res <- vector("list", m) # stores the result
i <- 1 # index of next matrix

while (i <= m) {

cand <- randomMatrix(5)
## check if the candidate is distinct from all matrices in res
if (!any(sapply(res, function(x) identical(x, cand)))) {
res[[i]] <- cand
i <- i + 1
}

}

If you really run into speed problems (which I doubt) you could use some hashes to speed up compoarison. For example you could store the row- and column sums for each matrix and only test for equality if the sums coincide.

Create combinations of a binary vector

A slightly faster version of Marat's answer:

f.roland <- function(n, m) {
ind <- combn(seq_len(n), m)
ind <- t(ind) + (seq_len(ncol(ind)) - 1) * n
res <- rep(0, nrow(ind) * n)
res[ind] <- 1
matrix(res, ncol = n, nrow = nrow(ind), byrow = TRUE)
}

all.equal(f.2(16, 8), f.roland(16, 8))
#[1] TRUE
library(rbenchmark)
benchmark(f(16,8),f.2(16,8),f.roland(16,8))

# test replications elapsed relative user.self sys.self user.child sys.child
#2 f.2(16, 8) 100 5.693 1.931 5.670 0.020 0 0
#3 f.roland(16, 8) 100 2.948 1.000 2.929 0.017 0 0
#1 f(16, 8) 100 8.287 2.811 8.214 0.066 0 0

Combinations of binary values in r

Some sample data:

n <- 10
dat <- data.frame(A = sample(0:1, n, replace = TRUE),
B = sample(0:1, n, replace = TRUE),
C = sample(0:1, n, replace = TRUE),
D = sample(0:1, n, replace = TRUE))

A function that given a number of columns to combine, computes all combinations and corresponding sums:

count.or <- function(dat, n = 2) {
or.sum <- function(cols) sum(rowSums(dat[cols]) > 0)
counts <- combn(colnames(dat), n, FUN = or.sum)
names <- combn(colnames(dat), n, FUN = paste, collapse = "")
setNames(counts, names)
}

In action:

count.or(dat, 1)
# A B C D
# 6 6 5 9
count.or(dat, 2)
# AB AC AD BC BD CD
# 8 7 9 9 10 9
count.or(dat, 3)
# ABC ABD ACD BCD
# 9 10 9 10
count.or(dat, 4)
# ABCD
# 10

Or in one call:

unlist(lapply(1:4, count.or, dat = dat))
# A B C D AB AC AD BC BD CD ABC ABD ACD BCD ABCD
# 6 6 5 9 8 7 9 9 10 9 9 10 9 10 10

R: Get all combinations of p binary variables

OK, found it:

p <- 3
l <- rep(list(0:1), p)
expand.grid(l)

From here:
Generate list of all possible combinations of elements of vector

R - generate all possible pairwise combinations of binary vectors

Something like this?

n <- 3
g <- 2 # g must be < n
m <- combn(n, g)
mm <- as.numeric(m)
mat <- matrix(0, nrow = g * ncol(m), ncol = n)
mat[ cbind(1:nrow(mat), mm)] <- 1

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

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

#[5,] 0 1 0
#[6,] 0 0 1

# mat is half the answer :)
# the other half is
mat[nrow(mat):1, ]

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

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

#[5,] 0 1 0
#[6,] 1 0 0

soln <- rbind(mat, mat[nrow(mat):1, ])

# as suggested by the OP to split the soln
d <- split(data.frame(soln), rep(1:(nrow(soln)/g), each=g))

How to create a matrix from all possible combinations of 2 or more matrices?

You can use expand.grid() and take its output to index the matrix A and B,

x <- expand.grid(1:3,1:3)

cbind(A[x[,1],], B[x[,2],])

gives,

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

EDIT:

For more than two matrices, you can use a function like below,

myfun <- function(...) {

arguments <- list(...)

a <- expand.grid(lapply(arguments, function(x) 1:nrow(x)))


do.call(cbind,lapply(seq(a),function(x) { arguments[[x]][a[,x],] }))


}

out <- myfun(A,B,C)

head(out)

gives,

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

Data:

A <- B <- diag(3)
C <- diag(4)


Related Topics



Leave a reply



Submit