rplotly

Determine color used by plotly when color is mapped


I can use the colors argument to provide a color palette for my plotly chart like this:

library(plotly)
library(viridis)

col_pal <- viridis(10)

plot_ly(mtcars) %>%
  add_trace(x = ~ mpg, y = ~ cyl, color = ~ factor(cyl), colors = col_pal,
            type = "scatter", mode = "markers", marker = list(size = 25))

This will produce the following plot: Plotly Scatterplot with three lines of differntly colored points

Using the following JavaScript I can determine which colors are effectively used:

const cols = $('path.point').map(function() {
  const style = $(this).attr('style');
  const fill_col = style.match(/fill: [^;]+/g)[0].replace('fill: ', '');
  const hex = '#' + fill_col
    .slice(4, -1)
    .split(',')
    .map(x => (+x).toString(16).padStart(2, 0))
    .join('');
  return hex;
})

new Set(cols)
// Set(3) {'#440154', '#25908c', '#fde725'}

If I compare that to col_pal:

col_pal
# [1] "#440154FF" "#482878FF" "#3E4A89FF" "#31688EFF" "#26828EFF" "#1F9E89FF"
# [7] "#35B779FF" "#6DCD59FF" "#B4DE2CFF" "#FDE725FF"

I see that plotly uses the first and the last element (#440154FF / #FDE725FF), and that the third color seems to be an interpolation of some sorts as it is not directly a member of col_pal:

scales::show_col(c(col_pal[1:5], "#25908CFF", col_pal[6:10]))

My guess that the color is an interpolation of col_pal[5:6]

Plot with 11 tiles showing different colors

But I have no idea of how the color is calculated? Any ideas how I could replicate the colors plotly uses in R knowing:

  1. The original color palette
  2. The number of colors used in plotly (here nlevels(as.factor(mtcars$cyl)))

P.S.: If my palette would contain a middle point, the color is exactly matched (change col_pal <- viridis(11), and the colors used are col_pal[c(1, 6, 11)]. However, I am really interested in any combinations of oclor palette length and needed colors.


Solution

  • After some debugging I identified the lines in which the color mapping is done (actually already on the R side):

    colScale <- scales::col_factor(pal, levels = names(pal) %||% lvls, na.color = na.color)
    

    Thus to get exactly the colors used by plotly we need to run:

    lvls <- levels(as.factor(mtcars$cyl))
    scales::col_factor(col_pal, levels = lvls)(lvls)
    # [1] "#440154" "#25908C" "#FDE725"