Set values along a diagonal in a matrix - r

I am trying to use the matrix() and diag() functions to create the following pattern, but with a 100 x 100 matrix rather than 5 x 5.
5 x 5 matrix:
| 0 1 0 0 0 |
| 1 0 1 0 0 |
| 0 1 0 1 0 |
| 0 0 1 0 1 |
| 0 0 0 1 0 |
In other words, I want to have two diagonals with values of 1, one to the left of the main diagonal, and one to the right of the main diagonal.

The diag() function (actually the diag<- function) can be used for assignment:
mat <- matrix( 0, 100,100)
diag(mat) <- 1
mat[1:10,1:10]
#-----------
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 1 0 0 0 0 0 0 0 0 0
[2,] 0 1 0 0 0 0 0 0 0 0
[3,] 0 0 1 0 0 0 0 0 0 0
[4,] 0 0 0 1 0 0 0 0 0 0
[5,] 0 0 0 0 1 0 0 0 0 0
[6,] 0 0 0 0 0 1 0 0 0 0
[7,] 0 0 0 0 0 0 1 0 0 0
[8,] 0 0 0 0 0 0 0 1 0 0
[9,] 0 0 0 0 0 0 0 0 1 0
[10,] 0 0 0 0 0 0 0 0 0 1
You, however, want the sub-diagonal and super-diagonal to be assigned values, so use logical expressions with col and row:
mat <- matrix( 0, 100,100)
mat[row(mat)==col(mat)-1] <- 1
mat[row(mat)==col(mat)+1] <- 1
mat[1:10,1:10]
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 1 0 0 0 0 0 0 0 0
[2,] 1 0 1 0 0 0 0 0 0 0
[3,] 0 1 0 1 0 0 0 0 0 0
[4,] 0 0 1 0 1 0 0 0 0 0
[5,] 0 0 0 1 0 1 0 0 0 0
[6,] 0 0 0 0 1 0 1 0 0 0
[7,] 0 0 0 0 0 1 0 1 0 0
[8,] 0 0 0 0 0 0 1 0 1 0
[9,] 0 0 0 0 0 0 0 1 0 1
[10,] 0 0 0 0 0 0 0 0 1 0
(This method does not depend on having a square matrix. I have a vague memory that there is a faster method that does not require using row and col. For very large objects each of those functions returns a matrix of the same dimensions as their arguments.)

For the main diagonal, the row and column indices are the same. For the other diagonals, there is a difference of 1 between the row index and column index. Generate those indices directly and assign values in those indices.
sz = 5
m = matrix(0, sz, sz)
inds1 = cbind(r = 1:(sz-1), c = 2:sz)
inds2 = cbind(r = 2:sz, c = 1:(sz-1))
m[inds1] = 1
m[inds2] = 1
m
# OR, to make it concise
m = matrix(0, sz, sz)
inds = rbind(cbind(1:(sz-1), 2:sz), cbind(2:sz, 1:(sz-1)))
replace(m, inds, 1)
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0 1 0 0 0
#[2,] 1 0 1 0 0
#[3,] 0 1 0 1 0
#[4,] 0 0 1 0 1
#[5,] 0 0 0 1 0

We could create a function using a math trick which would work for all square matrix.
get_off_diagonal_1s <- function(n) {
#Create a matrix with all 0's
mat <- matrix(0, ncol = n, nrow = n)
#Subtract row indices by column indices
inds = row(mat) - col(mat)
#Replace values where inds is 1 or -1
mat[inds == 1 | inds == -1] = 1
mat
}
get_off_diagonal_1s(5)
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0 1 0 0 0
#[2,] 1 0 1 0 0
#[3,] 0 1 0 1 0
#[4,] 0 0 1 0 1
#[5,] 0 0 0 1 0
get_off_diagonal_1s(8)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
#[1,] 0 1 0 0 0 0 0 0
#[2,] 1 0 1 0 0 0 0 0
#[3,] 0 1 0 1 0 0 0 0
#[4,] 0 0 1 0 1 0 0 0
#[5,] 0 0 0 1 0 1 0 0
#[6,] 0 0 0 0 1 0 1 0
#[7,] 0 0 0 0 0 1 0 1
#[8,] 0 0 0 0 0 0 1 0

Related

How to convert matrix elements from 0|1 to 1|0 in R?

