Replacing part of a triangle matrix by a vector - r

I have v<- c(2,4,5) and mat<- matrix(0,n,n).
and i want to replace vector in the upper of the matrix, each place which i identify, the arrays are replacing as length number of vector. for example ,for n=5 the output can be :
[,1] [,2] [,3] [,4] [,5]
[1,] 0 2 4 0 0
[2,] 0 0 5 0 0
[3,] 0 0 0 0 0
[4,] 0 0 0 0 0
[5,] 0 0 0 0 0
I try with this code:
mat <- matrix(0, nrow = 5, ncol = 5)
for (i in 1:5){
for (j in 1:5){
if (i<j & j<= 5){
mat [upper.tri(mat, diag = FALSE)]<- v
}
}
}
but output is:
[,1] [,2] [,3] [,4] [,5]
[1,] 0 2 4 2 2
[2,] 0 0 5 4 4
[3,] 0 0 0 5 5
[4,] 0 0 0 0 2
[5,] 0 0 0 0 0
I have two problems:
I want the vector replaced just one time in matrix and not to repeat.
I don't know where I have to replace i and j in the loop for the starting point of matrix to replace vector.(maybe I want to start the vector from [3,4] in matrix.).for example:
[,1] [,2] [,3] [,4] [,5]
[1,] 0 0 0 0 0
[2,] 0 0 0 0 0
[3,] 0 0 0 2 4
[4,] 0 0 0 0 5
[5,] 0 0 0 0 0

When you fill matrices from vectors, two good things to know:
It fills them in by-column; and
If the vector is shorter than the portion of the matrix to fill, then the vector will be recycled (repeated).
Further, in your code, you use two for loops, nested, with the apparent intent of operating only on those indices within the matrix ... but then you do not reference i and j in the matrix subset or assignment, so every time it tries to replace a single value, it's replacing the entire matrix upper-tri with the entire vector.
Here's a speedier way:
mat <- matrix(0, nrow = 5, ncol = 5)
v <- c(2, 4, 5)
mat[upper.tri(mat)][seq_along(v)] <- v
mat
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 2 4 0 0
# [2,] 0 0 5 0 0
# [3,] 0 0 0 0 0
# [4,] 0 0 0 0 0
# [5,] 0 0 0 0 0
The first indexing on mat is the upper-triangle, as you know. The second subsets to just length(v) of that subset.
If you want to replace it at arbitrary places in the upper-triangle, though, here's a shot:
mat <- matrix(0, nrow = 5, ncol = 5)
ut <- upper.tri(mat)
fullv <- integer(sum(ut)) # the size of the matrix subset
fullv
# [1] 0 0 0 0 0 0 0 0 0 0
fullv[5 + seq_along(v)] <- v
fullv
# [1] 0 0 0 0 0 2 4 5 0 0
mat[ut] <- fullv
mat
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 0 0 0 4
# [2,] 0 0 0 0 5
# [3,] 0 0 0 2 0
# [4,] 0 0 0 0 0
# [5,] 0 0 0 0 0
Here we demonstrate that the filling is being done by column, which is not what you were asking for. To better see how this filling is happening,
mat <- matrix(0, nrow = 5, ncol = 5)
ut <- upper.tri(mat)
mat[ut] <- seq_len(sum(ut))
mat
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 1 2 4 7
# [2,] 0 0 3 5 8
# [3,] 0 0 0 6 9
# [4,] 0 0 0 0 10
# [5,] 0 0 0 0 0
So if you want your v in very specific places and they are not consecutive per this ordering, then you'll need to be specific:
mat <- matrix(0, nrow = 5, ncol = 5)
ut <- upper.tri(mat)
mat[ut][c(6,9,10)] <- v
mat
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 0 0 0 0
# [2,] 0 0 0 0 0
# [3,] 0 0 0 2 4
# [4,] 0 0 0 0 5
# [5,] 0 0 0 0 0

