plotly/plotly.R

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")
Image

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!