Search code examples
rplotsurfacergl

Creating a 3D surface plot from two vectors and a matrix


I have got two vectors and a 2D-matrix, from which I want to create a 3D surface plot. I already have split my data into X and Y (vectors (time "t" and wavelength "w") and Z (matrix; absorbance "NIR" at time and wavelength) with the same number of rows/columns respectively:

t = matrix(1:456, ncol= 1)
w = matrix(1350:1650, nrow = 1)
NIR = as.matrix(read.table("NIR_alle_pur.txt", header = TRUE, dec =","))
colnames(NIR) = c(paste0("NIR.", 1350:1650))
dim(NIR)
# [1] 456 301
dput(NIR_example)
structure(c(60771.93, 57230.56, 56235.96, 41617.47, 41709.93, 
57466.6, 59916.97, 63376.4, 41966.73, 41254.34, 65535, 61468.76, 
65535, 41238.03, 42530.97, 56936.03, 65009.4, 65535, 40375.5, 
41021.6, 62757, 65455.44, 63795.6, 41349.6, 41178.2), .Dim = c(5L, 
5L), .Dimnames = list(NULL, c("NIR.Spectrum_1350.0000000", "NIR.Spectrum_1351.0000000", 
"NIR.Spectrum_1352.0000000", "NIR.Spectrum_1353.0000000", "NIR.Spectrum_1354.0000000"
)))

I tried to insert those into the rgl.surface function, but I get the following error message:

Error in rgl.surface(x, y, z, coords = 1:3) : Bad dimension for rows

I've also tried to plot them with plotly, but my success was equally low.

Can someone give me an input how I can get my spectral data to look like the last ones (multiple surfaces) on this site, individually? I'll try the overlay of the surfaces with plotlylater on!

I am happy for every extra input and information on my level! Thank you!


Solution

  • After looking at the source code, I'd guess the problem is that you stored your x and y vectors as matrices. If they are matrices, they need to be identical in shape to z.

    As I mentioned in a comment, you should avoid using rgl.surface (and the other rgl.* functions in most cases), and use surface3d instead, or persp3d if you want axes.

    The *3d functions are higher level functions that act more like other R functions, and they will lead to fewer problems in the long run.

    You haven't posted any data, so I'll post a completely artificial example. Let's suppose z = x^2 + y^2 + a, where a is a different constant for each surface. Then you can plot it like this:

    x <- seq(-2, 2, length = 7) 
    y <- seq(-3, 3, length = 5)  # I've chosen different ranges 
                                 # and lengths just to illustrate.
    z <- outer(x, y, function(x, y) x^2 + y^2)
    
    colours <- heat.colors(100)
    minval <- min(z)
    maxval <- max(z) + 10
    col <- colours[(z - minval)/(maxval - minval)*99 + 1]
    persp3d(x, y, z, col = col)  # get axes the first time
    
    z <- outer(x, y, function(x, y) x^2 + y^2 + 5)
    col <- colours[(z - minval)/(maxval - minval)*99 + 1]
    surface3d(x, y, z, col = col)  
    
    z <- outer(x, y, function(x, y) x^2 + y^2 + 10)
    col <- colours[(z - minval)/(maxval - minval)*99 + 1]
    surface3d(x, y, z, col = col) 
    
    aspect3d(1, 1, 1)  # Make axes all equal
    

    That produces this plot:

    enter image description here