rplotlybar-chart

Adding custom colours to text labels in plotly R


Following on from my previous question here, using the same reprex I am now trying to add custom colours to both bars and label's font colour. The goal is to have white font colour for text labels when the bar has dark fill colour.

Adding bar colours as named vector worked well, but the same method doesn't work for label's font colour. In the image below Month 4 and Month 5 colours are not as intended and different from the other months.

library(dplyr)
library(tibble)
library(plotly)

df <- structure(list(dsc_cat1 = c("Cat1", "Cat1", "Cat2", "Cat1", "Cat1", 
                                  "Cat2", "Cat2", "Cat1", "Cat1", "Cat2", "Cat2", "Cat1", "Cat1", 
                                  "Cat2", "Cat2", "Cat1", "Cat1", "Cat2", "Cat1", "Cat1", "Cat2", 
                                  "Cat2"), dsc_cat3 = c("Var 1", "Var 2", "Var 3", "Var 1", "Var 2", 
                                                        "Var 3", "Var 4", "Var 1", "Var 2", "Var 3", "Var 4", "Var 1", 
                                                        "Var 2", "Var 5", "Var 3", "Var 1", "Var 2", "Var 3", "Var 1", 
                                                        "Var 2", "Var 3", "Var 4"), val = c(6200.63, 5358.48, 2417.35, 
                                                                                            8022.49, 14852.6, 2417.35, 100.93, 5389.56, 14631.52, 2417.35, 
                                                                                            84.39, 5387.66, 18231.84, 632.72, 1325.05, 5387.03, 14852.6, 
                                                                                            2417.35, 5377.88, 14087.98, 1510.02, 47.27), mth = c(1, 1, 1, 
                                                                                                                                                 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6)), row.names = c(NA, 
                                                                                                                                                                                                                          -22L), class = c("tbl_df", "tbl", "data.frame"))

fill_colours <- setNames(
  object = c(
     "#304996"
    ,"#515575"
    ,"#915f78"
    ,"#cd6873"
    ,"#e88c7d"
  )
  ,nm = paste("Var", 1:5)
)

label_colours <- setNames(
  object = c(
     "white"
    ,"white"
    ,"white"
    ,"black"
    ,"black"
  )
  ,nm = paste("Var", 1:5)
)


subplot(
  lapply(unique(df$mth), function(m) {
    
    data <- df %>%  
      mutate(val = ifelse(mth==m, val, 0)) %>%
      arrange(dsc_cat1) 
    
    x_title <- paste("Month", m)  
    show_legend = ifelse(m == 1, TRUE, FALSE)
    
    plot_ly(data = data, 
            x = ~dsc_cat1,
            y = ~val,
            type = "bar",
            legendgroup=~dsc_cat3,
            text = ~val,
            textfont = list(size = 12, color = label_colours),
            textposition = "auto",
            color = ~dsc_cat3,
            colors = fill_colours,
            showlegend=show_legend
    ) %>% 
      layout(xaxis = list(title = x_title))
  }), titleX = TRUE, shareY = TRUE) %>% 
  layout(barmode = 'stack', showlegend = TRUE)

enter image description here

Next I joined the label colours to the original data, so that plotly can read the colours directly from the data. As can be from the image below, the colours for Var 3 should be white but for Month 3 and Month 6 the labels are in black.

df <- df %>% 
  left_join(
    y = enframe(label_colours)
    ,by = c("dsc_cat3" = "name")
  )


subplot(
  lapply(unique(df$mth), function(m) {
    
    data <- df %>%  
      mutate(
        val = ifelse(mth==m, val, 0)
      ) %>%
      arrange(dsc_cat1) 
    
    x_title <- paste("Month", m)  
    show_legend = ifelse(m == 1, TRUE, FALSE)
    
    plot_ly(data = data, 
            x = ~dsc_cat1,
            y = ~val,
            type = "bar",
            legendgroup=~dsc_cat3,
            text = ~val,
            textfont = list(size = 12, color = ~value),
            textposition = "auto",
            color = ~dsc_cat3,
            colors = fill_colours,
            showlegend=show_legend
    ) %>% 
      layout(xaxis = list(title = x_title))
  }), titleX = TRUE, shareY = TRUE) %>% 
  layout(barmode = 'stack', showlegend = TRUE)

enter image description here

Any help is appreciated, TIA.


Solution

  • You have to arrange your data by both cat1 and cat3. I don't 100% understand but I think because the textfont is in a list it is selecting all the values, and using them in the order they are, not lining them up with the data. If you arrange based on both columns that data is sorted on it should select them in the correct order.

    See commented line below

    subplot(
      lapply(unique(df$mth), function(m) {
      
      data <- df %>%  
        mutate(
          val = ifelse(mth==m, val, 0)
        ) %>%
        #### THIS LINE ONLY CHANGE
        arrange(dsc_cat1, dsc_cat3) 
      
      x_title <- paste("Month", m)  
      show_legend = ifelse(m == 1, TRUE, FALSE)
      
      plot_ly(data = data, 
              x = ~dsc_cat1,
              y = ~val,
              type = "bar",
              legendgroup=~dsc_cat3,
              text = ~val,
              textfont =  ~value,
              textposition = "auto",
              color = ~dsc_cat3,
              colors = fill_colours,
              showlegend=show_legend
      ) %>% 
        layout(xaxis = list(title = x_title))
    }), titleX = TRUE, shareY = TRUE) %>% 
    layout(barmode = 'stack', showlegend = TRUE)