Levelplot in R on irregular grid - r

I would like to create a 2D levelplot in R where the x and y coordinates are from an irregular grid without using interpolation. The grid is given below:
grid<-cbind(seq(from=0.05,to=0.5,by=0.05),seq(from=0.05,to=0.5,by=0.05))
grid<-rbind(grid,cbind(seq(from=0.0,to=0.95,by=0.05),seq (from=0.05,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.9,by=0.05),seq (from=0.1,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.85,by=0.05),seq(from=0.15,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.75,by=0.05),seq(from=0.25,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.80,by=0.05),seq(from=0.20,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.70,by=0.05),seq(from=0.30,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.65,by=0.05),seq(from=0.35,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.60,by=0.05),seq(from=0.40,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.55,by=0.05),seq(from=0.45,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.50,by=0.05),seq(from=0.50,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.40,by=0.05),seq(from=0.60,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.45,by=0.05),seq(from=0.55,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=.35,by=0.05),seq(from=0.65,to=1,by=0.05)))
grid<-rbind(grid,cbind(seq(from=0,to=0.30,by=0.05),seq(from=0.70,to=1,by=0.05)))
x=grid[,1]
y=grid[,2]
The Z-values are stored in another vector. I have tried to use the image-function, but without any luck. For instance, if I try
image(x,y,height.vals)
where
height.vals=matrix(runif(dim(grid)[1]),nrow=dim(grid)[1],ncol=1)
I get an error message saying that x and y should be increasing.
One could use the akima function interp, but then I get interpolated data.

Looks like you have points on a 20 x 20 grid. So, you can create a 20 x 20 matrix and fill it with the values from height.vals.
With a little bit of tweaking, you can turn the x and y values into indices of the matrix and use those indices to assign height.vals to the appropriate places in the matrix.
# Turn the x and y values into integers.
# R doesn't take 0 as an index, so add 1 to the x values to get rid of the 0s
inds <- cbind(x = as.integer(20*x + 1), y = as.integer(20*y))
# create the 20 x 20 matrix
m <- matrix(nrow = 20, ncol = 20)
# fill the matrix with height.vals based on the indices
m[inds] <- height.vals
Then, you can use m as an input to functions like image, filled.contour, and lattice::levelplot
image(m)

Related

R: Interpolate x values given y and z in a matrix using interp2 and fzero

Brief summary
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.
In Depth
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)

Plotting function with 3 parameters (4d) in R

I want to see how three variables x, y, and z respond to a function f using R.
I've searched for R solutions (e.g. rgl using 4d plots) but none seem to allow the input of a function as the fourth variable while allowing manipulation of x, y, and z across their full range of values.
# First I create three variables that each have a domain 0 to 4
x
y
z
# Then I create a function from those three variables
f <- sqrt(x^2 + y^2 + z^2)
EDIT: I originally stated that I wanted x, y, and z to be seq(0, 4, 0.01) but in fact I only want them to range from 0 to 4, and do so independently of other variables. In other words, I want to plot the function across a range of values letting x move independently of y and z and so forth, rather than plotting a 3-D line. The result should be a 3-D surface.
I want to:
a) see how the function f responds to all possible combinations of x, y, and z across a range of x, y, and z values 0 to 4, and
b) find what maxima/minima exist especially when holding one variable constant.
This is rather a mathematical questions. Unfortunately, our computer screens are not really made fro 4D, neither our brains. So what you ask wont be possible as if. Indeed, you want to show a dense set of data (a cube between 0 and 4), and we can not display what is "inside" the cube.
To come back to R, you can always display a slice of it, for example fixing z and plot sqrt(x^2 + y^2 + z^2) for x and y. Here you have two examples:
# Points where the function should be evaluated
x <- seq(0, 4, 0.01)
y <- seq(0, 4, 0.01)
z <- seq(0, 4, 0.01)
# Compute the distance from origin
distance <- function(x,y,z) {
sqrt(x^2 + y^2 + z^2)
}
# Matrix to store the results
slice=matrix(0, nrow=length(x),ncol=length(y))
# Fill the matrix with a slice at z=3
i=1
for (y_val in y)
{
slice[,i]=distance(x,y_val,3)
i=i+1
}
# PLot with plot3D library
require(plot3D)
persp3D(z = slice, theta = 100,phi=50)
# PLot with raster library
library(raster)
plot(raster(slice,xmn=min(x), xmx=max(x), ymn=min(y), ymx=max(y)))
If you change your z values, you will not really change the shape (just making it "flatter" for bigger z). Note that the function being symmetric in x, y and z, the same plots are produced if you keep xor y constant.
For your last question about the maximum, you can re-use the slice matrix and do:
max_ind=which(slice==max(slice),arr.ind = TRUE)
x[max_ind[,1]]
y[max_ind[,2]]
(see Get the row and column name of the minimum element of a matrix)
But again with math we can see from your equation that the maximum will always be obtained by maxing x, y and z. Indeed, the function simply measure the distance from the origin.

