R: Multiplication of data frame elements with neighbors

I have a data frame of 300x300 elements. Each of them is -1 or +1:

     [,1]   [,2]   [,3]  
[1,]   1     -1     -1   
[2,]   1      1      1  
[3,]  -1     -1      1  
[4,]   1      1     -1

      

What I want is to iterate over my data frame and multiply each value by each adjacent value.
That is to say:
For element [1,1] in my original dataframe, I want the product from [1,1], [1,2] and [2,1]
For element [2,2] in my original dataframe I want to get the product [2,2], [1,2], [2,1], [2,3] and [3,2].

I tried to create 4 new dataframes, each offset 1 element to the right, left, up and down respectively:

x_up <- shift(x, 1, dir='up')
x_up <- as.array(x_up)
dim(x_up) <- dims
x_down <- shift(x, 1, dir='down')
x_down <- as.array(x_down)
dim(x_down) <- dims
x_left <- shift(x, 1, dir='left')
x_left <- as.array(x_left)
dim(x_left) <- dims
x_right <- shift(x, 1, dir='right')
x_right <- as.array(x_right)
dim(x_right) <- dims

      

where x is my original dataframe.
I can see when I used this approach, the new data frames are not rightfully shifted; moreover, they are identical. I have verified this with identical ().

Is there any other approach to my problem?

Edit:
shift () has binhf library

+3


source to share


2 answers


I think there is perhaps a smarter way to do this, but the standard approach would be to iterate over each element and multiply its environment.

Beginning with:

mat <- matrix(c(1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1), ncol=3)

      

To avoid problems with positive margins, you should add column and row 1

as fields (positive 1 won't be a problem when multiplying, if you summed it up, it should be 0

for example).

mat2 <- addmargins(mat, FUN=function(x) 1)

      



Now you create an empty matrix to hold the output, and then iterate over the elements and multiply the adjacent ones.

out <- matrix(nrow=nrow(mat), ncol=ncol(mat))
for (i in 1:nrow(mat)) {
  for (j in 1:ncol(mat)) {
    out[i,j] <- prod(mat[i,j], mat2[i-1, j], mat2[i, j-1], mat2[i+1, j], mat2[i, j+1])
  }
}

      

Result:

> out
     [,1] [,2] [,3]
[1,]   -1    1    1
[2,]   -1    1   -1
[3,]    1    1    1
[4,]   -1    1   -1

      

It took less than a second for a 300x300 matrix, so it might be enough for you.

+4


source


This should do the trick:

ind <- which(x==x, arr.ind=TRUE) # index matrix

# find distances (need distances of 1 or 0) 
dist.mat <- as.matrix(dist(ind))
inds2mult <- apply(dist.mat, 1, function(ii) which(ii <= 1))

# get product of each list element in inds2mult
# and reform into appropriate matrix
matrix(
    sapply(inds2mult, function(ii) prod(unlist(x)[ii])),
    ncol=ncol(x))

#     [,1] [,2] [,3]
#[1,]   -1    1    1
#[2,]   -1    1   -1
#[3,]    1    1    1
#[4,]   -1    1   -1

      

To get around memory problems with large matrices when called dist

, you can try a function fields.rdist.near

(with a delta value of 1) from the package fields

:



x <- matrix(rep(-1, 300*300), ncol=300)

ind <- which(x==x, arr.ind=TRUE) # index matrix

library(fields)
ind.list <- fields.rdist.near(ind, delta=1) # took my computer ~ 15 - 20 seconds

inds2mult <- tapply(ind.list$ind[,2], ind.list$ind[,1], list)

matrix(
    sapply(inds2mult, function(ii) prod(unlist(x)[ii])),
    ncol=ncol(x))

      

Delta argument from the man page fields.rdist.near

:

Distance to the Cross. All point pairs separated by more than a delta distance are ignored.

+2


source







All Articles