3D Surface Interpolation - r

I have a three column data frame with latitude, longitude, and underground measurements as the columns. I am trying to figure out how to interpolate data points between the points I have (which are irregularly space) and then create a smooth surface plot of the entire area. I have tried to use the 'surface3d' function in the 'rgl' package but my result looks like a single giant spike. I have been able to plot the data with 'plot3d' but I need to take it a step further and fill in the blank spaces with interpolation. Any ideas or suggestions? I'm also open to using other packages, the rgl just seemed like the best fit at the time.
EDIT: here's an excerpt from my data (measurements of aquifer depth) :
lat_dd_NAD83 long_dd_NAD83 lev_va_ft
1 37.01030 -101.5006 288.49
2 37.03977 -101.6633 191.68
3 37.05201 -100.4994 159.34
4 37.06567 -101.3292 174.07
5 37.06947 -101.4561 285.08
6 37.10098 -102.0134 128.94

Just to add small but (maybe) important note about interpolation.
Using very nice package "akima" you can easily interpolate your data:
library(akima)
library(rgl)
# library(deldir)
# Create some fake data
x <- rnorm(100)
y <- rnorm(100)
z <- x^2 + y^2
# # Triangulate it in x and y
# del <- deldir(x, y, z = z)
# triangs <- do.call(rbind, triang.list(del))
#
# # Plot the resulting surface
# plot3d(x, y, z, type = "n")
# triangles3d(triangs[, c("x", "y", "z")], col = "gray")
n_interpolation <- 200
spline_interpolated <- interp(x, y, z,
xo=seq(min(x), max(x), length = n_interpolation),
yo=seq(min(y), max(y), length = n_interpolation),
linear = FALSE, extrap = TRUE)
x.si <- spline_interpolated$x
y.si <- spline_interpolated$y
z.si <- spline_interpolated$z
persp3d(x.si, y.si, z.si, col = "gray")
Spline - interpolated picture (200 steps)
With this package you can easily change amount of steps of interpolation, etc. You will need at least 10 (the more the better) points to get a reasonable spline interpolation with this package. Linear version works well regardless amount of points.
P.S. Thanks for user 2554330 - didn't knew about deldir, really useful thing in some cases.

You could use the deldir package to get a Delaunay triangulation of your points, then convert it to the form of data required by triangles3d for plotting. I don't know how effective this would be on a really large dataset, but it seems to work on 100 points:
library(deldir)
library(rgl)
# Create some fake data
x <- rnorm(100)
y <- rnorm(100)
z <- x^2 + y^2
# Triangulate it in x and y
del <- deldir(x, y, z = z)
triangs <- do.call(rbind, triang.list(del))
# Plot the resulting surface
plot3d(x, y, z, type = "n")
triangles3d(triangs[, c("x", "y", "z")], col = "gray")
EDITED to add:
The version of rgl on R-forge now has a function to make this easy. You can now produce a plot similar to the one above using
library(deldir)
library(rgl)
plot3d(deldir(x, y, z = z))
There is also a function to construct mesh3d objects from the deldir() output.

Related

3D plot from model in R plotly?

Is it possible to generate a 3D plot from models using plotly? I tried to search over the internet, but many examples are based on the infamous volcano dataset that generates a plot from a matrix of points.
My two models are:
y = 0.49867x - 4.78577
y = 76.13084x + 4.81945
If not possible, how can i transform my data into the matrix format such as that in the volcano dataset? For more details, I have hosted the data file here. I have never used plotly before and i'm unfamiliar with the grammar, but i think i can manage if i can at least format my data into the likes of the volcano dataset.
Thank you.
To plot a surface with plotly, you need to construct a numeric matrix.
Taking Himmelblau's function as a test:
f <- function(x, y) { (x^2+y-11)^2 + (x+y^2-7)^2 }
Create x and y values:
x <- seq(-6, 6, length = 100)
y <- x
Then, create z with outer function. It will return a matrix.
z <- outer(x, y, f)
We can now create a surface plot:
library(plotly)
plot_ly(x = x, y = y, z = ~z) %>% add_surface()

