AW27
AW27

Reputation: 505

How to create surface plot in R

I'm currently trying to develop a surface plot that examines the results of the below data frame. I want to plot the increasing values of noise on the x-axis and the increasing values of mu on the y-axis, with the point estimate values on the z-axis. After looking at ggplot2 and ggplotly, it's not clear how I would plot each of these columns in surface or 3D plot.

 df <-  "mu noise0    noise1     noise2     noise3    noise4    noise5
    1      1  0.000000  0.9549526  0.8908646  0.919630  1.034607
    2      2  1.952901  1.9622004  2.0317115  1.919011  1.645479
    3      3  2.997467  0.5292921  2.8592976  3.034377  3.014647
    4      4  3.998339  4.0042379  3.9938346  4.013196  3.977212
    5      5  5.001337  4.9939060  4.9917115  4.997186  5.009082
    6      6  6.001987  5.9929932  5.9882173  6.015318  6.007156
    7      7  6.997924  6.9962483  7.0118066  6.182577  7.009172
    8      8  8.000022  7.9981131  8.0010066  8.005220  8.024569
    9      9  9.004437  9.0066182  8.9667536  8.978415  8.988935
   10     10 10.006595  9.9987245  9.9949733  9.993018 10.000646"

Thanks in advance.

Upvotes: 2

Views: 4109

Answers (2)

chemdork123
chemdork123

Reputation: 13803

Here's one way using geom_tile(). First, you will want to get your data frame into more of a Tidy format, where the goal is to have columns:

  • mu: nothing changes here
  • noise: need to combine your "noise0", "noise1", ... columns together, and
  • z: serves as the value of the noise and we will apply the fill= aesthetic using this column.

To do that, I'm using dplyr and gather(), but there are other ways (melt(), or pivot_longer() gets you that too). I'm also adding some code to pull out just the number portion of the "noise" columns and then reformatting that as an integer to ensure that you have x and y axes as numeric/integers:

# assumes that df is your data as data.frame
df <- df %>% gather(key="noise", value="z", -mu)
    df <- df %>% separate(col = "noise", into=c('x', "noise"), sep=5) %>% select(-x)
    df$noise <- as.integer(df$noise)

Here's an example of how you could plot it, but aesthetics are up to you. I decided to also include geom_text() to show the actual values of df$z so that we can see better what's going on. Also, I'm using rainbow because "it's pretty" - you may want to choose a more appropriate quantitative comparison scale from the RColorBrewer package.

ggplot(df, aes(x=noise, y=mu, fill=z)) + theme_bw() +
    geom_tile() +
    geom_text(aes(label=round(z, 2))) +
    scale_fill_gradientn(colors = rainbow(5))

enter image description here

EDIT: To answer OP's follow up, yes, you can also showcase this via plotly. Here's a direct transition:

p <- plot_ly(
    df, x= ~noise, y= ~mu, z= ~z,
    type='mesh3d', intensity = ~z,
    colors= colorRamp(rainbow(5))
  )
p

Static image here:

enter image description here

A much more informative way to show this particular set of information is to see the variation of df$z as it relates to df$mu by creating df$delta_z and then using that to plot. (you can also plot via ggplot() + geom_tile() as above):

df$delta_z <- df$z - df$mu

p1 <- plot_ly(
    df, x= ~noise, y= ~mu, z= ~delta_z,
    type='mesh3d', intensity = ~delta_z,
    colors= colorRamp(rainbow(5))
  )

Giving you this (static image here):

enter image description here

Upvotes: 5

mattek
mattek

Reputation: 942

ggplot accepts data in the long format, which means that you need to melt your dataset using, for example, a function from the reshape2 package:

dfLong = melt(df, 
              id.vars = "mu", 
              variable.name = "noise", 
              value.name = "meas")

The resulting column noise contains entries such as noise0, noise1, etc. You can extract the numbers and convert to a numeric column:

dfLong$noise = with(dfLong, as.numeric(gsub("noise", "", noise)))

This converts your data to:

   mu noise       meas
1   1     0  1.0000000
2   2     0  2.0000000
3   3     0  3.0000000
...

As per ggplot documentation:

ggplot2 can not draw true 3D surfaces, but you can use geom_contour(), geom_contour_filled(), and geom_tile() to visualise 3D surfaces in 2D.

So, for example:

ggplot(dfLong,
   aes(x = noise
       y = mu, 
       fill = meas)) +
geom_tile() +
scale_fill_gradientn(colours = terrain.colors(10))

Produces: result plot

Upvotes: 1

Related Questions