Plotly - Different Colours for Different Surfaces

Plotly - different color surfaces

The documentation is a bit cryptic here.

surfacecolor

(list, numpy array, or Pandas series of numbers, strings, or datetimes.)

Sets the surface color values, used for setting a color scale independent of z.

I never managed to put a list of strings, i.e. color values like 'rgb(0.3, 0.5, 0)', or RGB tuples in it.

But you can define your own color scale with the needed colors.

colorscale = [[0, 'rgb' + str(cmap(1)[0:3])], 
[1, 'rgb' + str(cmap(2)[0:3])]]

and then provide a numeric array with the same dimensions as your plotted values.

colors_saddle = np.zeros(shape=saddle.shape)    

All values are set to 0 and will therefore map to the first color in your colorscale. The same for the next color.

In addition you need to set cmax and cmin manually.

Complete code

import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objs as go
import plotly.offline as off

off.init_notebook_mode()

make_int = np.vectorize(int)
cmap = plt.get_cmap("tab10")

saddle = np.array([[x**2-y**2 for x in np.arange(-10,11)] for y in np.arange(-10,11)])
paraboloid = np.array([[x**2 + y**2-100 for x in np.arange(-10,11)] for y in np.arange(-10,11)])

colors_saddle = np.zeros(shape=saddle.shape)
colors_paraboloid = np.ones(shape=paraboloid.shape)

colorscale = [[0, 'rgb' + str(cmap(1)[0:3])],
[1, 'rgb' + str(cmap(2)[0:3])]]

trace_a = go.Surface(z=saddle,
surfacecolor=colors_saddle,
opacity=.7,
name="Trace A",
cmin=0,
cmax=1,
colorscale=colorscale)
trace_b = go.Surface(z=paraboloid,
surfacecolor=colors_paraboloid,
opacity=.7,
name="Trace B",
cmin=0,
cmax=1,
showscale=False,
colorscale=colorscale)

data = [trace_a, trace_b]
off.iplot(data)

Sample Image

plotly - different colours for different surfaces

Sounds trivial but it's a bit tricky in Plotly. The color of a surface plot is either derived from the z values or from an array with the same dimensions as z. This color array only accepts numerical values, no color strings or RGB values.

So let's define an array for our colors

color <- rep(0, length(df1$p1))
dim(color) <- dim(df1$p1)

Next we need to trick Plotly into ignoring the colorscale.

surfacecolor=color,
cauto=F,
cmax=1,
cmin=0

et voilà, we have a uniformely colored plot.

Sample Image


library(plotly)
t1 <- seq(-3, 3, 0.1); t2 <- seq(-3, 3, 0.1)

p1 <- matrix(nrow = length(t1), ncol = length(t2))
p2 <- matrix(nrow = length(t1), ncol = length(t2))

p8a1 <- 1.2
p8a2 <- 1
p8d <- -1
p8b1 <- 0.7
p8b2 <- 0.6

for (i in 1:length(t2)) {
for (j in 1:length(t1)) {
p1[i, j] <- 1 / (1 + exp(-1.7 * (p8a1 * t1[j] + p8a2 * t2[i] + p8d)))
p2[i, j] <- (1 / (1 + exp(-1.7 * p8a1 * (t1[j]- p8b1)))) *
(1 / (1 + exp(-1.7 * p8a2 * (t2[j]- p8b2))))
}
}

df1 <- list(t1, t2, p1)
df2 <- list(t1, t2, p2)

names(df1) <- c("t1", "t2", "p1")
names(df2) <- c("t1", "t2", "p2")
m <- list(l = 10, r = 10, b = 5, t = 0, pad = 3)

color <- rep(0, length(df1$p1))
dim(color) <- dim(df1$p1)
p <- plot_ly(colors = c('red', 'blue')) %>%
add_surface(x = df1$t1,
y = df1$t2,
z = df1$p1,
opacity = 0.8,
#surfacecolor=c('red')
surfacecolor=color,
cauto=F,
cmax=1,
cmin=0
)
color2 <- rep(1, length(df2$p2))
dim(color2) <- dim(df2$p2 )

p <- add_surface(p,
x = df2$t1,
y = df2$t2,
z = df2$p2,
opacity = 1,
surfacecolor=color2,
cauto=F,
cmax=1,
cmin=0)
p

plotly - different color scales for multiple surfaces

I managed to get the result I wanted by using the colorscale argument properly as in the following code:

