I have a matrix of values representing a topological surface. I'm trying to calculate the x values for an exact contour z for each column y.
I'm fitting experimental data to a 3-dimensional non-linear decay model, z(x,y). Any code for my analysis needs to fit a number of different datasets. I've managed to parameterize the model successfully, and calculate z given each whole value of x and y. I then need to extract the values of specific contours. The range of each variable are x: (0 > x > 80); y: (0 > y > 100) ; z: (0 > z > 1).
require(pracma)
# Truncated matrix with demo data, normally created by fitting parameterized algorithm to 81x100 matrix
M <- matrix(c(0,1,1,1,1,1,1,1,1,
0,0.843,1,1,1,1,1,1,1,
0,0.484,0.907,1,1,1,1,1,1,
0,0.218,0.459,0.721,0.978,1,1,1,1,
0,0.082,0.185,0.313,0.461,0.621,0.781,0.925,1,
0,0.029,0.066,0.113,0.171,0.242,0.323,0.414,0.511,
0,0.010,0.022,0.038,0.058,0.082,0.112,0.146,0.186,
0,0.003,0.008,0.013,0.020,0.028,0.037,0.049,0.062,
0,0.001,0.003,0.004,0.007,0.009,0.012,0.016,0.021,
0,0.000,0.001,0.001,0.002,0.003,0.004,0.006,0.007,
0,0.000,0.000,0.001,0.001,0.001,0.001,0.002,0.002),ncol = 11)
# Extract Contours ----
X <- seq(from = 0, to = 80, by = 10) # normally by = 1 on full dataset
Y <- seq(from = 0, to = 100, by = 10)
z <- 0.8
FindZ <- function(x) interp2(X, Y, M, x, y)-z
x_out <- matrix()
for(i in 1:11){
y <- i*10 # adjusted for truncated dataset. Normally in increments of 1
x <- fzero(FindZ, mean(X))
x_out[i] <- x$x
print(x$x)
next}
I get the following error:
Error in if (fb == 0) return(list(x = b, fval = fb)) : missing value where TRUE/FALSE needed
I realize not all rows y
will contain values exceeding the z
contour I'm looking for at any given time. To circumvent I have attempted to use tryCatch()
on my larger dataset . When I do this, I get nothing for say, up to loop iteration y[20]
, it runs successfully for approximately 15 iterations, then I get the same error for the balance. The problem being I have been getting this on rows where there are values >z, and the interpolation runs to an error. I have also switched the code, and sought solutions y
for values of x
and z
, and achieved almost exactly the same result.
I don't want to subset the data as I don't want to lose the absolute x,y positions as they correspond to physical measurements. If there is no z
value, I'd be expecting to place a 0
for the column using replace()
.
Ideally, I want a matrix of values of (x,y)
that if I fed back into my original function, they would calculate to my value z
. Any pointers would be great.
I think you're trying to reinvent the wheel here. What you are attempting to do can be achieved efficiently using the isoband
package, which can calculate the x, y co-ordinates of an exact z value given a matrix of z values with vectors of x, y co-ordinates:
result <- as.data.frame(isoband::isobands(Y, X, M, z, z)[[1]])
head(result)
#> x y id
#> 1 44.08998 80.00000 1
#> 2 44.08998 80.00000 1
#> 3 42.44618 70.00000 1
#> 4 40.00000 61.31944 1
#> 5 39.13242 60.00000 1
#> 6 35.27704 50.00000 1
We can show this by plotting your grid with color representing the z values, and draw our result as a line.
library(ggplot2)
ggplot(reshape2::melt(M), aes(Y[Var2], X[Var1])) +
geom_tile(aes(fill = value)) +
geom_path(data = result, aes(x , y), col = "red")
Created on 2022-08-08 by the reprex package (v2.0.1)