Julia BoundsError with less information - don't know why - julia
I have the following code:
# package for ploting functions
using Plots
# use GR
gr()
# nb points to plot
nbPts = 22
# define polar coordinates of a 30 degree (pi/6) rotation
sine = sin(pi/6)
cosine = cos(pi/6)
# scale factor
scale_factor = 0.9
#---------------------------------------
# 1. PLOT POINTS USING ROTATION MATRIX
#---------------------------------------
# define Rotation matrix ( angle = pi/6, center = (0, 0) )
R = zeros(Float64, 2, 2)
R[1,1] = R[2,2]= cosine
R[1,2] = -sine
R[2,1] = sine
# Scale matrix
### ... <-- EXERCISE 4(c): define a uniform scaling matrix (use scale_factor)
# arrays of points coords
X_mat = zeros(nbPts)
Y_mat= zeros(nbPts)
# first Point (1,0)
X_mat[1] = 1.0
Y_mat[1] = 0.0
for i in 2:nbPts
prevPoint = [X_mat[i-1], Y_mat[i-1]]
#apply rotation to previous point to obtain new point
newPoint = R * prevPoint
### ... <-- EXERCISE 4(c): apply scaling matrix
X_mat[i] = newPoint[1]
Y_mat[i] = newPoint[2]
end
# plot points in blue
plt1 = scatter(X_mat, Y_mat, color=:blue, xlim = (-1.1, 1.1), ylim = (-1.1, 1.1), label=false, title="Rotation using matrices" );
#---------------------------------------
# 2. PLOT POINTS USING COMPLEX NUMBERS
#---------------------------------------
function ComplexProduct(z, w)
(((z[1]*w[1])+(z[2]*w[2])),((z[1]*w[2])+(z[2]*w[1])))
### ... <-- EXERCISE 4(b): implement complex product z * w
end
# first point: z = 1 + 0 * i
Z = ( 1.0, 0.0 )
# second point: w = cosine( pi/6) + sine( pi/6) * i
W = ( cosine, sine )
### ... <-- EXERCISE 4(c): apply scale_factor to W
# arrays of points coords
X_comp = zeros(nbPts)
Y_comp = zeros(nbPts)
# first Point (1,0)
X_comp[1] = Z[1]
Y_comp[1] = Z[2]
for i in 2:nbPts
prevPoint = (X_comp[i-1], Y_comp[i-1])
newPoint = ComplexProduct(prevPoint[1], prevPoint[2]) ### <-- EXERCISE 4(b): compute newPoint by applying rotation to prevPoint (use complex product)
X_comp[i] = newPoint[1]
Y_comp[i] = newPoint[2]
end
# plot points in red
plt2 = scatter(X_comp, Y_comp, color=:red, xlim = (-1.1, 1.1), ylim = (-1.1, 1.1), label=false, title="Rotation using complex numbers" );
# arrange and display
display( plot( plt1, plt2, layout = (1, 2), size=(600*2, 600) ))
The Error:
The Thing I want:
I have to implement a product of complex numbers and this should be used to calculate the rotation with complex numbers.
Should look like that:
What do I have to change so that the BoundsError is fixed?
Don't know what exactly i do wrong because of the poorly information i get from this error log.
Greetings and thanks for the help.
prevPoint[1] is a scalar while your function ComplexProduct expects something that has 2 elements. Perhaps you wanted to pass prevPoint instead of prevPoint[1]?
BTW you use incorrect naming pattern. CamelNaming is discouraged for Julia.
Your variable should be named prev_point and your function should be named complex_product.
Fixed the bug by changing the following code:
newPoint = ComplexProduct(prevPoint, W)
in line 92
Related
Create sequence of degrees and radii that may contain 0 and convert to cartesian coordinates in R
I am needing to create a path between two points for which distance and degrees are known, and then convert the result to Cartesian coordinates using R. The origin for both points is 0,0. The radii always vary in length. The direction is always anti-clockwise. I have been unable to work out how to do the degrees sequence as the range sometimes may include 0°. The start and end degrees will vary so I need method that can handle any range <= 180°. In other words, the difference between the start and end angle will never exceed 180°. The issue is illustrated in the code below: rad <- seq(943.0975, 939.5975, length.out = 1000) deg <- seq(67.8352, 247.8352, length.out = 1000) The rad vector works correctly but the deg vector is incorrect as it ends up following a clockwise direction. Doing a descending seq() means it doesn't cross 0°. How is a range of degree values that contains 0° handled in this instance?
Unable to find a concise method, this is how I handled the issue. It involves a process that generates two vectors for degrees - one for everything to the "left" of 0° and one for everything to the "right" of 0°. It is "messy" around the 0° in that the difference between the two values directly to the left and right of 0° may not be == turni, but given the size of endturnp the issue is not perceptible. This code only works if degdiff <= 180°. Declared variables degA <- 67.8352 # Start point degrees radA <- 943.0975 # Start point radius degB <- 247.8352 # End point degrees radB <- 939.5975 # End point radius endturnp <- 2030 # Number of points to plot between start and end point Derived variables degdiff <- (outdegs - outdege + 180) %% 360 - 180 # Degrees difference between start and end degdiff <- ifelse(degdiff < 0, degdiff * -1, degdiff) # Convert to positive number if negative turni <- degdiff / endturnp # Get degrees increment between degA and degB based on "endturnp" Generate degree vector from degA to degB if(degA < degB) { # If range includes 0 degrees leftlen <- 0:floor((360 - degB) / turni) # Return length vector for degrees > 180 & < 360 leftdeg <- sort(degB + (leftlen * turni), decreasing = T) # Calculate degrees rightlen <- 0:floor(degA/turni) # Return length vector rightdeg <- degA - (turni * rightlen) # calculate degree indexdeg <- append(rightdeg,leftdeg) # Combine degrees vectors } else { indexdeg <- seq(degA, degB, length.out = endturnp) # Create index } Generate radii vector from degA to degB indexrad <- seq(radA, radB, length.out = length(indexdeg)) Combine radii and degrees vectors and calculate xy coordinates arcpath <- data.frame(rad = indexrad, deg = indexdeg, x = NA, y = NA) arcpath$x <- arcpath$rad * sin(pi * 2 * arcpath$deg / 360) arcpath$y <- arcpath$rad * cos(pi * 2 * arcpath$deg / 360) And finally, plot result ggplot() + geom_path(data = arcpath, aes(x = x, y = y), linewidth = .4, lineend = "square")
Can I express the size of some function whose 3arguments are equivalent to (x,y,z), by the density of color at each point on 3 dimension?
In the following program, is it possible to express the size of p[r0,gamma,a] at each point in 3 dimensions, where the X coordinate is r0, the Y coordinate is gamma, and the Z coordinate is a, by the intensity of the color? And if I want to do that, what kind of code and how/where should I implement it? Or is it impossible to that by R? http://okadasan.com/S_P.R I tried to use plot3d But I notice it's impossible, because not 4 ones but only 3 arguments can be used in this plot3d. ####################################### library(rgl) p = array(1:72000, dim=c(20,36,100)) q = c(1:20) k = 4187 # phase constant [rad/m] = (2pi)/lambda =(23.14)/(1500/1000000) f = 1000000 # frequency [Hz] rou = 1.06 # density for (r0 in 1:20) # Depth [5 mm] { for (gamma in 1:36) { for (a in 1:10) # Radius of Daiphragm [m] { a = a0.0005 Jn_z = 0 for (m in 1:100) { z = kasin(gamma10) P_Jn = (((-1)^m)/(factorial(m)factorial(m+1)))(z/2)^(2m+1) Jn_z = Jn_z + P_Jn } Imp<-complex(real=0,imaginary=2pifrou) Phase<-complex(real=0,imaginary=2pif-kr0) # Sound Pressure p[r0,gamma,a] = Imp*(a^2*5*exp(Phase))/(r0*(Jn_z/(k*a*sin(gamma*10)))) } } }
How to generate points in a specific part of a square
Suppose I have a square of size10x10, then I divide this square in equal parts, for example, in 4 equal parts (could be other number like 2, 8, 16, ...). After that, inside a loop I want to choose one of the 4 parts randomly and generate one point in this square. Here I will choose the second square. min.x = 0 max.x=10 min.y=0 max.y=10 xd = xMax-xMin yd = yMax-yMin #generating randomly coordinates at the second square set.seed(1) xx_1 = 5*runif(1) + 5; yy_1 = 5*runif(1) + 0 #ploting the big square and the point in the second square just to ilustrate For this example, if I'll do manually, I could use the following function for each one of the 4 squares: xx_1 = 5*runif(1)+0; yy_1 = 5*runif(1)+0 xx_2 = 5*runif(1)+5; yy_2 = 5*runif(1)+0 xx_3 = 5*runif(1)+0; yy_3 = 5*runif(1)+5 xx_4 = 5*runif(1)+5; yy_4 = 5*runif(1)+5 Any hint on how can I automatizate to generate a point in a specific square?
Here's a little function that does what you ask. You tell it the size of the square (i.e. the length on one side), the number of pieces you want to cut it into (which should obviously be a square number), and the piece you want a random sample in (numbered left to right, bottom to top, as in your example). square_sample <- function(size = 10, pieces = 4, n = 1) { x_min <- ((n - 1) %% sqrt(pieces)) * size/sqrt(pieces) y_min <- ((n - 1) %/% sqrt(pieces)) * size/sqrt(pieces) c(x = runif(1, x_min, x_min + size/sqrt(pieces)), y = runif(1, y_min, y_min + size/sqrt(pieces))) } Test it out on your example: we should get a point with an x value between 5 and 10, and a y value between 0 and 5: square_sample(size = 10, pieces = 4, n = 2) #> x y #> 5.968655 3.254514 Or pick the middle square of a 150 * 150 square cut into 9 pieces. Here we expect both x and y to be between 50 and 100: square_sample(size = 150, pieces = 9, n = 5) #> x y #> 78.47472 97.32562
You could write a function using three parameters: number of squares you want to go "to the right" (in your picture: 0 for squares 1&3, 2 for squares 2&4) number of squares you want to go up (0 for squares 1&2, 2 for squares 3&4) length of a size of your square using these parameters, you should be able to remodify your code, replace the +0/+5 with parameter * width of the square xx_1 = square_length*runif(1)+right_param * square_length yy_1 = square_length*runif(1)+upwards_param * square_length
x.min = 0 x.max=10 y.min=0 y.max=10 num.random = 100 possible.squares = c(1,2,4,8,16) squares = sample(possible.squares, 1) x.length = x.max/squares y.length = y.max/squares x.coord = seq(from=x.min, to=x.max, by = x.length) y.coord = seq(from=y.min, to=y.max, by = y.length) set.seed(1) loop { n = #<which ever square you want> x.rand = runif (1, min = x.coord[n-1], max = x.coord[n]) y.rand = runif (1, min = y.coord[n-1], max = y.coord[n]) #(x,y) is your coordinate for the random number in the nth square } Does this help?
you can use the absolute real part of a complex number vector, this code will generate any number of points that you want. Npoints = 4 # any multiple of 4 will generate equal number of points in each quarterion x = Re(1i**(1:Npoints)) %>% abs y = Re(1i**(0:(Npoints-1))) %>% abs randoms = lapply(1:(2*Npoints),function(x){ 5*runif(1) })%>% unlist coor.mat =cbind(x + randoms[1:Npoints], y + randoms[(Npoints +1) : (2*Npoints)]) Now coor.mat should be a 2 column matrix where col1 is x, col2 is y, and the number of rows is the number of points you wanted to generate. edit: small correction
Can I give R's approx() a parameter to interpolate by a minimum distance of the interpolated points?
I have spatial data points with x-y-coordinates. They represent the banks of a channel. Now, I need to make this channel bank line to be denser. I want to linearly interpolate the bank points by giving a minimum distance in meters that all interpolated points must have to each other. For example: the blue points are the original points. The red are the interpolated points. They are all at least 1 m apart from each other. The lower right two blue points do not receive an interpolation. How can I achieve this? EDIT Downvoting questions seems to be SO new hobby. What can I do to make you guys happy? My efforts? par = list() par$bank.min.dist = 6 # meters for(i in c(2:length(data$x[ix]))){ # add the actual point banks$x[[bank]] = c(banks$x[[bank]], data$x[ix.l][i-1]) banks$y[[bank]] = c(banks$y[[bank]], data$y[ix.l][i-1]) # calculate distance between consecutive bank points cb_dist = ( ( data$x[ix][i] - data$x[ix][i-1] )^2 + ( data$y[ix][i] - data$y[ix][i-1] )^2 ) ^(1/2) # if distance larger than threshold distance interpolate if(cb_dist > par$bank.min.dist){ # calculate number of artificial points nr_of_pts = ceiling(cb_dist / par$bank.min.dist) ap = ap + nr_of_pts # add artificial points for(ido in c(1: (nr_of_pts-1))){ banks$x[[bank]] = c(banks$x[[bank]], data$x[ix][i-1] + ( ( (data$x[ix][i] - data$x[ix][i-1]) / nr_of_pts ) * ido ) ) banks$y[[bank]] = c(banks$y[[bank]], data$y[ix][i-1] + ( ( (data$y[ix][i] - data$y[ix][i-1]) / nr_of_pts ) * ido ) ) } } } Currently I'm doing it with a loop. Which is slow but works. The suggestion by G5W is working! However, it does not incorporate the blue dots in the orange dots. Of course I can add the blue dots later to the interpolated orange ones. But I need the order of the dots intact for later processing. Since I think G5W is an expert in R, I don't think that there is a one-line-command to do this. Thus, I stick to loop I have. Here is a plot of the timing (I performed 10 runs to do the boxplots): Note: The absolute run time is decreased here, since I set the minimum distance quite high.
Updated to include original points This is basically the same as what you did, but using vectorized functions instead of the loop. See if this works better for you. ## Set up test data x = c(1, 2.5, 5, 8, 8.8) y = c(6.5, 7, 4.2, 0, 0) plot(x,y, pch=21, bg="blue") MinD = 1 ## Get the distances between successive points DM = as.matrix(dist(data.frame(x,y))) Distances = DM[row(DM) == col(DM) + 1] NumPts = ceiling(Distances/MinD) + 1 InterpX = unlist(sapply(1:(length(x)-1), function(i) { PAll = seq(x[i], x[i+1], length.out=NumPts[i]) PAll[-c(length(PAll))] } )) InterpX = c(InterpX, x[length(x)]) InterpY = unlist(sapply(1:(length(x)-1), function(i) { PAll = seq(y[i], y[i+1], length.out=NumPts[i]) PAll[-c(length(PAll))] } )) InterpX = c(InterpX, x[length(x)]) points(InterpX, InterpY , pch=16, col="orange")
3D with value interpolation in R (X, Y, Z, V)
Is there an R package that does X, Y, Z, V interpolation? I see that Akima does X, Y, V but I need one more dimension. Basically I have X,Y,Z coordinates plus the value (V) that I want to interpolate. This is all GIS data but my GIS does not do voxel interpolation So if I have a point cloud of XYZ coordinates with a value of V, how can I interpolate what V would be at XYZ coordinate (15,15,-12) ? Some test data would look like this: X <-rbind(10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50) Y <- rbind(10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50) Z <- rbind(-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29) V <- rbind(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,25,35,75,25,50,0,0,0,0,0,10,12,17,22,27,32,37,25,13,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,125,130,105,110,115,165,180,120,100,80,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
I had the same question and was hoping for an answer in R. My question was: How do I perform 3D (trilinear) interpolation using regular gridded coordinate/value data (x,y,z,v)? For example, CT images, where each image has pixel centers (x, y) and greyscale value (v) and there are multiple image "slices" (z) along the thing being imaged (e.g., head, torso, leg, ...). There is a slight problem with the given example data. # original example data (reformatted) X <- rep( rep( seq(10, 50, by=10), each=25), 3) Y <- rep( rep( seq(10, 50, by=10), each=5), 15) Z <- rep(c(-5, -17, -29), each=125) V <- rbind(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,25,35,75,25,50,0,0,0,0,0,10,12,17,22,27,32,37,25,13,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,125,130,105,110,115,165,180,120,100,80,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) # the dimensions of the 3D grid described do not match the number of values (length(unique(X))*length(unique(Y))*length(unique(Z))) == length(V) ## [1] FALSE ## which makes sense since 75 != 375 # visualize this: library(rgl) plot3d(x=X, y=Y, z=Z, col=terrain.colors(181)[V]) # examine the example data real quick... df <- data.frame(x=X,y=Y,z=Z,v=V); head(df); table(df$x, df$y, df$z); # there are 5 V values at each X,Y,Z coordinate... duplicates! # redefine Z so there are 15 unique values # making 375 unique coordinate points # and matching the length of the given value vector, V df$z <- seq(-5, -29, length.out=15) head(df) table(df$x, df$y, df$z); # there is now 1 V value at each X,Y,Z coordinate # that was for testing, now actually redefine the Z vector. Z <- rep(seq(-5,-29, length.out = 15), 25) # plot it. library(rgl) plot3d(x=X, y=Y, z=Z, col=terrain.colors(181)[V]) I couldn't find any 4D interpolation functions in the usual R packages, so I wrote a quick and dirty one. The following implements (without ANY error checking... caveat emptor!) the technique described at: https://en.wikipedia.org/wiki/Trilinear_interpolation # convenience function #1: # define a function that takes a vector of lookup values and a value to lookup # and returns the two lookup values that the value falls between between = function(vec, value) { # extract list of unique lookup values u = unique(vec) # difference vector dvec = u - value vals = c(u[dvec==max(dvec[dvec<0])], u[dvec==min(dvec[dvec>0])]) return(vals) } # convenience function #2: # return the value (v) from a grid data.frame for given point (x, y, z) get_value = function(df, xi, yi, zi) { # assumes df is data.frame with column names: x, y, z, v subset(df, x==xi & y==yi & z==zi)$v } # inputs df (x,y,z,v), points to look up (x, y, z) interp3 = function(dfin, xin, yin, zin) { # TODO: check if all(xin, yin, zin) equals a grid point, if so just return the point value # TODO: check if any(xin, yin, zin) equals a grid point, if so then do bilinear or linear interp cube_x <- between(dfin$x, xin) cube_y <- between(dfin$y, yin) cube_z <- between(dfin$z, zin) # find the two values in each dimension that the lookup value falls within # and extract the cube of 8 points tmp <- subset(dfin, x %in% cube_x & y %in% cube_y & z %in% cube_z) stopifnot(nrow(tmp)==8) # define points in a periodic and cubic lattice x0 = min(cube_x); x1 = max(cube_x); y0 = min(cube_y); y1 = max(cube_y); z0 = min(cube_z); z1 = max(cube_z); # define differences in each dimension xd = (xin-x0)/(x1-x0); # 0.5 yd = (yin-y0)/(y1-y0); # 0.5 zd = (zin-z0)/(z1-z0); # 0.9166666 # interpolate along x: v00 = get_value(tmp, x0, y0, z0)*(1-xd) + get_value(tmp,x1,y0,z0)*xd # 2.5 v01 = get_value(tmp, x0, y0, z1)*(1-xd) + get_value(tmp,x1,y0,z1)*xd # 0 v10 = get_value(tmp, x0, y1, z0)*(1-xd) + get_value(tmp,x1,y1,z0)*xd # 0 v11 = get_value(tmp, x0, y1, z1)*(1-xd) + get_value(tmp,x1,y1,z1)*xd # 65 # interpolate along y: v0 = v00*(1-yd) + v10*yd # 1.25 v1 = v01*(1-yd) + v11*yd # 32.5 # interpolate along z: return(v0*(1-zd) + v1*zd) # 29.89583 (~91.7% between v0 and v1) } > interp3(df, 15, 15, -12) [1] 29.89583 Testing that same source's assertion that trilinear is simply linear(bilinear(), bilinear()), we can use the base R linear interpolation function, approx(), and the akima package's bilinear interpolation function, interp(), as follows: library(akima) approx(x=c(-11.857143,-13.571429), y=c(interp(x=df[round(df$z,1)==-11.9,"x"], y=df[round(df$z,1)==-11.9,"y"], z=df[round(df$z,1)==-11.9,"v"], xo=15, yo=15)$z, interp(x=df[round(df$z,1)==-13.6,"x"], y=df[round(df$z,1)==-13.6,"y"], z=df[round(df$z,1)==-13.6,"v"], xo=15, yo=15)$z), xout=-12)$y # [1] 0.2083331 Checked another package to triangulate: library(oce) Vmat <- array(data = V, dim = c(length(unique(X)), length(unique(Y)), length(unique(Z)))) approx3d(x=unique(X), y=unique(Y), z=unique(Z), f=Vmat, xout=15, yout=15, zout=-12) [1] 1.666667 So 'oce', 'akima' and my function all give pretty different answers. This is either a mistake in my code somewhere, or due to differences in the underlying Fortran code in the akima interp(), and whatever is in the oce 'approx3d' function that we'll leave for another day. Not sure what the correct answer is because the MWE is not exactly "minimum" or simple. But I tested the functions with some really simple grids and it seems to give 'correct' answers. Here's one simple 2x2x2 example: # really, really simple example: # answer is always the z-coordinate value sdf <- expand.grid(x=seq(0,1),y=seq(0,1),z=seq(0,1)) sdf$v <- rep(seq(0,1), each=4) > interp3(sdf,0.25,0.25,.99) [1] 0.99 > interp3(sdf,0.25,0.25,.4) [1] 0.4 Trying akima on the simple example, we get the same answer (phew!): library(akima) approx(x=unique(sdf$z), y=c(interp(x=sdf[sdf$z==0,"x"], y=sdf[sdf$z==0,"y"], z=sdf[sdf$z==0,"v"], xo=.25, yo=.25)$z, interp(x=sdf[sdf$z==1,"x"], y=sdf[sdf$z==1,"y"], z=sdf[sdf$z==1,"v"], xo=.25, yo=.25)$z), xout=.4)$y # [1] 0.4 The new example data in the OP's own, accepted answer was not possible to interpolate with my simple interp3() function above because: (a) the grid coordinates are not regularly spaced, and (b) the coordinates to lookup (x1, y1, z1) lie outside of the grid. # for completeness, here's the attempt: options(scipen = 999) XCoor=c(78121.6235,78121.6235,78121.6235,78121.6235,78136.723,78136.723,78136.723,78136.8969,78136.8969,78136.8969,78137.4595,78137.4595,78137.4595,78125.061,78125.061,78125.061,78092.4696,78092.4696,78092.4696,78092.7683,78092.7683,78092.7683,78092.7683,78075.1171,78075.1171,78064.7462,78064.7462,78064.7462,78052.771,78052.771,78052.771,78032.1179,78032.1179,78032.1179) YCoor=c(5213642.173,523642.173,523642.173,523642.173,523594.495,523594.495,523594.495,523547.475,523547.475,523547.475,523503.462,523503.462,523503.462,523426.33,523426.33,523426.33,523656.953,523656.953,523656.953,523607.157,523607.157,523607.157,523607.157,523514.671,523514.671,523656.81,523656.81,523656.81,523585.232,523585.232,523585.232,523657.091,523657.091,523657.091) ZCoor=c(-3.0,-5.0,-10.0,-13.0,-3.5,-6.5,-10.5,-3.5,-6.5,-9.5,-3.5,-5.5,-10.5,-3.5,-5.5,-7.5,-3.5,-6.5,-11.5,-3.0,-5.0,-9.0,-12.0,-6.5,-10.5,-2.5,-3.5,-8.0,-3.5,-6.5,-9.5,-2.5,-6.5,-8.5) V=c(2.4000,30.0,620.0,590.0,61.0,480.0,0.3700,0.0,0.3800,0.1600,0.1600,0.9000,0.4100,0.0,0.0,0.0061,6.0,52.0,0.3400,33.0,235.0,350.0,9300.0,31.0,2100.0,0.0,0.0,10.5000,3.8000,0.9000,310.0,0.2800,8.3000,18.0) adf = data.frame(x=XCoor, y=YCoor, z=ZCoor, v=V) # the first y value looks like a typo? > head(adf) x y z v 1 78121.62 5213642.2 -3.0 2.4 2 78121.62 523642.2 -5.0 30.0 3 78121.62 523642.2 -10.0 620.0 4 78121.62 523642.2 -13.0 590.0 5 78136.72 523594.5 -3.5 61.0 6 78136.72 523594.5 -6.5 480.0 x1=198130.000 y1=1913590.000 z1=-8 > interp3(adf, x1,y1,z1) numeric(0) Warning message: In min(dvec[dvec > 0]) : no non-missing arguments to min; returning Inf
Whether the test data did or not make sense, I still needed an algorithm. Test data is just that, something to fiddle with and as a test data it was fine. I wound up programming it in python and the following code takes XYZ V and does a 3D Inverse Distance Weighted (IDW) interpolation where you can set the number of points used in the interpolation. This python recipe only interpolates to one point (x1, y1, z1) but it is easy enough to extend. import numpy as np import math #34 points XCoor=np.array([78121.6235,78121.6235,78121.6235,78121.6235,78136.723,78136.723,78136.723,78136.8969,78136.8969,78136.8969,78137.4595,78137.4595,78137.4595,78125.061,78125.061,78125.061,78092.4696,78092.4696,78092.4696,78092.7683,78092.7683,78092.7683,78092.7683,78075.1171,78075.1171,78064.7462,78064.7462,78064.7462,78052.771,78052.771,78052.771,78032.1179,78032.1179,78032.1179]) YCoor=np.array([5213642.173,523642.173,523642.173,523642.173,523594.495,523594.495,523594.495,523547.475,523547.475,523547.475,523503.462,523503.462,523503.462,523426.33,523426.33,523426.33,523656.953,523656.953,523656.953,523607.157,523607.157,523607.157,523607.157,523514.671,523514.671,523656.81,523656.81,523656.81,523585.232,523585.232,523585.232,523657.091,523657.091,523657.091]) ZCoor=np.array([-3.0,-5.0,-10.0,-13.0,-3.5,-6.5,-10.5,-3.5,-6.5,-9.5,-3.5,-5.5,-10.5,-3.5,-5.5,-7.5,-3.5,-6.5,-11.5,-3.0,-5.0,-9.0,-12.0,-6.5,-10.5,-2.5,-3.5,-8.0,-3.5,-6.5,-9.5,-2.5,-6.5,-8.5]) V=np.array([2.4000,30.0,620.0,590.0,61.0,480.0,0.3700,0.0,0.3800,0.1600,0.1600,0.9000,0.4100,0.0,0.0,0.0061,6.0,52.0,0.3400,33.0,235.0,350.0,9300.0,31.0,2100.0,0.0,0.0,10.5000,3.8000,0.9000,310.0,0.2800,8.3000,18.0]) def Distance(x1,y1,z1, Npoints): i=0 d=[] while i < 33: d.append(math.sqrt((x1-XCoor[i])*(x1-XCoor[i]) + (y1-YCoor[i])*(y1-YCoor[i]) + (z1-ZCoor[i])*(z1-ZCoor[i]) )) i = i + 1 distance=np.array(d) myIndex=distance.argsort()[:Npoints] weightedNum=0 weightedDen=0 for i in myIndex: weightedNum=weightedNum + (V[i]/(distance[i]*distance[i])) weightedDen=weightedDen + (1/(distance[i]*distance[i])) InterpValue=weightedNum/weightedDen return InterpValue x1=198130.000 y1=1913590.000 z1=-8 print(Distance(x1,y1,z1, 12))