How to plot an indexed set of (x,y) pairs such that the index is parallel to the x axis, but on the top of the frame

For example, let say:
x <- rnorm(20)
y <- rnorm(20) + 1
n <- seq(1,20,1)
data <- data.frame(n, x, y)
Is it possible to plot y~x with the indexed value of each pair at the top of the plot?
Can it be done with the base graphics, not ggplot?
It may be simple, but I am struggling to find help via Google. My guess is I'm using a poor selection of words.
Any help is much appreciated!
plot(x,y)
text(x = x, y = y, n, pos = 3)
#Adds text 'n' at co-ordinate (x,y)
# "pos = 3" means the text will be just above the co-ordinates
#See ?text for more
If you wanted to plot all the indices on a same line above the plot boundary, you can specify the appropriate value for y when using text. However, you will first have to pass par(xpd=TRUE) to be able to draw outside plot boundary
Yes we can add label. Try this code:
x <- rnorm(20)
y <- rnorm(20) + 1
n <- seq(1,20,1)
data <- data.frame(n, x, y)
plot(y~x)
with(data, text(y~x, labels = row.names(data)))

How to use persp() with R to graph three variables in a dataset

I have this data:
wine <-read.table("http://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data",sep=",")
attach(wine)
and I am trying to prompt a 3D plot of the variables V2, V3 and V4 with the persp() function
I get this error:
Error in persp.default(v2, v3, v4) :
increasing 'x' and 'y' values expected
Although I already sorted each variable with the sort() function.
How should I proceed?
As per Zheyuan's reply, persp is not the rigth choice for scatterplot in 3d, you can use rgl instead with your wine data:
library(rgl)
plot3d(wine$V1, wine$V2, wine$V3, type='s', size=2, col=wine$V1)
This is some conceptual mistake. persp is used for surface plot, but your data only support scatter plot.
For a surface plot, we need the surface values on a grid expanded by x, y. In other words, we are plotting a 2D function f(x, y) on a grid: expand.grid(x = sort(x), y = sort(y)). We need to know this function f and (in almost all situation) use outer to evaluate it on such grid. Consider this example:
x <- seq(-10, 10, length = 30) ## already in increasing order
y <- x ## already in increasing order
f <- function(x, y) {r <- sqrt(x ^ 2 + y ^ 2); 10 * sin(r) / r}
z <- outer(x, y, f) ## evaluation on grid; obtain a matrix `z`
persp(x, y, z)
Scatter plot on the other hand, is only restricted to (x, y):
library(scatterplot3d)
scatterplot3d(V2, V3, V4) ## your `wine` data

Plotting z as a color with R on a rGoogleMap

I have a function and I want to plot only x and y. z should be represented as a color. Is there a package that does the work for me ?
f = function(a,b){
dnorm(a^2+b^2)
}
x = seq(-2, 2, 0.1)
y = seq(-2, 2, 0.1)
z = outer(x, y, f)
persp(x, y, z)
I want to plot this function on a map generated with rGoogleMaps. Maybe there is a more specific package for this use?
Something like this?
library(ggmap) # loads ggplot2 as well
library(RgoogleMaps) # for getGeoCode
london.center <- getGeoCode("London")
london <- get_map("London", zoom=12)
x <- seq(-2,2,0.1)
df <- expand.grid(x=x,y=x)
df$z <- with(df,f(x,y))
df$x <- london.center[2]+df$x/20
df$y <- london.center[1]+df$y/20
ggp <- ggmap(london)+
geom_tile(data=df,aes(x=x,y=y,fill=z), alpha=0.2)+
scale_fill_gradientn(guide="none",colours=rev(heat.colors(10)))+
stat_contour(data=df, aes(x=x, y=y, z=z, color=..level..), geom="path", size=1)+
scale_color_gradientn(colours=rev(heat.colors(10)))
plot(ggp)
This solution uses ggplot. Perhaps someone else will show you how to do this using RgoogleMaps.
Basically, we load the map, using get_map(...) (which is just a wrapper for GetMap(...) in the RgoogleMaps package).
Then we create the sample data frame df, which contains three columns, x, y, and z, and one row for every combination of x and y (this is the format required by ggplot).
Then we create the map layers. First the map itself, using ggmap(...); then a layer of tiles "filled" based on the value of z, using geom_tile(...); then a set of contour lines colored using the value of z, using stat_contour(geom="path",...). The rest of the code sets the fill and line colors and renders the map.
Purists will tell you that you can render the filled contours directly using stat_contour(geom="polygon",...), instead of using tiles, but this has the unfortunate effect of clipping any contours not completely enclosed in the plot area.

