Obtaining connected components of neighboring values
Asked Answered
I

1

13

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).

Irrefutable answered 3/3, 2016 at 12:58 Comment(0)
T
16

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.

Triumphal answered 3/3, 2016 at 13:36 Comment(3)
Thank you for your answer, but my matrix is not an adjacency matrix: it does not represent a graph. I would like to find groups of 1's grouped (i.e. adacent in the matrix).Irrefutable
Ahh sorry - connected components is a term already used for what I described. It appears the raster package has a function named clump that does what you want. I will see if I can whip up an example.Triumphal
So excellent! The loop for (i in 1:max(Clumps, na.rm=TRUE)) can be simplified as for (i in 1:tot), isn't it?Stephanotis

© 2022 - 2024 — McMap. All rights reserved.