3D plot from model in R plotly? - r

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()

Related

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)))

3D Surface Interpolation

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.

Combining two plot in R

I wish to compare the observed values to the fitted ones. To do so, I decided to use a plot in R. What I want to do is to plot X vs Y and X vs Y.fitted on the same plot. I have written some code, but it is incomplete. My plot needs to look like this one below. On the plot, circles and crosses represent the observed and fitted values respectively
set.seed(1)
x <- runif(8,0,1)
y <- runif(8,0,1)
y.fitted <- runif(8,0,1)
plot(x,y,pch=1)
plot(x,y.fitted,pch=5)
In your code, the second plot will not add points to the existing plot but create a new one. You can + use the function points to add points to the existing plot.
plot(x, y, pch = 1)
points(x, y.fitted, pch = 4)
running plot the second time will create a new one. You could use points
set.seed(1)
x <- runif(8,0,1)
y <- runif(8,0,1)
y.fitted <- runif(8,0,1)
plot(x,y,pch=1)
points(x,y.fitted,pch=5)
A solution with ggplot2 giving a better and neat graph outlook:
library(ggplot2)
df = data.frame(x=runif(8,0,1),y=runif(8,0,1),y.fitted=runif(8,0,1))
df = melt(df, id=c('x'))
ggplot() + geom_point(aes(x=x,y=value, shape=variable, colour=variable), df)

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.

How do I plot the first derivative of the smoothing function?

I have the following script that emulates the type of data structure I have and analysis that I want to do on it,
library(ggplot2)
library(reshape2)
n <- 10
df <- data.frame(t=seq(n)*0.1, a =sort(rnorm(n)), b =sort(rnorm(n)),
a.1=sort(rnorm(n)), b.1=sort(rnorm(n)),
a.2=sort(rnorm(n)), b.2=sort(rnorm(n)))
head(df)
mdf <- melt(df, id=c('t'))
## head(mdf)
levels(mdf$variable) <- rep(c('a','b'),3)
g <- ggplot(mdf,aes(t,value,group=variable,colour=variable))
g +
stat_smooth(method='lm', formula = y ~ ns(x,3)) +
geom_point() +
facet_wrap(~variable) +
opts()
What I would like to do in addition to this is plot the first derivative of the smoothing function against t and against the factors, c('a','b'), as well. Any suggestions how to go about this would be greatly appreciated.
You'll have to construct the derivative yourself, and there are two possible ways for that. Let me illustrate by using only one group :
require(splines) #thx #Chase for the notice
lmdf <- mdf[mdf$variable=="b",]
model <- lm(value~ns(t,3),data=lmdf)
You then simply define your derivative as diff(Y)/diff(X) based on your predicted values, as you would do for differentiation of a discrete function. It's a very good approximation if you take enough X points.
X <- data.frame(t=seq(0.1,1.0,length=100) ) # make an ordered sequence
Y <- predict(model,newdata=X) # calculate predictions for that sequence
plot(X$t,Y,type="l",main="Original fit") #check
dY <- diff(Y)/diff(X$t) # the derivative of your function
dX <- rowMeans(embed(X$t,2)) # centers the X values for plotting
plot(dX,dY,type="l",main="Derivative") #check
As you can see, this way you obtain the points for plotting the derivative. You'll figure out from here how to apply this to both levels and combine those points to the plot you like. Below the plots from this sample code :
Here's one approach to plotting this with ggplot. There may be a more efficient way to do it, but this uses the manual calculations done by #Joris. We'll simply construct a long data.frame with all of the X and Y values while also supplying a variable to "facet" the plots:
require(ggplot2)
originalData <- data.frame(X = X$t, Y, type = "Original")
derivativeData <- data.frame(X = dX, Y = dY, type = "Derivative")
plotData <- rbind(originalData, derivativeData)
ggplot(plotData, aes(X,Y)) +
geom_line() +
facet_wrap(~type, scales = "free_y")
If data is smoothed using smooth.spline, the derivative of predicted data can be specified using the argument deriv in predict. Following from #Joris's solution
lmdf <- mdf[mdf$variable == "b",]
model <- smooth.spline(x = lmdf$t, y = lmdf$value)
Y <- predict(model, x = seq(0.1,1.0,length=100), deriv = 1) # first derivative
plot(Y$x[, 1], Y$y[, 1], type = 'l')
Any dissimilarity in the output is most likely due to differences in the smoothing.

Resources