RGL surface plot from data frame

I've created a nice plot using scatter3d() and Rcmdr. That plot contains two nice surface smooths. Now I'd like to add to this plot one more surface, the truth (i.e. the surface defined by the function generating my observations minus the noise component).
Here is my code so far:
library(car)
set.seed(1)
n <- 200 # number of observations (x,y,z) to be generated
sd <- 0.3 # standard deviation for error term
x <- runif(n) # generate x component
y <- runif(n) # generate y component
r <- sqrt(x^2+y^2) # used to compute z values
z_t <- sin(x^2+3*y^2)/(0.1+r^2) + (x^2+5*y^2)*exp(1-r^2)/2 # calculate values of true regression function
z <- z_t + rnorm(n, sd = sd) # overlay normally distrbuted 'noise'
dm <- data.frame(x=x, y=y, z=z) # data frame containing (x,y,z) observations
dm_t <- data.frame(x=x,y=y, z=z_t) # data frame containing (x,y) observations and the corresponding value of the *true* regression function
# Create 3D scatterplot of:
# - Observations (this includes 'noise')
# - Surface given by Additive Model fit
# - Surface given by bivariate smoother fit
scatter3d(dm$x, dm$y, dm$z, fit=c("smooth","additive"), bg="white",
axis.scales=TRUE, grid=TRUE, ellipsoid=FALSE, xlab="x", ylab="z", zlab="y")
The solution given in another thread is to then define a function:
my_surface <- function(f, n=10, ...) {
ranges <- rgl:::.getRanges()
x <- seq(ranges$xlim[1], ranges$xlim[2], length=n)
y <- seq(ranges$ylim[1], ranges$ylim[2], length=n)
z <- outer(x,y,f)
surface3d(x, y, z, ...)
}
f <- function(x, y)
sin(x^2+3*y^2)/(0.1+r^2) + (x^2+5*y^2)*exp(1-r^2)/2
my_surface(f, alpha=0.2)
This however yields an error, saying (translated from German since this is my system language, I apologize):
Error in outer(x, y, f) :
Dimension [Product 100] does not match the length of the object [200]
I then tried an alternative approach:
x <- seq(0,1,length=20)
y <- x
z <- outer(x,y,f)
surface3d(x,y,z)
This does add a surface to my plot but it doesn't look right at all (i.e. the observations are not even close to it). Here's what the supposed true surface looks like (this is obviously wrong):
Thanks!
I think the problem may in fact be scaling. Here I created a couple of points that sit on the plane z = x+y. Then I proceeded to try to plot that plane using my method above:
library(car)
n <- 50
x <- runif(n)
y <- runif(n)
z <- x+y
scatter3d(x,y,z, surface = FALSE)
f <- function(x,y)
x + y
x_grid <- seq(0,1, length=20)
y_grid <- x_grid
z_grid <- outer(x_grid, y_grid, f)
surface3d(x_grid, y_grid, z_grid)
This gives me the following plot:
Maybe one of you can help me out with this?
The scatter3d function in car rescales data before plotting it, which makes it incompatible with essentially all rgl plotting functions, including surface3d.
You can get a plot something like what you want by using all rgl functions, e.g. plot3d(x, y, z) in place of scatter3d, but of course it will have rgl-style axes rather than car-style axes.

Resources