I have a graph that converted to matrix.
g = sample_k_regular(10,3)
m =get.adjacency(g)
I want to select randomly some elements and convert to 0|1.(if it is 0 to become 1 and if it is 1 to become 0).
How to do this work?
You can make sample of n elements (10 in example) and change it
m1=as.matrix(m)
m1
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 1 1 0 0 0 0 1 0
[2,] 0 0 0 0 1 0 0 0 1 1
[3,] 1 0 0 0 0 0 0 1 0 1
[4,] 1 0 0 0 1 0 1 0 0 0
[5,] 0 1 0 1 0 1 0 0 0 0
[6,] 0 0 0 0 1 0 1 0 0 1
[7,] 0 0 0 1 0 1 0 1 0 0
[8,] 0 0 1 0 0 0 1 0 1 0
[9,] 1 1 0 0 0 0 0 1 0 0
[10,] 0 1 1 0 0 1 0 0 0 0
set.seed(1)
ss=sample(length(m1),size = 10)
m1[ss]=1-m1[ss]
m1
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 1 1 0 0 0 0 1 0
[2,] 0 0 0 0 1 0 1 0 1 1
[3,] 1 0 0 0 0 0 0 1 0 1
[4,] 1 0 0 0 1 0 1 0 0 0
[5,] 0 1 0 1 0 1 0 0 0 0
[6,] 1 0 0 0 1 0 1 0 1 1
[7,] 0 0 1 0 0 0 0 1 0 1
[8,] 0 0 1 0 0 1 1 0 1 0
[9,] 1 1 0 0 0 0 0 1 1 0
[10,] 0 0 1 0 0 1 0 0 0 0
For exclude diagonal as #ZheyuanLi told
you can calculate diad position and exlude it from data for sample :
m1=as.matrix(m)
m1
set.seed(1)
m_l=1:length(m1)
m_l=m_l[-which(diag(1,nrow = nrow(m1))==1)]
ss=sample(m_l,size = 10)
m1[ss]=1-m1[ss]
m1
For big matrix beter use seq.int than diag
n=1000
Unit: microseconds
expr min lq mean median uq max neval
{ which(diag(1, nrow = n) == 1) } 8976.718 9422.967 14397.44991 10489.0520 16001.550 190959.200 100
{ seq(1, by = n + 1, length = n) } 12.941 17.404 37.90449 31.9075 56.004 83.448 100
{ seq.int(1, by = n + 1, length = n) } 5.355 6.248 8.90736 7.1405 12.272 16.512 100
{ 1 + { (1:n) - 1 } * (1 + n) } 5.355 6.248 9.77758 8.9255 11.826 25.437 100

R programming: How to do replace values by row?

How to do a row-wise replacement of values using R?
I have a Matrix and I would like to replace some of its values using an index vector. The problem is that R automatically does a column-wise extraction of the values as opposed to a row-wise.
You will find my code and results below:
Matrix=matrix(rep(0,42),nrow=6,ncol=7,byrow=TRUE)
v=c(1,7,11,16,18)
Matrix[v]=1
Matrix
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 1 1 0 0 0 0 0
[2,] 0 0 0 0 0 0 0
[3,] 0 0 0 0 0 0 0
[4,] 0 0 1 0 0 0 0
[5,] 0 1 0 0 0 0 0
[6,] 0 0 1 0 0 0 0
What I actually want to get is the row-wise version of this meaning:
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 1 0 0 0 0 0 1
[2,] 0 0 0 1 0 0 0
[3,] 0 1 0 1 0 0 0
[4,] 0 0 0 0 0 0 0
[5,] 0 0 0 0 0 0 0
[6,] 0 0 0 0 0 0 0
>
Apparently R does a column-wise replacement of values by default.
What is the best way to obtain a row-wise replacement of the values?
Thanks!
You could recalculate the onedimensional indizes to row- and column-indices. Supposing you have calculated the row-indices in the first column of the matrix Ind and the columnindices in the second column of Ind you can do Matrix[Ind] <- 1
Matrix <- matrix(rep(0,42),nrow=6,ncol=7,byrow=TRUE)
v <- c(1,7,11,16,18)
Row <- (v-1) %/% ncol(Matrix) +1
Col <- (v-1) %% ncol(Matrix) +1
Matrix[cbind(Row,Col)] <- 1
Matrix
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] 1 0 0 0 0 0 1
# [2,] 0 0 0 1 0 0 0
# [3,] 0 1 0 1 0 0 0
# [4,] 0 0 0 0 0 0 0
# [5,] 0 0 0 0 0 0 0
# [6,] 0 0 0 0 0 0 0
We can do
+(matrix(seq_along(Matrix) %in% v, ncol=ncol(Matrix), nrow=nrow(Matrix), byrow=TRUE))
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
#[1,] 1 0 0 0 0 0 1
#[2,] 0 0 0 1 0 0 0
#[3,] 0 1 0 1 0 0 0
#[4,] 0 0 0 0 0 0 0
#[5,] 0 0 0 0 0 0 0
#[6,] 0 0 0 0 0 0 0
You could redo your 1's to make them row-wise or you can do the following:
Matrix=matrix(rep(0,42),nrow=6,ncol=7,byrow=TRUE)
v=c(1,7,11,16,18)
Matrix<-t(Matrix)
Matrix[v]=1
Matrix<-t(Matrix)
Matrix
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 1 0 0 0 0 0 1
[2,] 0 0 0 1 0 0 0
[3,] 0 1 0 1 0 0 0
[4,] 0 0 0 0 0 0 0
[5,] 0 0 0 0 0 0 0
[6,] 0 0 0 0 0 0 0

