I have a matrix that has NA
s. I aim to fill them in using the mean of the 4 neighboring values (a cross pattern). This is the "cross" pattern kernel:
kernel
[,1] [,2] [,3]
[1,] 0.00 0.25 0.00
[2,] 0.25 0.00 0.25
[3,] 0.00 0.25 0.00
Here's a code with a for loop. It works but I don't like it, it's not readable (even for me, it's really hard to follow what's happening). Additionally, I didn't benchmark, but I presume there might be a fastest way to do this.
mx <- matrix(rep(NA, 5^2), nrow = 5)
mx[3,3] <- 10
index <- which(is.na(mx), arr.ind = T)
for (i in 1:nrow(index)) {
# if NA is at first row, use that one, otherwise we will subtract one
bottom <- ifelse(index[i, 1] <= 1, index[i, 1], index[i, 1] - 1)
# if NA is at the last row, use that one, otherwise we will add 1
top <- ifelse(index[i, 1] >= nrow(mx), index[i, 1], index[i, 1] + 1)
# if NA is at first column use that one, otherwise subtract one
left <- ifelse(index[i, 2] <= 1, index[i, 2], index[i, 2] - 1)
# if NA is at the end, use that one, otherwise add one
right <- ifelse(index[i, 2] >= ncol(mx), index[i, 2], index[i, 2] + 1)
# inplace substitution
mx[index[i,1],index[i,2]] <- mean(c(
mx[bottom, index[i, 2]],
mx[top, index[i, 2]],
mx[index[i, 1], right],
mx[index[i, 1], left] ),
na.rm=TRUE)
}
Expected result is
> mx
[,1] [,2] [,3] [,4] [,5]
[1,] NaN NaN NaN NaN NaN
[2,] NaN NaN 10 10 10
[3,] NaN 10 10 10 10
[4,] NaN 10 10 10 10
[5,] NaN 10 10 10 10
I tried using some image packages but couldn't find the exact thing I'm looking for. OpenImageR::convolution
fails with NA
s, imager::inpaint()
does exactly what I want but doesn't allow me to provide my custom kernel.
# all of this reasoning is to convolve this filter
kernel <- matrix(c(0,1,0, 1, 0, 1, 0, 1, 0), nrow=3)
kernel <- kernel/4 # we need 1/4 to get the proper weights
# and use it to calculate the mean of the values
# then use the indices to fill the NAs on the original matrix
# not exactly working as I would like because of NAs
#OpenImageR::convolution(mx, kernel = kernel, mode = "same")
#imager::inpaint() does something similar