RoyalTS RoyalTS - 2 months ago 23
R Question

fix plotly color scale breaks

I have created a heatmap using plotly's R interface, like so:

df <- data.frame(date = rep(seq(ymd('2016-09-01'), ymd('2016-09-10'), by='day'), each=2),
period = rep(c('lunch', 'dinner'), times=10),
diff=rnorm(20))

df %>%
plot_ly(x = date,
y = period,
z = abs(diff),
type = "heatmap",
hoverinfo='none',
colors = rev(RColorBrewer::brewer.pal(5, "RdYlGn")),
showscale = F) %>%
add_trace(x = date,
y = period,
text = paste0(round(diff*100), '%'),
mode='text',
hoverinfo='none')


which produces this:

heatmap

Is there any way I can fix the breaks of the color scale? I would like the midpoint of the color scale to be at 20% and any value that exceeds 50% to be colored with the red at the end of the scale.

Answer

Well, a heatmap is generally supposed to have colors that vary with the value of the element, but you could force each element into 1 of 5 groups based on whatever criteria you want and assign 5 colors accordingly. Then, that grouping would be passed into z. z should be a number though, not a factor, so you can convert the grouping to an integer:

# create 5 levels or groups for the diff variable
# choose breaks as desired
diffLevel <- cut(abs(df$diff),
                 breaks = c(0, 0.0625, .125, .3, .5, Inf))

df %>%
  plot_ly(x = date,
          y = period,
          z = as.integer(diffLevel),
          type = "heatmap",
          hoverinfo='none',
          colors = rev(RColorBrewer::brewer.pal(5, "RdYlGn")),
          showscale = F) %>%
  add_trace(x = date,
            y = period,
            text = paste0(round(diff*100), '%'),
            mode='text',
            hoverinfo='none')

Another approach would be to set the zmax value to 0.5 (and zauto = FALSE). This won't necessarily set the center of the colorscale to .2, but it works fairly well, and establish a cutoff of 0.5, above which everything is red. It also allows you to pass in abs(diff) directly:

df %>%
  plot_ly(x = date,
          y = period,
          z = abs(diff),
          zauto = FALSE,  # set this to FALSE!
          zmax = 0.5,              
          type = "heatmap",
          hoverinfo='none',
          colors = rev(RColorBrewer::brewer.pal(5, "RdYlGn")),
          showscale = F) %>%
  add_trace(x = date,
            y = period,
            text = paste0(round(diff*100), '%'),
            mode='text',
            hoverinfo='none')

One final method would be to work with a custom colorscale. I found this to be tricky to get the colorscale argument in the righ format. There may be a better way to do this, but I wasn't able to come up with anything elegant.

customPalette <- colorRampPalette(c("red", "yellow", "green"))(61) 
# I put 30 values between 0 and .2 and and 30 between .2 and 1.
# And so, my palette has 61 colors  
customColors <- setNames(
  data.frame(breaks =  c(seq(0, .2, length.out = 31), seq(.21, 1, length.out = 30)),
             colors = as.character(
              apply(col2rgb(rev(customPalette)), 2, function(ii)
                paste("rgb(", paste(ii, collapse = ","),")", sep = ""))),
             stringsAsFactors = FALSE),
  NULL)
# not perfect, but it seems to work decently

# initialize a list
colList <- list() 
colList[[dim(customColors)[1]]] <- 0

# format custom color list
for(i in seq_len(dim(customColors)[1])) {
  colList[[i]] <- c(customColors[i, 1], customColors[i, 2])
}

# set zmax = 0.5 and zauto = FALSE
df %>%
  plot_ly(x = date,
          y = period,
          z = abs(diff),
          zauto = FALSE,
          zmax = 0.5,
          type = "heatmap",
          hoverinfo='none',
          colorscale =  colList,
          showscale = F) %>%
  add_trace(x = date,
            y = period,
            text = paste0(round(diff*100), '%'),
            mode='text',
            hoverinfo='none')
Comments