Displaying a discrete, not continuous colorscale bar in scatter3d plot when mapping color to integer stand-ins for factor levels
Closed this issue · 3 comments
Issue Description:
When using the plot_ly function to create a 3D scatter plot (scatter3d), I am trying to apply a specific discrete color scale to markers based on a factor column (with each unique value in the column representing a different color). I have explored a lot of options (including trying ~20 chatGPT suggestions), but encounter two situations:
- If I map the colors to character or factor data, the colorscale is ignored, whether I define it by scale name ("Viridis") or as a list.
- If I map the colors to integer values matched to factor levels, the correct colors are displayed, but the color scale bar is shown as continuous. I have found ways to display the proper tick marks mapped to my levels, but not to change the type of scale bar displayed from continuous to discrete.
Expected Behavior:
There are two abnormal behaviors here:
- One would expect it to be possible to control the color scale when plotting character/factor data. Instead, the color scale is ignored.
- If plotting integer data (as a workaround to the first problem), it should be possible to force a discrete color scale, such that a discrete color bad is also displayed.
Example Code:
library(plotly)
library(viridis)
# Sample data
scoresA <- data.frame(
X = rnorm(100),
Y = rnorm(100),
Z = rnorm(100),
Colour = sample(1:5, 100, replace = TRUE),
Label = sample(letters, 100, replace = TRUE)
)
scoresA$ColourFactor <- factor(scoresA$Colour)
numLev <- length(unique(scoresA$Colour))
myCols <- viridis_pal()(numLev)
colscale <- lapply(1:numLev, function(x) {
list((x-1)/(numLev-1), myCols[x])
})
scoresA$ColorMapped <- myCols[match(scoresA$ColourFactor, levels(scoresA$ColourFactor))]
plot_lyPSMsPCA <- plot_ly(scoresA, x = ~X, y = ~Y, z = ~Z,
type = "scatter3d", mode = "markers",
marker = list(color = ~as.numeric(ColourFactor),
colorscale = colscale,
showscale = TRUE,
colorbar = list(
tickvals = seq(1, numLev),
ticktext = levels(scoresA$ColourFactor),
tickmode = "array",
ticklen = numLev
)))
I am not sure I completely understood your goal. Does the below work for you?
library(plotly)
# Sample data
scoresA <- data.frame(
X = rnorm(100),
Y = rnorm(100),
Z = rnorm(100),
Colour = sample(1:5, 100, replace = TRUE),
Label = sample(letters, 100, replace = TRUE)
)
scoresA$ColourFactor <- factor(scoresA$Colour)
plot_ly(scoresA, x = ~X, y = ~Y, z = ~Z, color=~ColourFactor, colors="viridis",
type = "scatter3d", mode = "markers")

Dear romanzenka, your solution does indeed work for me. I am confused now, because I feel pretty certain that I tried it the other day... Well, do I feel ridiculous now... Anyway, thank you for taking the time to help.
@Arthfael That is cool, more eyes see more, and maybe with this resolution here, even ChatGPT will be more useful in the future!
Feel free to close the issue!