R : Updating a matrix given a set of indices

I have a matrix(initialized to zeros) and a set of indices. If the i'th value in indices is j, then I want to set the (j,i)th entry of the matrix to 1.
For eg:
> m = matrix(0, 10, 7)
> indices
[1] 2 9 3 4 5 1 10
And the result should be
> result
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 0 0 0 0 0 1 0
[2,] 1 0 0 0 0 0 0
[3,] 0 0 1 0 0 0 0
[4,] 0 0 0 1 0 0 0
[5,] 0 0 0 0 1 0 0
[6,] 0 0 0 0 0 0 0
[7,] 0 0 0 0 0 0 0
[8,] 0 0 0 0 0 0 0
[9,] 0 1 0 0 0 0 0
[10,] 0 0 0 0 0 0 1
I asked a somewhat related question a little while back, which used a vector instead of a matrix. Is there a similar simple solution to this problem?
## OP's example data
m = matrix(0, 10, 7)
j <- c(2, 9, 3, 4, 5, 1, 10)
## Construct a two column matrix of indices (1st column w. rows & 2nd w. columns)
ij <- cbind(j, seq_along(j))
## Use it to subassign into the matrix
m[ij] <- 1
m
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] 0 0 0 0 0 1 0
# [2,] 1 0 0 0 0 0 0
# [3,] 0 0 1 0 0 0 0
# [4,] 0 0 0 1 0 0 0
# [5,] 0 0 0 0 1 0 0
# [6,] 0 0 0 0 0 0 0
# [7,] 0 0 0 0 0 0 0
# [8,] 0 0 0 0 0 0 0
# [9,] 0 1 0 0 0 0 0
# [10,] 0 0 0 0 0 0 1
For the record, the answer in your linked question can easily be adapted to suit this scenario too by using sapply:
indices <- c(2, 9, 3, 4, 5, 1, 10)
sapply(indices, tabulate, nbins = 10)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] 0 0 0 0 0 1 0
# [2,] 1 0 0 0 0 0 0
# [3,] 0 0 1 0 0 0 0
# [4,] 0 0 0 1 0 0 0
# [5,] 0 0 0 0 1 0 0
# [6,] 0 0 0 0 0 0 0
# [7,] 0 0 0 0 0 0 0
# [8,] 0 0 0 0 0 0 0
# [9,] 0 1 0 0 0 0 0
# [10,] 0 0 0 0 0 0 1
For small datasets you might not notice the performance difference, but Josh's answer, which uses matrix indexing, would definitely be much faster, even if you changed my answer here to use vapply instead of sapply.

Find a path or route in R

