Sampling with replacement using matrices?

I am performing a simple multiplication of rows vs. columns of a 3x3 matrix, like so:

Instead of one, I have 3 of these matrices, let's call them red/green/blue_mat :

I could make these matrices and perform the multiplication like so:

colors <- c("red", "green", "blue")

for(i in 1:length(colors)){
  #Initialize three 3x3 matrices with desired row/colnames
  mat <- matrix(NA, nrow=3, ncol=3)
  
  rowvals <- c(9,4,0)
  colvals <- c(1,3,7)
  
  rownames(mat) <- rowvals + i
  colnames(mat) <- colvals + i

  #Multiply rows vs cols
  mat <- as.numeric(rownames(mat)) %*% t(as.numeric(colnames(mat)))
  
  #name them red/green/blue_mat
  assign(paste0(colors[i], "_mat"), mat) 
  
  #Remove 'mat'
  rm(mat) 
}
red_mat
     [,1] [,2] [,3]
[1,]   20   40   80
[2,]   10   20   40
[3,]    2    4    8

Rather than do this for the 3 matrices as they are, however, I want to sample with replacement to generate more than 3 matrices and then do the multiplication. Here's what that would look like for just one square (and without replacement I think):

How can I do this but for all squares? and with replacement for n resulting matrices?

Thanks for anyone who can help with this!

(Sort of related to another previous post that @nirgrahamuk helped solve previously)

First, a terminology aspect: when you write "sample", I expect a random sampling (which is the case for bootstrap), but in your example you seem to want all combinations (e.g. for a permutation test).

Assuming the latter, I don't think you actually need to compute the matrices "red", "green" and "blue", you only need their rownames and colnames (which you can store as vectors). Then you can get all permutations using expand.grid(), and finally for each combination you can get the corresponding permuted matrix. So this code should do what you want:

colors <- c("red", "green", "blue")

rows <- lapply(1:3,
               \(i) c(9,4,0) + i) |>
  setNames(colors)


cols <- lapply(1:3,
               \(i) c(1,3,7) + i) |>
  setNames(colors)

rows
#> $red
#> [1] 10  5  1
#> 
#> $green
#> [1] 11  6  2
#> 
#> $blue
#> [1] 12  7  3
cols
#> $red
#> [1] 2 4 8
#> 
#> $green
#> [1] 3 5 9
#> 
#> $blue
#> [1]  4  6 10

all_combinations <- expand.grid(rows = rows, cols = cols)

all_combinations
#>       rows     cols
#> 1 10, 5, 1  2, 4, 8
#> 2 11, 6, 2  2, 4, 8
#> 3 12, 7, 3  2, 4, 8
#> 4 10, 5, 1  3, 5, 9
#> 5 11, 6, 2  3, 5, 9
#> 6 12, 7, 3  3, 5, 9
#> 7 10, 5, 1 4, 6, 10
#> 8 11, 6, 2 4, 6, 10
#> 9 12, 7, 3 4, 6, 10


mats <- list()
for(i in seq_len(nrow(all_combinations))){
  mats[[i]] <- all_combinations$rows[[i]] %*% t(all_combinations$cols[[i]])
}
mats
#> [[1]]
#>      [,1] [,2] [,3]
#> [1,]   20   40   80
#> [2,]   10   20   40
#> [3,]    2    4    8
#> 
#> [[2]]
#>      [,1] [,2] [,3]
#> [1,]   22   44   88
#> [2,]   12   24   48
#> [3,]    4    8   16
#> 
#> [[3]]
#>      [,1] [,2] [,3]
#> [1,]   24   48   96
#> [2,]   14   28   56
#> [3,]    6   12   24
#> 
#> [[4]]
#>      [,1] [,2] [,3]
#> [1,]   30   50   90
#> [2,]   15   25   45
#> [3,]    3    5    9
#> 
#> [[5]]
#>      [,1] [,2] [,3]
#> [1,]   33   55   99
#> [2,]   18   30   54
#> [3,]    6   10   18
#> 
#> [[6]]
#>      [,1] [,2] [,3]
#> [1,]   36   60  108
#> [2,]   21   35   63
#> [3,]    9   15   27
#> 
#> [[7]]
#>      [,1] [,2] [,3]
#> [1,]   40   60  100
#> [2,]   20   30   50
#> [3,]    4    6   10
#> 
#> [[8]]
#>      [,1] [,2] [,3]
#> [1,]   44   66  110
#> [2,]   24   36   60
#> [3,]    8   12   20
#> 
#> [[9]]
#>      [,1] [,2] [,3]
#> [1,]   48   72  120
#> [2,]   28   42   70
#> [3,]   12   18   30

