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
source to share
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.
source to share
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.
source to share