Find Neighbouring Elements of a Matrix in R

For each element in a matrix, find the sum of all of its neighbors

One way would be to make matrices with the neighbor on each side and add them together.

rbind(z[-1,],0) + rbind(0,z[-nrow(z),]) + cbind(z[,-1],0) + cbind(0,z[,-ncol(z)])
## [,1] [,2] [,3]
## [1,] 6 9 8
## [2,] 13 20 17
## [3,] 12 21 14

how to select neighbouring elements in a vector and put them into a list or matrix in R

You can do:

matrix(c(vl[-length(vl)], vl[-1]), ncol = 2)

[,1] [,2]
[1,] 1 2
[2,] 2 3
[3,] 3 4
[4,] 4 5

If you want to sort two columns rowwise, then you can use pmin() and pmax() which will be faster than using apply(x, 1, sort) with a large number of rows.

sapply(c(pmin, pmax), do.call, data.frame(vl[-length(vl)], vl[-1]))

Find number of neighbouring values in a matrice (R)

In the following we use the example matrix m generated reproducibly in the Note at the end.

1) Append a row and column of zeros on each end and then apply rollsum , transpose, apply it again and transpose again. Finally subtract the original matrix so that only neighbors are counted. This solution is the most compact of those here.

library(zoo)

m2 <- rbind(0, cbind(0, m, 0), 0)
t(rollsum(t(rollsum(m2, 3)), 3)) - m
## [,1] [,2] [,3] [,4] [,5]
## [1,] 2 3 5 3 2
## [2,] 3 5 6 4 2
## [3,] 2 6 5 5 2
## [4,] 2 5 2 2 1
## [5,] 1 3 2 2 1

2) A second approach is the following. It would be much faster than the others here.

nr <- nrow(m)
nc <- ncol(m)

mm <- cbind(m[, -1], 0) + m + cbind(0, m[, -nc])
rbind(mm[-1, ], 0) + mm + rbind(0, mm[-nr, ]) - m
## [,1] [,2] [,3] [,4] [,5]
## [1,] 2 3 5 3 2
## [2,] 3 5 6 4 2
## [3,] 2 6 5 5 2
## [4,] 2 5 2 2 1
## [5,] 1 3 2 2 1

3) Using loops we can write the following. It is probably the most straight forward. Note that subscripting by 0 omits that element and i %% 6 equals i if i is in 1, 2, 3, 4, 5 and equals 0 if i equals 0 or 6.

nr <- nrow(m); nr1 <- nr + 1
nc <- ncol(m); nc1 <- nc + 1

mm <- 0 * m # initialize result
for(i in seq_len(nr))
for(j in seq_len(nc))
mm[i, j] <- sum(m[seq(i-1, i+1) %% nr1, seq(j-1, j+1) %% nc1]) - m[i,j]
mm
## [,1] [,2] [,3] [,4] [,5]
## [1,] 2 3 5 3 2
## [2,] 3 5 6 4 2
## [3,] 2 6 5 5 2
## [4,] 2 5 2 2 1
## [5,] 1 3 2 2 1

Note

set.seed(123)
m <- +matrix(rnorm(25) > 0, 5)

m
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0 1 1 1 0
## [2,] 0 1 1 1 0
## [3,] 1 0 1 0 0
## [4,] 1 0 1 1 0
## [5,] 1 0 0 0 0

R - finding the neighbors of neighbors and storing them in a unique adjacency matrix

This is a simple summary of the distances table.

library(igraph)
g = graph_from_adjacency_matrix(A, mode = "undirected")
dists = distances(g)

(result = ifelse(dists == 2, 1, 0))
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
# [1,] 0 0 0 0 0 0 0 0 1
# [2,] 0 0 0 0 0 1 0 0 0
# [3,] 0 0 0 1 0 0 0 1 0
# [4,] 0 0 1 0 0 0 0 1 0
# [5,] 0 0 0 0 0 0 0 0 0
# [6,] 0 1 0 0 0 0 0 0 0
# [7,] 0 0 0 0 0 0 0 0 0
# [8,] 0 0 1 1 0 0 0 0 0
# [9,] 1 0 0 0 0 0 0 0 0

And this easily extends to 2 steps away, 3 steps away, etc, however
the distances table will give shortest-path length, so if you look at lengths > 1 and your graph has cycles, this will only look at the shortest path.

Finding neighbours in a two-dimensional array

(pseudo-code)

row_limit = count(array);
if(row_limit > 0){
column_limit = count(array[0]);
for(x = max(0, i-1); x <= min(i+1, row_limit); x++){
for(y = max(0, j-1); y <= min(j+1, column_limit); y++){
if(x != i || y != j){
print array[x][y];
}
}
}
}

Of course, that takes almost as many lines as the original hard-coded solution, but with this one you can extend the "neighborhood" as much as you can (2-3 or more cells away)

List of n first Neighbors from a 3d Array R

