问题
I am using plotly via R for the first time and trying to create a surface from a grid and color it based on a calculation.
For example, I would like to use the surface from data(volcano), as in
library(plotly)
plot_ly(z = ~volcano) %>% add_surface()
But instead of color based on the z-value (altitude), let's just say I wanted to color based on distance from my house on the little mesa at (20,60) .
house_loc <- c(20,60,150) # (x,y,z) of my house
dist_to_house <- Vectorize(function(x,y,z){sqrt(sum( (c(x,y,z)-house_loc)^2 ))})
So far I have tried:
color_me <-function(x){
colorRampPalette(c('tan','blue')
)(24L)[findInterval(x,seq(0,1,length.out=25),
all.inside=TRUE)]
}
library(dplyr)
library(reshape2)
volcano %>%
melt( varnames=c('y','x'),value.name='z' ) %>%
mutate( d = dist_to_house(x, y, z) ,
d_rel = d/max(d),
d_color = color_me(d_rel)
) -> df
plot_ly(df,
type='scatter3d',
mode='none', # no markers, just surface
x=~x,
y=~y,
z=~z,
surfaceaxis=2,
surfacecolor=~d_color) # last argument seems not to work
Which just returns:
The desired result would color the landscape tan in the region of the house and gradually fade to blue in the regions far from the house.
Somewhat related question uses mesh3d code found elsewhere and doesn't explain how to calculate (i, j, k)
回答1:
Your code virtually has everything you need, just use a surface plot and use your distance array as the color.
library(plotly)
library(dplyr)
library(reshape2)
house_loc <- c(20,60,150)
dist_to_house <- Vectorize(function(x,y,z){sqrt(sum( (c(x,y,z)-house_loc)^2 ))})
volcano %>%
melt( varnames=c('y','x'),value.name='z' ) %>%
mutate( d = dist_to_house(x, y, z) ,
d_rel = d/max(d)
) -> df
color <- df$d_rel
dim(color) <- dim(volcano)
plot_ly(df,
type='surface',
z=volcano,
surfacecolor=color,
colors=c('tan','blue'))
回答2:
In addition to the surface plot (see accepted answer) we can also do a mesh3d plot and avoid the reshaping (back to grid) step that plot requires.
However, the scale bar still isn't right (showing range of z, not d_rel)
plot_ly(df,
type='mesh3d',
x = ~x,
y = ~y,
z = ~z,
intensity=~d_rel,
colors = colorRamp(c("tan", "blue"))
)
Counter-intuitively, it is intensity= and not color= which seems to control the conditional coloring.
I originally avoided mesh3d because I thought I had to create a triangular mesh (Delaunay something or another) and had no idea how to do that, but it seems to be handled automatically in this case.
来源:https://stackoverflow.com/questions/42963243/use-conditional-coloring-on-a-plotly-surface