Laurent
Laurent

Reputation: 449

How to replace a spot of cells by surrounding cells value in a matrix (R)

I obtain a matrix from a calculation. This matrix can have spots of 1 while the rest of the matrix presents values of 0, 2 and 3.

> ##Working folder
> setwd("C:/Users/laure/Desktop/Code")
> ##Load matrix from excel
> mat <- read.csv("test.csv",  header = TRUE)
> mat <- as.matrix(mat)
> mat
      X1 X1.1 X0 X0.1 X0.2 X0.3 X1.2 X1.3
 [1,]  1    0  0    0    3    0    0    2
 [2,]  1    2  0    3    1    1    0    0
 [3,]  0    0  0    0    3    1    0    0
 [4,]  2    0  0    0    0    0    0    0
 [5,]  0    0  0    0    0    0    2    2
 [6,]  0    0  1    0    0    0    2    2
 [7,]  0    1  1    1    0    0    2    2
 [8,]  0    0  1    1    0    0    2    2
 [9,]  0    0  1    0    0    0    0    0
[10,]  0    0  0    0    0    0    0    0
[11,]  0    0  0    0    2    0    0    1
[12,]  0    0  0    0    2    2    1    1
[13,]  1    1  0    0    0    0    1    1
[14,]  1    1  0    0    0    1    1    1
[15,]  1    1  1    0    0    1    1    1

I would like to replace all the spots of 1 that are completely surrounded by cell values of 0 (in this example have only 1 spot completely surrounded by 0). I do not want to include the spot of 1 on the borders as there are not completely surrounded. I do not know the location and the number of the spots as it depends of the calculation performed before.

I can find the all spots having value of 1 using clump function but how to find the surrounding cells value and replace the spot cells.

####convert matrix into raster
r <-raster(mat)

####select cells with criteria based on cell value
rx <- r == 1

###extract IDs of clumps according the criteria
rc <- clump(rx) 
f <- freq(rc, useNA="no")
> f
     value count
[1,]     1     2
[2,]     2     3
[3,]     3     7
[4,]     4    11
[5,]     5     7

Upvotes: 1

Views: 110

Answers (1)

thothal
thothal

Reputation: 20329

Adopting the approach from compute_neighb_sum you can use the following code:

embed_matrix <- function(mx) {
   cbind(Inf, rbind(Inf, mx, Inf), Inf)
}

disembed_matrix <- function(mx) {
   mx[-c(1, nrow(mx)), -c(1, ncol(mx)), drop = FALSE]
}

is_valid_idx <- function(idx, dim) {
   rowSums(t(t(idx) > dim | t(idx) < 0)) == 0
} 

sum_neighbor_cells <- function(m, include_corner = TRUE, include_element = FALSE) {
   em <- embed_matrix(m)
   dims <- dim(em)
   offsets <- as.matrix(expand.grid(r = -1:1, c = -1:1))
   exclude_offsets <- matrix(integer(0), ncol = 2)
   if (!include_element) {
     exclude_offsets <- rbind(exclude_offsets, c(0, 0))
   }
   if (!include_corner) {
     exclude_offsets <- rbind(exclude_offsets, 
                              matrix(c(-1, -1, 1, 1, -1, 1, -1, 1), ncol = 2))
   }
   dupes <- duplicated(rbind(offsets, exclude_offsets), fromLast = TRUE)
   offsets <- offsets[!dupes[seq_len(nrow(offsets))], , drop = FALSE]
   idx <- cbind(c(row(em)), c(col(em)))
   res <- apply(offsets, 1, function(row) {
      t(t(idx) + row)
   })
   dim(res) <- c(dim(idx), nrow(offsets))
   idx <- aperm(res, 3:1)
   res <- apply(idx, 3, function(i) {
      valid_idx <- i[is_valid_idx(i, dims), , 
             drop = FALSE]
      sum(em[valid_idx])
   })
   dim(res) <- dims
   res <- disembed_matrix(res)
   res
}

Then you can use sum_neighbor_cells to get the sum of all neighboring cells (with ot without the corner cells):

set.seed(123)
(m <- matrix(sample(0:1, 25, TRUE), 5))
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    0    1    1    0    0
# [2,]    0    1    1    1    1
# [3,]    0    1    1    0    0
# [4,]    1    0    0    0    0
# [5,]    0    0    1    0    0

sum_neighbor_cells(m, include_corner = FALSE)
#      [,1] [,2] [,3] [,4] [,5]
# [1,]  Inf  Inf  Inf  Inf  Inf
# [2,]  Inf    3    4    2  Inf
# [3,]  Inf    2    2    2  Inf
# [4,]  Inf    2    2    0  Inf
# [5,]  Inf  Inf  Inf  Inf  Inf
sum_neighbor_cells(m, include_corner = TRUE)  
#      [,1] [,2] [,3] [,4] [,5]
# [1,]  Inf  Inf  Inf  Inf  Inf
# [2,]  Inf    5    6    4  Inf
# [3,]  Inf    4    4    4  Inf
# [4,]  Inf    4    3    2  Inf
# [5,]  Inf  Inf  Inf  Inf  Inf

With this function you can get the indices of cells with have only 0's as neighboring cells easily via:

(idx <- which(sum_neighbor_cells(m, include_corner = FALSE) == 0, arr.ind = TRUE))
#      row col
# [1,]   4   4
m[idx] <- NA
m
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    0    1    1    0    0
# [2,]    0    1    1    1    1
# [3,]    0    1    1    0    0
# [4,]    1    0    0   NA    0
# [5,]    0    0    1    0    0

Upvotes: 1

Related Questions