You can figure out the {r,c} indices for each element of a length n vector (you will also need the dimensions of a square triangle required to fit such a vector.
For example, for a length 3 vector starting at {3,4} (assuming column-major as in R), you will need the indices
i r c
1 3,4
2 3,5
3 4,5
4 3,6
5 4,6
6 5,6
...
You can probably see the patterns already. For the rows, there is a common r function which gives the pattern 3 3 4 3 4 5 ... which is sequence specifically sequence(1:3) + 2.
Next the columns have a simpler solution in 4 5 5 6 6 6 ... which is just rep(1:3, 1:3) + 3, so we can just combine these two to get the indices for each element of the vector.
## use 1:(n - 1) for a length n vector
mat <- matrix(0, 5, 5)
v <- c(2, 4, 5)
i <- cbind(sequence(1:2) + (3 - 1), rep(1:2, 1:2) + (4 - 1))
mat[i] <- v
mat
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 0 0 0 0
# [2,] 0 0 0 0 0
# [3,] 0 0 0 2 4
# [4,] 0 0 0 0 5
# [5,] 0 0 0 0 0
You can see your starting positions in the above code, so we can generalize this logic:
f <- function(matrix, vector = 1:3, start = c(1, 1)) {
i <- sequence(rep(1:nrow(matrix), 1:ncol(matrix))[length(vector)])
i <- cbind(
sequence(i) + start[1] - 1,
rep(i, i) + start[2] - 1
)
## recycle vector so there are no incompatible length errors
matrix[i] <- rep_len(vector, nrow(i))
matrix
}
mat <- matrix(0, 5, 5)
f(mat, v, c(3, 4))
f(mat, v, c(2, 2))
f(mat, c(2, 4, 5), c(1, 1))
f(mat, c(v, v), c(1, 1))
f(mat, c(v, v, v), c(1, 2))

Related

How to pad vectors and matrices with 0s

vec_length <- 4
nonzero_ind <- c(1, 3)
zero_ind <- c(2, 4)
Vector:
I currently have a vector called vec that contains 2 elements.
vec <- c(22, -5)
I want to obtain a vector of length vec_length that expands vec by adding 0s. The position of these 0s are specified by zero_ind. That is, I want a vector that looks like:
> expanded_vec
[1] 22 0 -5 0
Matrix
I currently have a 2x2 matrix called mat that corresponds to vec.
> mat
[,1] [,2]
[1,] 1 2
[2,] 2 3
I want to obtain a vec_length by vec_length matrix where there are 0s in the (i,j)-th position if either i OR j is one of the nonzero_ind. That is, I want a matrix that looks like:
> expected_mat
[,1] [,2] [,3] [,4]
[1,] 1 0 2 0
[2,] 0 0 0 0
[3,] 2 0 3 0
[4,] 0 0 0 0
For the vector:
"[<-"(numeric(vec_length), nonzero_ind, vec)
#[1] 22 0 -5 0
"[<-"(numeric(vec_length), seq_len(vec_length)[-zero_ind], vec)
#[1] 22 0 -5 0
x <- numeric(vec_length)
(x[nonzero_ind] <- vec)
#[1] 22 0 -5 0
x <- numeric(vec_length)
(x[-zero_ind] <- vec)
#[1] 22 0 -5 0
and the Matrix:
i <- "[<-"(rep(NA,vec_length), nonzero_ind, seq_along(nonzero_ind))
mat <- mat[i, i]
mat[is.na(mat)] <- 0
mat
# [,1] [,2] [,3] [,4]
#[1,] 1 0 2 0
#[2,] 0 0 0 0
#[3,] 2 0 3 0
#[4,] 0 0 0 0
or
mat_out <- matrix(0, vec_length, vec_length)
mat_out[nonzero_ind, nonzero_ind] <- mat
mat_out
# [,1] [,2] [,3] [,4]
#[1,] 1 0 2 0
#[2,] 0 0 0 0
#[3,] 2 0 3 0
#[4,] 0 0 0 0
Maybe you can try kronecker like below
> kronecker(vec,c(1,0))
[1] 22 0 -5 0
> kronecker(mat,matrix(c(1,0,0,0),nrow(mat)))
[,1] [,2] [,3] [,4]
[1,] 1 0 2 0
[2,] 0 0 0 0
[3,] 2 0 3 0
[4,] 0 0 0 0
One example:
# Input data
vec_inp <- c(22, -5)
mat_inp <- matrix(c(1, 2, 2, 3), ncol = 2)
# Vector case
update_vec <- function(inp, len, nzi) {
replace(x = vector(mode = 'double', length = len), list = nzi, values = inp)
}
update_vec(vec_inp, vec_length, nonzero_ind)
# [1] 22 0 -5 0
# Matrix case, work column by column
mat_out <- matrix(vector(mode = 'double', length = vec_length^2), ncol = vec_length)
for (i in seq_along(nonzero_ind)) {
mat_out[, nonzero_ind[i]] <- update_vec(mat_inp[, i], vec_length, nonzero_ind)
}
mat_out
# [,1] [,2] [,3] [,4]
# [1,] 1 0 2 0
# [2,] 0 0 0 0
# [3,] 2 0 3 0
# [4,] 0 0 0 0

Obtain all possible matrices by swapping only two positions in any given column

Let's start with the following matrix.
M <- matrix(c(0,0,1,1,0,0,1,1,
0,1,1,0,0,1,1,0,
0,0,0,0,1,1,1,1,
0,1,0,1,1,0,1,0,
0,0,1,1,1,1,0,0,
0,1,1,0,1,0,0,1),nrow = 8,ncol = 6)
Here is the M
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 0 0 0 0 0 0
[2,] 0 1 0 1 0 1
[3,] 1 1 0 0 1 1
[4,] 1 0 0 1 1 0
[5,] 0 0 1 1 1 1
[6,] 0 1 1 0 1 0
[7,] 1 1 1 1 0 0
[8,] 1 0 1 0 0 1
If I pick a random column, say 4, I want to swap two positions in that column. One such possibility is swapping 5th and 6th position is given by
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 0 0 0 0 0 0
[2,] 0 1 0 1 0 1
[3,] 1 1 0 0 1 1
[4,] 1 0 0 1 1 0
[5,] 0 0 1 0 1 1
[6,] 0 1 1 1 1 0
[7,] 1 1 1 1 0 0
[8,] 1 0 1 0 0 1
I want to do this for every possible swap in each column and then for all columns to obtain all the possible matrices.
Here's another solution:
# Return all unique permutations for c(0,0,0,0,1,1,1,1)
library(gtools)
perms = unique(permutations(8, 8, M[,1], set = FALSE))
# Create nested list
Mat_list = lapply(vector("list", ncol(M)), function(x) vector("list", nrow(perms)))
# Loop through every column and every permutations replacing each column
# with each unique permutation one at a time
for(ii in 1:ncol(M)){
for(jj in 1:nrow(perms)){
New_Mat = M
New_Mat[,ii] = perms[jj,]
Mat_list[[ii]][[jj]] = New_Mat
}
}
Result:
> Mat_list[[1]][[2]]
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 0 0 0 0 0 0
[2,] 0 1 0 1 0 1
[3,] 1 1 0 0 1 1
[4,] 1 0 0 1 1 0
[5,] 0 0 1 1 1 1
[6,] 1 1 1 0 1 0
[7,] 0 1 1 1 0 0
[8,] 1 0 1 0 0 1
Note:
Instead of creating a super long list, I've created a nested list of matrices with 8 elements and n sub-elements per element (where n is the number of unique permutations). You can unlist the result if you prefer the long list form.
This code gives every permutation of 0s and 1s by column. I used a smaller toy example here, because the number of possibilities can get very large -- prod(choose(nrow(M), colSums(M))). As a note, this will likely not run on a standard computer for the matrix given, because of memory requirements.
library(gtools)
set.seed(1234)
M <- matrix(sample(0:1, 16, replace = TRUE), ncol = 4)
M
# [,1] [,2] [,3] [,4]
# [1,] 0 1 1 0
# [2,] 1 1 1 1
# [3,] 1 0 1 0
# [4,] 1 0 1 1
perm1s <- function(n, N) {
unique(permutations(N, N, c(rep(0, N - n), rep(1, n)), FALSE, FALSE))
}
createMat <- function(vec, lst) {
tmp <- lapply(seq_along(vec), function(x) lst[[x]][vec[x], ])
do.call(cbind, tmp)
}
makeMats <- function(M) {
sums <- colSums(M)
rows <- nrow(M)
rowPerm <- lapply(sums, perm1s, N = rows)
comb <- expand.grid(lapply(sapply(rowPerm, nrow), seq))
comb <- lapply(split(comb, seq(nrow(comb))), unlist)
mats <- lapply(comb, createMat, lst = rowPerm)
mats
}
res <- makeMats(M)
res[[1]]
# [,1] [,2] [,3] [,4]
# [1,] 0 0 1 0
# [2,] 1 0 1 0
# [3,] 1 1 1 1
# [4,] 1 1 1 1
To hold other columns constant when varying 1 column -- sum(choose(nrow(M), colSums(M))) possibilities:
makeMats2 <- function(M) {
sums <- colSums(M)
rows <- nrow(M)
rowPerm <- lapply(sums, perm1s, N = rows)
ind <- rep(seq_along(rowPerm), sapply(rowPerm, nrow))
rowPerm <- lapply(rowPerm, function(x) split(x, seq(nrow(x))))
rowPerm <- unlist(rowPerm, recursive = FALSE)
mats <- rep(list(M), length(rowPerm))
mats <- mapply(function(x, y, z) {x[ , y] <- z; x},
x = mats, y = ind, z = rowPerm, SIMPLIFY = FALSE)
mats
}

How get a binary matrix according a condition to each its row

I have a vector of integers.And I need to construct a binary matrix which depend on this vectors.In fact,I want to get for each line of matrix zeros just the column which its index equal
to the w value.
This is an example :
w=c(2,3,5,4)
nr=length(w)
M=matrix(1*nr*length(w),nrow=nr,ncol=max(w))
the result should be like that for this example:
01000
00100
00001
00010
so I run this code but i didn't get the result shown belown:
for ( i in 1:nr)
{
for(j in 1:max(w))
{ if(w[j]==j)
M[i,j]=1
else
M[i,j]=0
}
}
thanks for the help!
Try this:
`[<-`(matrix(0,nrow=length(w),ncol=max(w)),cbind(seq_along(w),w),1)
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0 1 0 0 0
#[2,] 0 0 1 0 0
#[3,] 0 0 0 0 1
#[4,] 0 0 0 1 0
We can create a matrix with all 0 and then replace those cells to be 1.
w <- c(2, 3, 5, 4)
nr <- length(w)
M <- matrix(0, nrow = nr, ncol = max(w))
for (i in 1:nr){
M[i, w[i]] <- 1
}
M
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 0 0 0
[2,] 0 0 1 0 0
[3,] 0 0 0 0 1
[4,] 0 0 0 1 0

Index a matrix based on another matrix's values

I have a matrix (really, a 3-D array) that I would like to "threshold" based on the values in another matrix (which is completely binarized). So, e.g.
set.seed(1234)
M <- matrix(1:9, nrow=3, byrow=F)
M
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
set.seed(1234)
N <- matrix(sample(c(0, 0, 1), 9, replace=T), nrow=3)
N
[,1] [,2] [,3]
[1,] 0 0 0
[2,] 0 1 0
[3,] 0 0 0
I would like to keep only the values in M that are in the same location as where N equals 1; turn the rest into 0's. Output should be:
M.thresh
[,1] [,2] [,3]
[1,] 0 0 0
[2,] 0 5 0
[3,] 0 0 0
Just replace with matrix indexing.
N[N == 1] <- M[N == 1]
N
# [,1] [,2] [,3]
# [1,] 0 0 0
# [2,] 0 5 0
# [3,] 0 0 0
Or replace(N, N == 1, M[N == 1])

Sampling and replacing random elements of a vector, conditionally

Suppose I have a vector containing data:
c <- c(1:100)
c[1:75] <- 0
c[76:100] <- 1
What I need to do is select a number of the 0's and turn them into 1's. There are potentially many ways to do this - like if I'm switching 25 of the 0's, it'd be 75 choose 25, so 5.26x10^19 - so I need do it, say, 1000 times randomly. (this is part of a larger model. I'll be using the mean of the results.)
I know (think), that I need to use sample() and a for loop - but how do I select n values randomly among the 0's, then change them to 1's?
vec <- c(rep(0, 75), rep(1, 25))
n <- 25
to_change <- sample(which(vec == 0), n)
modified_vec <- vec
modified_vec[to_change] <- 1
Something like this. You could wrap it up in a function.
And you should really do it in a matrix with apply, rather than a for loop.
This small example is easy to see it work:
n_vecs <- 5
vec_length <- 10
n_0 <- 7 # Number of 0's at the start of each vector
vec_mat <- matrix(c(rep(0, n_vecs * n_0), rep(1, n_vecs * (vec_length - n_0))),
nrow = vec_length, ncol = n_vecs, byrow = T)
> vec_mat
[,1] [,2] [,3] [,4] [,5]
[1,] 0 0 0 0 0
[2,] 0 0 0 0 0
[3,] 0 0 0 0 0
[4,] 0 0 0 0 0
[5,] 0 0 0 0 0
[6,] 0 0 0 0 0
[7,] 0 0 0 0 0
[8,] 1 1 1 1 1
[9,] 1 1 1 1 1
[10,] 1 1 1 1 1
change_n_0 <- function(x, n) {
x_change <- sample(which(x == 0), n)
x[x_change] <- 1
return(x)
}
vec_mat <- apply(vec_mat, MARGIN = 2, FUN = change_n_0, n = 2)
> vec_mat
[,1] [,2] [,3] [,4] [,5]
[1,] 1 1 0 0 1
[2,] 0 0 0 1 0
[3,] 0 0 0 0 0
[4,] 0 0 0 0 0
[5,] 0 0 1 0 1
[6,] 0 1 0 1 0
[7,] 1 0 1 0 0
[8,] 1 1 1 1 1
[9,] 1 1 1 1 1
[10,] 1 1 1 1 1
You can scale up the constants at the beginning as big as you'd like.

Resources