Can I use filled.contour to plot data with decimal cartesian coordinates?

I have a 288000x3 matrix (288000 rows, 3 columns) of x and y cartesian coordinates from -60 to 60 with decimals that trail to 8 places along with a value at those coordinates.
Example-
y.cart x.cart value
[1,] 0.001308930 0.07498858 -49.36752
[2,] 0.002617462 0.07495431 -48.33903
[3,] 0.003925197 0.07489722 -51.42450
[4,] 0.005231736 0.07481730 -51.93874
[5,] 0.006536681 0.07471460 -513.73075
[6,] 0.007839635 0.07458914 -52.45299
[7,] 0.009140201 0.07444096 -51.93874
[8,] 0.010437983 0.07427011 -48.85327
[9,] 0.011732585 0.07407663 -49.36752
[10,] 0.013023613 0.07386058 -50.91025
This is weather radar reflectivity data and I need to plot it to look like the output that filled.contour creates, but in order to use filled.contour, the values need to be in a matrix because the function uses the matrix position as the coordinates for the plot which doesn't work with the way that my data is organized. Is there a way to use a filled.contour with the data in this form or, is there another way to do this? I've been fiddling with it for two days and haven't gotten very far. Any help would be greatly appreciated.
You can try to get the value column in a Matrix. This can done in a for loop. But for this I make the assumption, that in your data the y and x values in the variables y.cart and x.cart are not unique. I did this because I think you have something like a map and on this map every point from a grid is a pair of coordinates.
Is this correct you can try this code:
# Some sample data:
y.cart <- x.cart <- seq(-60,60,length.out = 600)
# Bring it in the form like your data are:
DF <- data.frame(x.cart = sample(x = x.cart, length(x.cart)^2, replace = TRUE),
y.cart = sample(x = y.cart, length(y.cart)^2, replace = TRUE),
value = rnorm(length(y.cart)^2))
# Also works for a Matrix:
DF <- as.matrix(DF)
# Define the Matrix Z. In this Matrix are just NAs, because if a value on a
# special coordinate doesn't exist there should be nothing drawn:
Z <- matrix(rep(NA,length(DF[,1])^2), nrow = length(DF[,1]))
# Get the unique points which represent the x and y coordinate. It's important
# to use the unique points for getting the index for the Matrix out of this vectors:
x <- sort(unique(DF[,1]))
y <- sort(unique(DF[,2]))
# In this loop every row in de data.frame (or matrix) is matched with the vector
# x for the i-th row in the Matrix and with the vector y for the j-th column in
# the Matrix Z[i,j]:
for(i in seq(along = DF[,1])) {
Z[which(x == DF[i,1]),which(y == DF[i,2])] <- DF[i,3]
}
# Now you can use persp or filled.contour with the following call:
persp(x,y,Z)
filled.contour(x,y,Z)
This works for my sample data, even though it makes no sense for them. Keep in your mind that the for loop isn't very fast and with your data it could take a while. You can build in a process bar to controle the status from the loop with:
pb <- txtProgressBar(min = 1, max = length(DF[,1]), style = 3)
for(i in seq(along = DF[,1])) {
Z[which(x == DF[i,1]),which(y == DF[i,2])] <- DF[i,3]
setTxtProgressBar(pb, i)
}
Also it's necessary that x and y have the same length and the Matrix Z is a Matrix with dimensions lenght(x) and length(y).
I hope this works for you. If my thinkings about the data aren't true you can maybe give a little more details about the data. And do not forget to replace DF with the name of your matrix.