p6 <- plot_ly(showscale = TRUE) %>%
add_surface(z = ~z1, cmin = min(z1), cmax = max(z2), colorscale = list(c(0,1),c("rgb(255,112,184)","rgb(128,0,64)"))) %>%
add_surface(z = ~z2, cmin = min(z1), cmax = max(z2), colorscale = list(c(0,1),c("rgb(107,184,214)","rgb(0,90,124)"))) %>%
layout(title="SURFACE 1 + SURFACE 2\n(Distinct colorscales as defined)", scene = list(zaxis = list(range = c(min(z1),max(z2)))))

It gives something like this: https://plot.ly/~sebastien1785/20/

However, I was not able to reproduce this result by using the colors argument. Thank you for your help.

Specific colors for multiple surface plot

You can use the argument colorscale, as the following:


fig <- plot_ly(showscale = FALSE)
fig <- fig %>% add_surface(z = ~z, colorscale = list(c(0,1),c("rgb(107,184,255)","rgb(0,90,124)")) )
fig <- fig %>% add_surface(z = ~z2, opacity = 0.98, colorscale = list(c(0,1),c("rgb(255,107,184)","rgb(128,0,64)")))
fig <- fig %>% add_surface(z = ~z3, opacity = 0.98, colorscale = list(c(0,1),c("rgb(107,255,184)","rgb(0,124,90)")))
fig

For each surface, you specify two colors: a darker and a lighter one. The contrast between them correspond to different heights of each surface. The heights of surfaces are scaled between 0 and 1. The pair of colors specified are assigned to respective bottom and top heights of each surface.

You can play with shades of RGB colors to get exactly the colors you want. Check this or this sites for more information on RGB color scales.

Sample Image

My code was adapted from this page, which worked with a very similar example from yours, but only two surfaces.

Based on the same logic, you can expand the number of layers, as requested in the comments:

z4 <- z + 2 
z5 <- z + 3
z6 <- z + 4

fig <- plot_ly(showscale = FALSE)
fig <- fig %>% add_surface(z = ~z, colorscale = list(c(0,1),c("rgb(107,184,255)","rgb(90,90,124)")) )
fig <- fig %>% add_surface(z = ~z2, opacity = 0.98, colorscale = list(c(0,1),c("rgb(255,107,184)","rgb(128,0,64)")))
fig <- fig %>% add_surface(z = ~z3, opacity = 0.98, colorscale = list(c(0,1),c("rgb(107,255,184)","rgb(0,124,90)")))
fig <- fig %>% add_surface(z = ~z4, opacity = 0.98, colorscale = list(c(0,1),c("rgb(182,142,242)","rgb(104,3,255)")))
fig <- fig %>% add_surface(z = ~z5, opacity = 0.98, colorscale = list(c(0,1),c("rgb(247,226,157)","rgb(255,192,7)")))
fig <- fig %>% add_surface(z = ~z6, opacity = 0.98, colorscale = list(c(0,1),c("rgb(129,212,247)","rgb(12,177,247)")))
fig

The above code produced:

Sample Image

Plotly: How to change the colour scheme of a 3D surface plot?

You can easily change the colour scheme through colorscale in

go.Surface(colorscale ='<color>')

Here's an example using colorscale='Blues:

fig = go.Figure(data=[go.Surface(z=z_data.values, colorscale ='Blues')])

Plot

Sample Image

Your colorscale options are (but not necessarily limited to):

Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.

Sample code:

import plotly.graph_objects as go

import pandas as pd

# Read data from a csv
z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')

fig = go.Figure(data=[go.Surface(z=z_data.values, colorscale ='Blues')])

fig.update_layout(title='Mt Bruno Elevation', autosize=False,
width=500, height=500,
margin=dict(l=65, r=50, b=65, t=90))
f = fig.full_figure_for_development(warn=False)

fig.show()

Color surface by variable with plotly in R

You will need to set correct values for cmin and cmax:

library(plotly)

MatrixForColor <- matrix(1, nrow = nrow(volcano), ncol = ncol(volcano))
MatrixForColor[, 1:30] <- 2

plot_ly(colors = c('blue', 'red')) %>%
add_surface(z = volcano,
opacity = 0.8,
surfacecolor = MatrixForColor,
cauto=F,
cmax=max(MatrixForColor),
cmin=min(MatrixForColor)
)

Sample Image

Legend for multi surface plot with specific colors

Are you looking for this:

  plot_ly(showscale = FALSE, showlegend = TRUE) %>% 
add_surface(z = ~z, name = "Color Blue", colorscale = list(c(0,1),c("rgb(107,184,255)","rgb(0,90,124)"))) %>%
add_surface(z = ~z2, name = "Color Red", colorscale = list(c(0,1),c("rgb(255,107,184)","rgb(128,0,64)")))

Sample Image



Related Topics



Leave a reply



Submit