Based on the comments, I assume you are defining "first nearest neighbor" as all cells with a euclidean distance of 1 or less (excluding self), "second nearest neighbors" as those with 2 or less, etc. Your assertion in a comment in @evan058's answer that "for (1,1,1) the first level neighbors is 2,4,5,10,11,13", I'm actually interpreting this to include the immediate diagonals (with a distance of 1.414) but not further diagonals (in your example, 14 would be a further diagonal with a distance of 1.732).

This function accepts either a pre-defined array (ary) or the dimensions to make one (dims).

nearestNeighbors(dims = c(3,3,3), elem = c(1,1,1), dist = 1)
# dim1 dim2 dim3
# [1,] 2 1 1
# [2,] 1 2 1
# [3,] 1 1 2
nearestNeighbors(dims = c(3,3,3), elem = c(1,1,1), dist = 1,
return_indices = FALSE)
# [1] 2 4 10
nearestNeighbors(dims = c(3,3,3), elem = c(1,1,1), dist = 2,
return_indices = FALSE)
# [1] 2 3 4 5 7 10 11 13 14 19

nearestNeighbors(ary = array(27:1, dim = c(3,3,3)), elem = c(1,1,1), dist = 2)
# dim1 dim2 dim3
# [1,] 2 1 1
# [2,] 3 1 1
# [3,] 1 2 1
# [4,] 2 2 1
# [5,] 1 3 1
# [6,] 1 1 2
# [7,] 2 1 2
# [8,] 1 2 2
# [9,] 2 2 2
# [10,] 1 1 3
nearestNeighbors(ary = array(27:1, dim = c(3,3,3)), elem = c(1,1,1), dist = 2,
return_indices = FALSE)
# [1] 26 25 24 23 21 18 17 15 14 9

The function:

#' Find nearest neighbors.
#'
#' @param ary array
#' @param elem integer vector indicating the indices on array from
#' which all nearest neighbors will be found; must be the same
#' length as \code{dims} (or \code{dim(ary)}). Only one of
#' \code{ary} and \code{dim} needs to be provided.
#' @param dist numeric, the max distance from \code{elem}, not
#' including the 'self' point.
#' @param dims integer vector indicating the dimensions of the array.
#' Only one of \code{ary} and \code{dim} needs to be provided.
#' @param return_indices logical, whether to return a matrix of
#' indices (as many columns as dimensions) or the values from
#' \code{ary} of the nearest neighbors
#' @return either matrix of indices (one column per dimension) if
#' \code{return_indices == TRUE}, or the appropriate values in
#' \code{ary} otherwise.
nearestNeighbors <- function(ary, elem, dist, dims, return_indices = TRUE) {
if (missing(dims)) dims <- dim(ary)
tmpary <- array(1:prod(dims), dim = dims)
if (missing(ary)) ary <- tmpary
if (length(elem) != length(dims))
stop("'elem'' needs to have the same dimensions as 'ary'")
# work on a subset of the whole matrix
usedims <- mapply(function(el, d) {
seq(max(1, el - dist), min(d, el + dist))
}, elem, dims, SIMPLIFY=FALSE)
df <- as.matrix(do.call('expand.grid', usedims))
# now, df is only as big as we need to possibly satisfy `dist`
ndist <- sqrt(apply(df, 1, function(x) sum((x - elem)^2)))
ret <- df[which(ndist > 0 & ndist <= dist),,drop = FALSE]
if (return_indices) {
return(ret)
} else {
return(ary[ret])
}
}

Edit: changed the code for a "slight" speed improvement: using a 256x256x256 array and a distance of 2 previously took ~90 seconds on my machine. Now it takes less than 1 second. Even a distance of 5 (same array) takes less than a second. Not fully tested, please verify it is correct.

Edit: Removed the extra { on the fifty line of the function.

Checking the neighbours of a cell value from a matrix

What you could do is to merge if statements that lead to the same results. This will lead to shorter (and more readable) code, and can boost performance in certain cases.

So for example if you have:

if([row - 1][col])
// do A
else if([row][col + 1])
// do B
else if([row + 1][col])
// do A

then you could write it as such:

if([row - 1][col] || [row + 1][col])
// do A
else if([row][col + 1])
// do B

Do this, and you feel that your code needs further improvements, please post at Code Review.

Count outside edges of adjacent cells in a matrix in R

If you know the area and the number of inside edges, it is simple to calculate the number of outside edges. Every patch has four edges so in some way, the total number of edges is 4 * area. But that is not quite right because every inside edge is shared between two patches. So the right number of total edges is

4*area - inside

The number of outside edges is the total edges minus the inside edges, so

outside = total - inside = (4*area- inside) - inside = 4*area - 2*inside.

Patch

You can see that the area is made up of 6 squares each of which has 4 sides. The inside edges (the red ones) are shared by two adjacent squares.



Related Topics



Leave a reply



Submit