2
votes

I have this code: (I found somewhere on stackoverflow, just using it to illustrate my idea.)

x<-runif(20) 
y<-runif(20) 
z<-runif(20, -5,5) 

library(rgl) 
plot3d(x,y,z)

fit <- lm(z ~ poly(x,2) +poly(y,2) + x:y )

xnew <- seq(min(x), max(x), len=20) 
ynew <- seq(min(y), max(y), len=20) 
df <- expand.grid(x = xnew, 
                  y = ynew) 

df$z <- predict(fit, newdata=df)

surface3d(xnew, ynew, df$z, col="red")

enter image description here

My question is, how do I make the color a gradient of greens above zero, and reds below zero? Am I being clear?

I found this post: Formatting of persp3d plot

Which helps do the gradient, but I cannot figure out how to make it dependent on the positive/negative values of z, and how to restrict it to green and red, respectively.

Thanks for any help!

EDIT:

I tried to do some things below, adding this:

colors<-rep("red", length(df$z))
colors[(df$z > 0)] <- colorRampPalette(c("yellow","green"))(sum(df$z > 0))
colors[(df$z < 0)] <- colorRampPalette(c("red","yellow"))(sum(df$z < 0))

surface3d(xnew, ynew, df$z, col=colors)

Which produced this:

enter image description here

It looks like the gradient is along the wrong axis. It should be yellow near z = 0, getting more red aas z gets more negative, and more green as z gets more positive. But it is not doing that, applying the gradient (seemingly) along the y-axis.

Thoughts?

3

3 Answers

1
votes

You can use the values of z tested against 0 to return a vector of 1's and 0's (and then coercing to 2's and 1's with a 1+-operation):

surface3d(xnew, ynew, df$z, col=c("red","blue")[1+(df$z > 0)] )
rgl.snapshot("zerocol.png",top=FALSE)

You do see a fair amount of jagginess at the color threshold because your grid is rather coarse. I see that I used the colors in a manner slightly different than requested, but the principle is to put the color name in the order of FALSE-z first and TRUE-z second (1+TRUE == 2). Cool saddle fit on my random draw.

enter image description here

0
votes

Here is a similar method which will highlight green>0 and red<0:

colors<-rep("red", length(df$z))
colors[(df$z>0.0)]<-"green"
surface3d(xnew, ynew, df$z, col=colors)

Define a array with the base color, change the color of the elements >0 and then plot.

Edit: To add a gradient check out the code sample for the rgl.surface command. This might be something closer to what you are looking for:

zlenpos<- max(df$z)+1
zlenneg<- 0-min(df$z)
colorscale<-rainbow(zlenpos+zlenneg, start = 0, end = 2/6)
colors<-colorscale[df$z - min(df$z) + 1]
surface3d(xnew, ynew, df$z, col=colors)

This performs the gradient from red to yellow to green but the yellow close to the zero of z. I'll leave this up to others to refine the details.

0
votes

If your surface is specified as a function of x and y rather than a matrix, you can use the persp3d.function method: it will automatically work out the appropriate colours based on z values. See the first example in ?persp3d.function.

If your z is a matrix, you'll have to duplicate what persp3d.function does. You can see the source using rgl:::persp3d.function.

Generally for high level things like this you'll want to use persp3d; surface3d does the low level plotting of the surface.