So here is a matrix A which shows if point 1 to 10 is connected with each other. 1 means they are connected and 0 means they are not. I would like to find out if there is a path from one point to the other. Let's say the start point is 1 and the end point is 3. The number of points involved in between doesn't matter. Points can be used repeatedly. I just want to know if 1 can reach 3. How can I do this?
From what we can see, one of the possible paths is 1-8-6-2-3. But how to do it with R? Thanks a lot. Any help is appreciated.
A
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 0 0 0 0 0 1 0 0
[2,] 0 0 1 0 1 1 0 0 0 0
[3,] 0 1 0 0 0 0 0 0 0 0
[4,] 0 0 0 0 0 0 0 1 0 0
[5,] 0 1 0 0 0 0 0 0 0 0
[6,] 0 1 0 0 0 0 1 1 0 0
[7,] 0 0 0 0 0 1 0 0 0 1
[8,] 1 0 0 1 0 1 0 0 0 0
[9,] 0 0 0 0 0 0 0 0 0 0
[10,] 0 0 0 0 0 0 1 0 0 0
For this task I think that igraph will make your life easier
require(igraph)
dat <- read.table(text =
'0 0 0 0 0 0 0 1 0 0
0 0 1 0 1 1 0 0 0 0
0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0
0 1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 1 1 0 0
0 0 0 0 0 1 0 0 0 1
1 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0', header = FALSE)
dat <- as.matrix(dat)
g <- graph.adjacency(dat, mode = "undirected")
get.shortest.paths(g, 1, 3)
## [[1]]
## [1] 1 8 6 2 3
If you just want to test if a path exist you can create your own function like this one
test_paths <- function(g, from, to, ...) {
is.finite(c(shortest.paths(g, from, to, ...)))
}
test_paths(g, 1, 9)
## [1] FALSE
test_paths(g, 1, 8)
## [1] TRUE
The idea behind this code is simple : shortest.path return Inf when there's no path between two node (and the path length when it exists) so we can just test whether the number returned is finite (is.finite).
You can do this by repetitive matrix multiplication, until the matrix stays the same:
# generate symetric matrix
set.seed(123)
m <- matrix(rbinom(100, 1, 0.2), nrow = 10)
m <- m * upper.tri(m)
m <- m + t(m)
m0 <- m
m0
Generated matrix:
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 1 1 1 0 0 0 0 0 0
[2,] 1 0 0 1 0 0 0 0 0 0
[3,] 1 0 0 0 0 0 0 0 0 0
[4,] 1 1 0 0 0 0 0 0 0 0
[5,] 0 0 0 0 0 0 1 0 0 0
[6,] 0 0 0 0 0 0 0 0 0 0
[7,] 0 0 0 0 1 0 0 0 1 0
[8,] 0 0 0 0 0 0 0 0 1 0
[9,] 0 0 0 0 0 0 1 1 0 0
[10,] 0 0 0 0 0 0 0 0 0 0
Now multiply, until its stabilized:
m <- m0
while (TRUE) {
new_m <- sign(m + m %*% m)
if (all(new_m == m))
break;
m <- new_m
}
m
The resultant matrix contains 1 if there is a path between those nodes:
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 1 1 1 1 0 0 0 0 0 0
[2,] 1 1 1 1 0 0 0 0 0 0
[3,] 1 1 1 1 0 0 0 0 0 0
[4,] 1 1 1 1 0 0 0 0 0 0
[5,] 0 0 0 0 1 0 1 1 1 0
[6,] 0 0 0 0 0 0 0 0 0 0
[7,] 0 0 0 0 1 0 1 1 1 0
[8,] 0 0 0 0 1 0 1 1 1 0
[9,] 0 0 0 0 1 0 1 1 1 0
[10,] 0 0 0 0 0 0 0 0 0 0

add a value randomly to a matrix

How can I randomly add a value to a matrix?
say I have:
mat <- matrix(0, 10, 10)
v = 5
how can I add randomly v to mat, 2 positions at a time? The output should look like this after a single iteration:
out
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 0 0 0 0 0 0 0 0
[2,] 5 0 0 0 0 0 0 0 0 0
[3,] 0 0 0 0 0 0 0 0 0 0
[4,] 0 0 0 0 5 0 0 0 0 0
[5,] 0 0 0 0 0 0 0 0 0 0
[6,] 0 0 0 0 0 0 0 0 0 0
[7,] 0 0 0 0 0 0 0 0 0 0
[8,] 0 0 0 0 0 0 0 0 0 0
[9,] 0 0 0 0 0 0 0 0 0 0
[10,] 0 0 0 0 0 0 0 0 0 0
After another iteration, mat should have 2 more positions filled with the value in 'v'
You could use ?sample to randomly index your matrix:
idx <- sample(length(mat), size=2)
mat[idx] <- mat[idx] + v

Resources