R Surface Plot from List of X,Y,Z points

I am trying to make a surface plot for data that is in a very long list of x,y,z points. To do this, I am dividing the data into a grid of 10k squares and finding the max value of z within each square. From my understanding, each z value should be stored in a matrix where each element of the matrix corresponds to a square on the grid. Is there an easier way to do this than the code below? That last line is already pretty long and it is only one square.
x<-(sequence(101)-1)*max(eff$CFaR)/100
y<-(sequence(101)-1)*max(eff$EaR)/100
effmap<-matrix(ncol=length(x)-1, nrow=length(y)-1)
someMatrix <- max(eff$Cost[which(eff$EaR[which(eff$CFaR >= x[50] & eff$CFaR <x[51], arr.ind=TRUE)]>=y[20] & eff$EaR[which(eff$CFaR >= x[50] & eff$CFaR <x[51], arr.ind=TRUE)]< y[91])])
So this is my interpretation of what you are trying to accomplish...
df <- read.csv("effSample.csv") # downloaded from your link
df <- df[c("CFaR","EaR","Cost")] # remove unnecessary columns
df$x <- cut(df$CFaR,breaks=100,labels=FALSE) # establish bins: CFaR
df$y <- cut(df$EaR,breaks=100,labels=FALSE) # establish bins: EaR
df.max <- expand.grid(x=1:100,y=1:100) # template; 10,000 grid cells
# maximum cost in each grid cell - NOTE: most of the cells are *empty*
df.max <- merge(df.max,aggregate(Cost~x+y,df,max),all.x=TRUE)
z <- matrix(df.max$Cost,nr=100,nc=100) # Cost vector -> matrix
# colors based on z-value
palette <- rev(rainbow(20)) # palette of 20 colors
zlim <- range(z[!is.na(z)])
colors <- palette[19*(z-zlim[1])/diff(zlim) + 1]
# create the plot
library(rgl)
open3d(scale=c(1,1,10)) # CFaR and EaR range ~ 10 X Cost range
x.values <- min(df$CFaR)+(0:99)*diff(range(df$CFaR))/100
y.values <- min(df$EaR)+(0:99)*diff(range(df$EaR))/100
surface3d(x.values,y.values,z,col=colors)
axes3d()
title3d(xlab="CFaR",ylab="EaR",zlab="Cost")
The code above generates a rotatable 3D plot, so the image is just a screen shot. Notice how there are lots of "holes". This is (partially) because you provided only part of your data. However, it is important to realize that just because you imagine 10,000 grid cells (e.g., a 100 X 100 grid), does not mean that there will be data in every cell.

Octave 3D mesh, data from file

I have a big file with 3 columns: density, dimension, value.
example:
10 0.3 200
10 0.4 300
20 0.3 250
20 0.4 320
I am trying to draw a 3d plot - mesh with mesh() function in octave, like this:
data = load ("file.txt");
mesh(data(:,1), data (:,2), data (:,3));
Problem I have is , I always get error:
rows (z) must be the same as length (y), columns (z) must be the same as length (x).
It worked with function plot3(), but I would like a mesh kind of plot.
The problem is that mesh(X,Y,Z) is expecting your X and Y matrices to be generated using X = meshgrid(x) and Y = meshgrid(y) where x and y only contain unique points. Your data basically already defines the meshgrid, but it is difficult to get it out.
I suggest using reshape as:
X = reshape(data(:,1),m,n);
Y = reshape(data(:,2),m,n); % might be reshape(data(:,2),n,m)
Z = reshape(data(:,3),m,n);
mesh(X,Y,Z);
In this case the assumption is that you have m unique values in Y, and n unique values in X. You may have to transpose these in your call to mesh as mesh(X',Y',Z) or something like that.

Resources