Search code examples
rchartshighchartsdata-visualizationr-highcharter

Custom colors in Highcharter are not mapping correctly to the legend


I am having difficulty mapping specific colors to the values of my stacked bar chart such that they are consistent across a wide array of charts built from the same dataset. For example, highcharter will assign the default colors (or color list that I give it) to my grouped categorical values, but when the chart is modified (say in Shiny) a new chart is generated but the colors may have changed. This is very confusing and potentially misleading to my audience.

Here is a sample dataset:

responses <- c('Pro','Against','Neutral','Resigned/Accepting','Not Specified')
constituents <- c('dual degree','law only','undergrad only','friend','parent only')
indiv <- rep(1:50)
Name.Change <- sample(responses,50,replace = TRUE)
constituent.type <- sample(constituents,50,replace = TRUE)

demo <- as.data.frame(cbind(indiv,Name.Change,constituent.type))

I factorize my stacked variable because that seems to determine the order of the stack consistently. Then I assign colors to the values of 'Name.Change' so that they will remain consistent across a wide range of charts.

demo$Name.Change.fac <- factor(demo$Name.Change, levels = c("Pro","Resigned/Accepting","Neutral","Against","Not Specified"), ordered = TRUE)

demo <- demo %>%
  mutate(
    name.change.color = plyr::mapvalues(
      Name.Change.fac,
      from = c(
        "Pro",
        "Against",
        "Resigned/Accepting",
        "Neutral",
        "Not Specified"
      ),
      to = c("#1395BA","#F16C20","#0D3C55","#EBC844","#A2B86C")
    )
  )

Here is my best attempt with HighCharter:

demo %>% ## these colors don't match
  group_by(constituent.type,Name.Change.fac,name.change.color) %>%
  summarise(count = n()) %>%
  hchart(type = "bar",
         hcaes(y = count,
               x = constituent.type,
               group = Name.Change.fac,
               color = name.change.color),
         color = unique(demo$name.change.color)) %>%
  hc_plotOptions(bar = list(stacking = "percent")) %>%
  hc_tooltip(shared = TRUE)

As you can see, the colors in the labels do not correspond to the (correctly assigned) colors in the stacked chart. If I remove color=name.change.color within hcaes(), the colors match between the bars and labels, but the colors are not as I assigned them and may change from chart to chart. I explored ways to manually recreate the legend, but I could not then have the reactive clicking functionality that allows me to toggle the visible bars in my chart.

Here are my libraries:

library('plyr')
library('dplyr')
library('tidyr')
library('highcharter')

Solution

  • Try if

    color = levels(demo$name.change.color)
    

    instead of

    color = unique(demo$name.change.color)
    

    gives you the correct order.

    If you use unique or drop factor levels somewhere in your code things can get out of line.

    Alternatively, you could simplify things a bit like this:

    invisible(suppressPackageStartupMessages(lapply(c("dplyr","tidyr","highcharter"), 
        require, character.only=TRUE)))
    responses <- c('Pro','Against','Neutral','Resigned/Accepting','Not Specified')
    constituents <- c('dual degree','law only','undergrad only','friend','parent only')
    indiv <- rep(1:50)
    set.seed(123)
    Name.Change <- factor(sample(responses, 50, replace = TRUE), 
        levels = responses, ordered = TRUE)
    constituent.type <- sample(constituents, 50, replace = TRUE)
    demo <- tibble(indiv, Name.Change, constituent.type)
    
    name.change.color <- setNames(
        c("#1395BA","#F16C20","#0D3C55","#EBC844","#A2B86C"),
        levels(demo$Name.Change))
    
    demo %>% 
        group_by(constituent.type, Name.Change) %>%
        summarise(count = n()) %>%
        hchart(type = "bar",
               hcaes(y = count,
                     x = constituent.type,
                     group = Name.Change),
               color = name.change.color
        ) %>%
        hc_plotOptions(bar = list(stacking = "percent")) %>%
        hc_tooltip(shared = TRUE)
    #> `summarise()` regrouping output by 'constituent.type' (override with `.groups` argument)
    

    Created on 2020-07-31 by the reprex package (v0.3.0)