Plot 3D data in R

R_User picture R_User · Jul 16, 2011 · Viewed 143.1k times · Source

I have a 3D dataset:

data = data.frame(
    x = rep( c(0.1, 0.2, 0.3, 0.4, 0.5), each=5),
    y = rep( c(1, 2, 3, 4, 5), 5)
)

data$z = runif(
    25,
    min = (data$x*data$y - 0.1 * (data$x*data$y)),
    max = (data$x*data$y + 0.1 * (data$x*data$y))
)

data
str(data)

And I want to plot it, but the built-in-functions of R alwyas give the error

increasing 'x' and 'y' values expected

# ### 3D Plots ######################################################
# built-in function always give the error
#    "increasing 'x' and 'y' values expected"
demo(image)
image(x = data$x, y = data$y, z = data$z)

demo(persp)
persp(data$x,data$y,data$z)

contour(data$x,data$y,data$z)

When I searched on the internet, I found that this message happens when combinations of X and Y values are not unique. But here they are unique.

I tried some other libraries and there it works without problems. But I don't like the default style of the plots (the built-in functions should fulfill my expectations).

# ### 3D Scatterplot ######################################################
# Nice plots without surface maps?
install.packages("scatterplot3d", dependencies = TRUE)
library(scatterplot3d)
scatterplot3d(x = data$x, y = data$y, z = data$z)

# ### 3D Scatterplot ######################################################
# Only to play around?
install.packages("rgl", dependencies = TRUE)
library(rgl)
plot3d(x = data$x, y = data$y, z = data$z)
lines3d(x = data$x, y = data$y, z = data$z)
surface3d(x = data$x, y = data$y, z = data$z)

Why are my datasets not accepted by the built-in functions?

Answer

Backlin picture Backlin · Jul 17, 2011

I use the lattice package for almost everything I plot in R and it has a corresponing plot to persp called wireframe. Let data be the way Sven defined it.

wireframe(z ~ x * y, data=data)

wireframe plot

Or how about this (modification of fig 6.3 in Deepanyan Sarkar's book):

p <- wireframe(z ~ x * y, data=data)
npanel <- c(4, 2)
rotx <- c(-50, -80)
rotz <- seq(30, 300, length = npanel[1]+1)
update(p[rep(1, prod(npanel))], layout = npanel,
    panel = function(..., screen) {
        panel.wireframe(..., screen = list(z = rotz[current.column()],
                                           x = rotx[current.row()]))
    })

Multiple wireframe plots using panel and update

Update: Plotting surfaces with OpenGL

Since this post continues to draw attention I want to add the OpenGL way to make 3-d plots too (as suggested by @tucson below). First we need to reformat the dataset from xyz-tripplets to axis vectors x and y and a matrix z.

x <- 1:5/10
y <- 1:5
z <- x %o% y
z <- z + .2*z*runif(25) - .1*z

library(rgl)
persp3d(x, y, z, col="skyblue")

rgl::persp3d

This image can be freely rotated and scaled using the mouse, or modified with additional commands, and when you are happy with it you save it using rgl.snapshot.

rgl.snapshot("myplot.png")