Created on 2022-12-04 by the reprex package (v2.0.1)

If what you want is indeed a (random) sampling with replacement, i.e. the same values can appear an arbitrary number of times, I still see two possibilities. If you sample a colname or rowname all at once, this is obtained simply by replacing the call to expand.grid() with calls to sample() with replace = TRUE.

Note that here the number of samples is a parameter you decide about, it's not determined by the size of your matrices. For bootstrap, it's common to have big numbers (e.g. 1000).

set.seed(123)
colors <- c("red", "green", "blue")

rows <- lapply(1:3,
               \(i) c(9,4,0) + i) |>
  setNames(colors)


cols <- lapply(1:3,
               \(i) c(1,3,7) + i) |>
  setNames(colors)

rows
#> $red
#> [1] 10  5  1
#> 
#> $green
#> [1] 11  6  2
#> 
#> $blue
#> [1] 12  7  3
cols
#> $red
#> [1] 2 4 8
#> 
#> $green
#> [1] 3 5 9
#> 
#> $blue
#> [1]  4  6 10

nb_sampled <- 5
sampled_rows <- sample(rows, size = nb_sampled, replace = TRUE)
sampled_cols <- sample(cols, size = nb_sampled, replace = TRUE)

sampled_rows
#> $blue
#> [1] 12  7  3
#> 
#> $blue
#> [1] 12  7  3
#> 
#> $blue
#> [1] 12  7  3
#> 
#> $green
#> [1] 11  6  2
#> 
#> $blue
#> [1] 12  7  3
sampled_cols
#> $green
#> [1] 3 5 9
#> 
#> $green
#> [1] 3 5 9
#> 
#> $green
#> [1] 3 5 9
#> 
#> $blue
#> [1]  4  6 10
#> 
#> $red
#> [1] 2 4 8

mats <- list()
for(i in seq_along(sampled_rows)){
  mats[[i]] <- sampled_rows[[i]] %*% t(sampled_rows[[i]])
}
mats
#> [[1]]
#>      [,1] [,2] [,3]
#> [1,]  144   84   36
#> [2,]   84   49   21
#> [3,]   36   21    9
#> 
#> [[2]]
#>      [,1] [,2] [,3]
#> [1,]  144   84   36
#> [2,]   84   49   21
#> [3,]   36   21    9
#> 
#> [[3]]
#>      [,1] [,2] [,3]
#> [1,]  144   84   36
#> [2,]   84   49   21
#> [3,]   36   21    9
#> 
#> [[4]]
#>      [,1] [,2] [,3]
#> [1,]  121   66   22
#> [2,]   66   36   12
#> [3,]   22   12    4
#> 
#> [[5]]
#>      [,1] [,2] [,3]
#> [1,]  144   84   36
#> [2,]   84   49   21
#> [3,]   36   21    9

Created on 2022-12-04 by the reprex package (v2.0.1)

Finally, there is another case, which is if you don't want to sample colnames as blocks, but instead to take each element randomly from the set of all possible colnames. I don't think this is what you want based on your description.

1 Like

Thank you !

This second scenario is exactly what I was looking for; the number of random samples is specifed explicitly but (in this case) there will only ever be 9 options to choose from.

For some reason the first part of your code to generate the rows/cols doesn't work for me, returning:

Error: unexpected input in:
"rows <- lapply(1:3,
"
setNames(colors)
Error in setNames(colors) : argument "nm" is missing, with no default

So I just did that part manually:

rows <- list("red"=c(10,5,1),
             "green"=c(11,6,2),
             "blue"=c(12,7,3))

cols <- list("red"=c(2,4,8),
             "green"=c(3,5,9),
             "blue"=c(4,6,10))

Then assumed you meant mats[[i]] <- sampled_rows[[i]] %*% t(sampled_cols[[i]]) (instead of sampled_rows listed twice)...

..and it worked!

The order of combinations pairs is preserved, thanks a ton for this!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.