Fate
Fate

Reputation: 43

How to assign a set of numbers randomly to a matrix in R with no row with all the same elements

I want to make a random matrix with n rows and m columns. I want the elements of each row of the matrix to be randomly chosen from a set of numbers. I want to have at least one different number in each row. I don't want all the elements of a row to be the same. I once asked my question here, but I don't know why the function I was provided still gives me some rows with all the same elements. How can I adjust this function?

f2 <- function(x, n, m) {
if ( length(unique(x)) == 1 ) {
    stop('x has only one unique element.', call. = FALSE)
}
result <- t(replicate(n, sample(x, m, replace = TRUE)))
while ( any(apply(result, 1, function(x) length(unique(result)) == 1)) ) {
    result <- t(replicate(n, sample(x, m, replace = TRUE)))
}
return(result)}

Here is an example:

x <- c(1, 1.5, 2, 3,4)
set.seed(123456)
matall=f2(x, 1200, 4)
View(matall)

      [,1] [,2] [,3] [,4]
   [1,]  3.0  3.0  1.5  1.5
   [2,]  1.5  1.0  2.0  1.0
   [3,]  4.0  1.0  3.0  2.0
   [4,]  4.0  4.0  4.0  4.0

Upvotes: 0

Views: 114

Answers (2)

Uwe
Uwe

Reputation: 42544

There is a typo in the function definition. The while clause should read

while ( any(apply(result, 1, function(x) length(unique(x)) == 1)) ) {

instead of

while ( any(apply(result, 1, function(x) length(unique(result)) == 1)) ) {

However, the function does not terminate quickly as it tries to create the whole matrix anew each time a row with identical values is found.

The improved version only replaces rows with identical values

f3 <- function(x, n, m) {
  if ( length(unique(x)) == 1 ) {
    stop('x has only one unique element.', call. = FALSE)
  }
  result <- replicate(m, sample(x, n, replace = TRUE))
  uni_rows <- apply(result, 1, function(x) length(unique(x)) == 1)
  while ( any(uni_rows) ) {
    result[which(uni_rows), ] <- replicate(m, sample(x, sum(uni_rows), replace = TRUE))
    uni_rows <- apply(result, 1, function(x) length(unique(x)) == 1)
  }
  return(result)
}

Now,

x <- c(1, 1.5, 2, 3, 4)
set.seed(123456)
matall <- f3(x, 1200, 4)
any(apply(matall, 1, function(x) length(unique(x)) == 1))
[1] FALSE

returns

head(matall, 11)
      [,1] [,2] [,3] [,4]
 [1,]  3.0    3  1.5  1.5
 [2,]  1.5    1  2.0  1.0
 [3,]  4.0    1  3.0  2.0
 [4,]  2.0    4  3.0  1.0
 [5,]  4.0    1  1.5  3.0
 [6,]  4.0    3  2.0  3.0
 [7,]  2.0    3  4.0  4.0
 [8,]  4.0    1  2.0  4.0
 [9,]  1.5    1  4.0  1.0
[10,]  4.0    4  3.0  3.0
[11,]  1.5    3  4.0  1.5

Upvotes: 1

lukeA
lukeA

Reputation: 54237

I want to make a random matrix with n rows and m columns.

cols <- 3; rows <- 3
m <- matrix(ncol = cols, nrow = rows)

I want the elements of each row of the matrix to be randomly chosen from a set of numbers.

set.seed(2)
set <- seq(ncol(m)-1L)
m[] <- sample(set, length(m), replace = T)
m
#      [,1] [,2] [,3]
# [1,]    1    1    1
# [2,]    2    2    2
# [3,]    2    2    1

I want to have at least one different number in each row.

rowRanges <- matrixStats::rowRanges(m)
(isSingle <- rowRanges[,2]-rowRanges[,1]==0)
# [1]  TRUE  TRUE FALSE

m[isSingle,1] <- vapply(rowRanges[isSingle, 1], function(x) set[set!=x][1], 0L)
m
#      [,1] [,2] [,3]
# [1,]    2    1    1
# [2,]    1    2    2
# [3,]    2    2    1

or, if you want to randomize the assignment in terms of which set value and which column to choose:

vsample <- Vectorize(function(x) sample(set[set!=x], size = 1L), "x")
idx <- cbind(row=which(isSingle), col=sample(ncol(m), sum(isSingle), replace = TRUE))
mvals <- vsample(rowRanges[isSingle, 1])
m[idx] <- mvals

Upvotes: 1

Related Questions