I am suppose to change a square matrix which represents a graph (the vertices-and-edges kind) and change it into a list that represents the same graph
square matrix: element (i,j) = 1 means there is an edge i -> j
list: element i is a vector (possibly empty, coded as NA) of all j s.t. there is an edge i -> j
My problem is that if there is a zero in the middle of the row it returns a NA and it is only suppose to do that when a vector is empty(no edges). It only does it when a zero is between two 1's. I don't know why and NA.omit doesn't work.
This is my first time programming in R.
squaretolist <- function(m){
ml <- list() #creates an empty list that we will return at the end
for(i in 1:ncol(m)){ #loop through columns
b1 <- c()
for(j in 1:nrow(m)){ #loop through rows
ifelse(m[i,j] %in% 1, b1[j] <- j, next)
}
ifelse(length(b1) == 0, ml[[i]]<- NA, ml[[i]] <- b1 )
}
return(ml)
}
In your function, if you have a zero in between two 1s, for example 1 in the 1st position and in the 3rd position, you're assigning b1[1] to 1, b1[3] to 3 but, as you have a 0 in the 2nd position, you're not assigning b1[2] to anything so it becomes NA.
To avoid that, you can replace ifelse(m[i,j] %in% 1, b1[j] <- j, next)
by ifelse(m[i,j] %in% 1, b1 <- c(b1,j), next).
You can also get what you want with the use of grep and apply functions :
ml <- apply(m, 1, function(i) {if(any(i==1)) grep(1, i) else NA})
This instruction tells R to apply, for each row of the matrix m, a function that returns, if there is at least one 1, the position of the 1(s), else NA.
Example:
set.seed(123)
m<-matrix(sample(c(0,1),25,replace=T),nrow=5)
m[4,]<-rep(0,5)
# > m
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 0 1 1 1
# [2,] 1 1 0 0 1
# [3,] 0 1 1 0 1
# [4,] 0 0 0 0 0
# [5,] 1 0 0 1 1
ml<-apply(m,1,function(i){if(any(i==1)) grep(1,i) else NA})
# > ml
# [[1]]
# [1] 3 4 5
# [[2]]
# [1] 1 2 5
# [[3]]
# [1] 2 3 5
# [[4]]
# [1] NA
# [[5]]
# [1] 1 4 5
Related
I converted an image into a 100x100 matrix of 0's and 1's.
An ntile is nXn selection. I am trying to calculate how many 2 tiles there are in the matrix with the 2 left most entries are 1 and the two most right entries are 0.
Eg
[1 0]
[1 0]
Any idea on how to start this?. I am quite new to R. Thanks very much in advance.
You can subset all chunks of larger matrix with dimensions equal to that of ntile and then check if all the elements of the chunk match the corresponding elements of ntile.
#Data
set.seed(1)
m = matrix(sample(1:0, 16, TRUE), 4)
m[3, 4] = 0
ntile = rbind(1:0, 1:0)
n = dim(ntile)
ans = t(sapply(n[1]:nrow(m), function(i){
sapply(n[2]:ncol(m), function(j){
temp = m[(i- nrow(ntile) + 1):i, (j - ncol(ntile) + 1):j]
all(temp == ntile)
})
}))
ans
# [,1] [,2] [,3]
#[1,] FALSE FALSE FALSE
#[2,] FALSE FALSE FALSE
#[3,] FALSE FALSE TRUE
sum(ans)
#[1] 1
Here is a simple solution if I understood your question correctly:
set.seed(123)
size <- 4
m <- matrix(sample(0:1, 12, replace = TRUE), size-1, size)
m <- rbind(m, c(0,0,1,0))
sum(m[1:(size-1),1:(size-1)] == 1 & m[2:size,1:(size-1)] == 1 &
m[1:(size-1),2:size] == 0 & m[2:size,2:size] == 0)
Input
[,1] [,2] [,3] [,4]
[1,] 0 1 1 0
[2,] 1 1 1 1
[3,] 0 0 1 0
[4,] 0 0 1 0
Output
# 1
You can make sure that the number of 2 tiles is 1.
Have written a script that iterates through a matrix and returns the x co-ord and y co-ord of every non-NA value in the matrix. How do i append this code to create another column of the value of each element in the matrix attached to the co-ordinates
matrixop = function(m2){
zzz <- NULL
for (i in 1:ncol(m2)){
for (j in 1:nrow(m2)) {
if ((is.na(m2[i,j])) == FALSE ){
}
zzz <- rbind(zzz,c(i,j))
}
}
zzz
}
result = lapply(m1, FUN = matrixop) #m1 being existing nxn matrix
actual results were a nx2 matrix with the x co ords in the first column and the y coords in the 2nd column. Trying to get a third column with the value attached to those co ords
Take advantage of which argument arr.ind and cbind with the values of the matrix seen as a vector. The missing values are removed with complete.cases.
mat2coord <- function(x){
d <- which(x == x | is.na(x), arr.ind = TRUE)
d <- cbind(d, value = c(x))
d[complete.cases(d), ]
}
m <- matrix(1:6, nrow = 3)
mat2coord(m)
# row col value
#[1,] 1 1 1
#[2,] 2 1 2
#[3,] 3 1 3
#[4,] 1 2 4
#[5,] 2 2 5
#[6,] 3 2 6
set.seed(1234)
is.na(m) <- sample(6, 2)
mat2coord(m)
# row col value
#[1,] 1 1 1
#[2,] 3 1 3
#[3,] 2 2 5
#[4,] 3 2 6
"a" is a list and "b" is a matrix.
a<-list(matrix(c(0,2,0,1,0,2,0,0,1,0,0,0,0,0,2,2),4),
matrix(c(0,1,0,0,0,1,1,0,0,0,0,0),3),
matrix(c(0,0,0,0,2,0,1,0,0,0,0,0,2,0,2,1,0,1,1,0),5))
b<-matrix(c(2,2,1,1,1,2,1,2,1,1,2,1,1,1,1,1,1,2,2,2,1,2,1,1),6)
> a
[[1]]
[,1] [,2] [,3] [,4]
[1,] 0 0 1 0
[2,] 2 2 0 0
[3,] 0 0 0 2
[4,] 1 0 0 2
[[2]]
[,1] [,2] [,3] [,4]
[1,] 0 0 1 0
[2,] 1 0 0 0
[3,] 0 1 0 0
[[3]]
[,1] [,2] [,3] [,4]
[1,] 0 0 0 1
[2,] 0 1 0 0
[3,] 0 0 2 1
[4,] 0 0 0 1
[5,] 2 0 2 0
> b
[,1] [,2] [,3] [,4]
[1,] 2 1 1 2
[2,] 2 2 1 2
[3,] 1 1 1 1
[4,] 1 1 1 2
[5,] 1 2 1 1
[6,] 2 1 2 1
There are 3 objects in list "a". I want to test whether all the non-zero elements in each object in the list "a" match with the corresponding position of the same row in matrix "b". If matched, output the matched row number of b.
For example, the second object is
[[2]]
[,1] [,2] [,3] [,4]
[1,] 0 0 1 0
[2,] 1 0 0 0
[3,] 0 1 0 0
We can see the non-zero number in the 1st row is 1, and it locates in the third place of the row, it can match the 1-5 rows of matrix "b", the non-zero number in the 2nd row is 1, and it locates in the first place of this row, it can match the 3-5 rows of matrix "b", the non-zero number in the 3rd row is 1, and it locates in the second place of this row, it can match the 3-4 rows of matrix "b". so only the 3rd or 4th row of Matrix "b" can match all the rows in this object, so the output result is "3 4".
My attempting code is as follows:
temp<-Map(function(y) t(y), Map(function(a)
apply(a,1,function(x){
apply(b,1, function(y) identical(x[x!=0],y[x!=0]))}),a))
lapply(temp, function(a) which(apply(a,2,prod)==1))
The result is as follows:
[[1]]
integer(0)
[[2]]
[1] 3 4
[[3]]
[1] 6
It is right. but I wonder whether there is more quick code to handle this question?
Having a few columns and trying to take advantage of columns with > 1 unique values or no non-zero values to reduce computations:
ff = function(a, b)
{
i = seq_len(nrow(b)) #starting candidate matches
for(j in seq_len(ncol(a))) {
aj = a[, j]
nzaj = aj[aj != 0L]
if(!length(nzaj)) next #if all(a[, j] == 0) save some operations
if(sum(tabulate(nzaj) > 0L) > 1L) return(integer()) #if no unique values in a column break looping
i = i[b[i, j] == nzaj[[1L]]] #update candidate matches
}
return(i)
}
lapply(a, function(x) ff(x, b))
#[[1]]
#integer(0)
#
#[[2]]
#[1] 3 4
#
#[[3]]
#[1] 6
With data of your actual size:
set.seed(911)
a2 = replicate(300L, matrix(sample(0:3, 20 * 5, TRUE, c(0.97, 0.01, 0.01, 0.01)), 20, 5), simplify = FALSE)
b2 = matrix(sample(1:3, 15 * 5, TRUE), 15, 5)
identical(OP(a2, b2), lapply(a2, function(x) ff(x, b2)))
#[1] TRUE
microbenchmark::microbenchmark(OP(a2, b2), lapply(a2, function(x) ff(x, b2)), times = 50)
#Unit: milliseconds
# expr min lq mean median uq max neval cld
# OP(a2, b2) 686.961815 730.840732 760.029859 753.790094 785.310056 863.04577 50 b
# lapply(a2, function(x) ff(x, b2)) 8.110542 8.450888 9.381802 8.949924 9.872826 15.51568 50 a
OP is:
OP = function (a, b)
{
temp = Map(function(y) t(y), Map(function(a) apply(a, 1,
function(x) {
apply(b, 1, function(y) identical(x[x != 0], y[x !=
0]))
}), a))
lapply(temp, function(x) which(apply(x, 2, prod) == 1))
}
Your explanations of what you want and what your possible matrices look like are really not clear. From what I can deduce, you want to match the row number in b that matches the unique non-zero number in each column of a matrix in a. If so, here's a simpler option:
lapply(a, function(x){ # loop across the matrices in a
x[x == 0] <- NA # replace 0s with NA
which(apply(b, 1, function(y){ # loop across the rows of b, trying to match
all(y == colMeans(x, na.rm = TRUE)) # the rows of b with the colmeans of x
}))
})
# [[1]]
# [1] 2
#
# [[2]]
# [1] 5
#
# [[3]]
# [1] 6
I have a matrix with values 0 or 1 and I would like to obtain a list of groups of adjacent 1's. Vertical and horisontal neighbors of each 1 are considered when defining the connected groups.
For example, the matrix
mat = rbind(c(1,0,0,0,0),
c(1,0,0,1,0),
c(0,0,1,0,0),
c(0,0,0,0,0),
c(1,1,1,1,1))
> mat
[,1] [,2] [,3] [,4] [,5]
[1,] 1 0 0 0 0
[2,] 1 0 0 1 0
[3,] 0 0 1 0 0
[4,] 0 0 0 0 0
[5,] 1 1 1 1 1
should return the following 4 connected components:
C1 = {(1,1);(2,1)}
C2 = {(2,4)}
C3 = {(3,3)}
C4 = {(5,1);(5,2);(5,3);(5,4);(5,5)}
Does anybody has an idea of how to do it fast in R? My real matrix is indeed rather large, like 2000x2000 (but I expect that the number of connected components to be reasonably small, i.e. 200).
You can turn your binary matrix into a raster object, and use the raster::clumps function to "Detect clumps (patches) of connected cells. Each clump gets a unique ID". Then it is just data management to return the exact format you want. Example below:
library(igraph)
library(raster)
mat = rbind(c(1,0,0,0,0),
c(1,0,0,1,0),
c(0,0,1,0,0),
c(0,0,0,0,0),
c(1,1,1,1,1))
Rmat <- raster(mat)
Clumps <- as.matrix(clump(Rmat, directions=4))
#turn the clumps into a list
tot <- max(Clumps, na.rm=TRUE)
res <- vector("list",tot)
for (i in 1:tot){
res[i] <- list(which(Clumps == i, arr.ind = TRUE))
}
Which then res prints out at the console:
> res
[[1]]
row col
[1,] 1 1
[2,] 2 1
[[2]]
row col
[1,] 2 4
[[3]]
row col
[1,] 3 3
[[4]]
row col
[1,] 5 1
[2,] 5 2
[3,] 5 3
[4,] 5 4
[5,] 5 5
I wouldn't be surprised if there is a better way to go from the raster object to your end goal though. Again a 2000 by 2000 matrix should not be a big deal for this.
Old (wrong answer) but should be useful for people who want connected components of a graph.
You can use the igraph package to turn your adjacency matrix into a network and return the components. Your example graph is one component, so I removed one edge for illustration.
library(igraph)
mat = rbind(c(1,0,0,0,0),
c(1,0,0,1,0),
c(0,0,1,0,0),
c(0,0,0,0,0),
c(1,1,1,1,1))
g <- graph.adjacency(mat) %>% delete_edges("5|3")
plot(g)
clu <- components(g)
groups(clu)
The final line then returns at the prompt:
> groups(clu)
$`1`
[1] 1 2 4 5
$`2`
[1] 3
My experience with this algorithm it is pretty fast - so I don't think 2,000 by 2,000 will be a problem.
I've got a matrix with a lot of zeros and with positive numerical values.
I want to get the row and column number for which the element is
the minimal NONZERO value of the matrix.
I don't find that min() has extra options to exclude zero, so how can I
handle this?
Seems like there could be a shorter answer but u can replace the zeros with NA and use na.rm=T
test = matrix(c(0:9,0:9),nrow =4,ncol=5)
test[which(test==0)] = NA
minValue = min(test,na.rm=T)
rows = which(apply(test,1,min,na.rm=T)==minValue)
cols = which(apply(test,2,min,na.rm=T)==minValue)
Allows for duplicates
Using x[x>0] you can directly find your nonzero element, but as you want row number and column number you have to give extra effort. I have used apply method for the same, which will return you index for row number and column number...
set.seed(1234)
set.seed(1234)
myMat = matrix(sample(0:1,replace=T,25),nrow=5)
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 1 1 0
[2,] 1 0 1 0 0
[3,] 1 0 0 0 0
[4,] 1 1 1 0 0
[5,] 1 1 0 0 0
#Defining a function which will return zero valued member index
FindIndex = function(x){
v=vector()
j=1
for(i in 1:length(x)){
if(x[i]>0){
v[j]=i
j=j+1
}
}
return = v
}
#Find column number row-wise
apply(myMat, 1, FUN=FindIndex)
[[1]]
[1] 2 3 4
[[2]]
[1] 1 3
[[3]]
[1] 1
[[4]]
[1] 1 2 3
[[5]]
[1] 1 2
#Find row number column-wise
apply(myMat, 2, FUN=FindIndex)
[[1]]
[1] 2 3 4 5
[[2]]
[1] 1 4 5
[[3]]
[1] 1 2 4
[[4]]
[1] 1
[[5]]
logical(0)
You can convert this return values to your required format..