rplotlyheatmapcolorbar

Changing the colorbar labels in a plotly heatmap (R)


I am working with survey data where a person responds from 'not' to 'absolutely' in response to being asked whether one variable affects another. The responses are mapped to numbers, and a mean response is calculated.

I am presenting these mean values in a heatmap (x and y are lists with the same variable names). I would like the colour of squares in the heatmap to reflect the numerical mean, but I would like the labels on the colorbar to reflect the actual response text (e.g. 'not', 'lowly, 'moderately', 'highly', 'very highly') and to limit the tick marks to positions 0,1,2,3,4.

I'm not sure this can be done with plotly. I was able to do it with ggiraph, but this is going into Shiny and ggiraph has its own issues there - in plotly I have more control over display size and I couldn't get ggiraph to render large enough.

Minimal code is below, and so is the output.

library(plotly)
library(tidyr)

M <- matrix(c(NA, 1,3, 2, NA, 4, 3, 0, NA), nrow = 3, ncol = 3)
names_M <- c('var1', 'var2', 'var3')


val_to_char <- function(x) {
        if(is.na(x)) {return(x)}
        else if(x < 0.5) {return('not')}
        else if(x < 1.5) {return('lowly')}
        else if(x < 2.5) {return('moderately')}
        else if(x < 3.5) {return('highly')}
        else {return('very high')}
      }
      
labels <- apply(M, c(1,2), val_to_char)

   
fig <- plot_ly()
fig <- fig %>%
  add_trace(
    type = 'heatmap',
    x = names_M, y = names_M, z = M, text = labels,
    hovertemplate = '<extra></extra> Row: %{y}</br></br>Col: %{x}</br>Avg response: %{text}'
  )

fig

enter image description here


Solution

  • You could use ggplot and ggplotly for more flexibility. But you first need to tranform your data into long data.frame:

    M <- as.data.frame.matrix(matrix(c(NA, 1,3, 2, NA, 4, 3, 0, NA), nrow = 3, ncol = 3))
    names(M) <- c('var1', 'var2', 'var3')
    
    
    p <- M %>%
    pivot_longer(.,paste0("var",1:3),names_to = "x",values_to = "z") %>%
      mutate(y = rep(paste0("var",1:3),each = 3)) %>%
      ggplot(aes(x,y,fill = z))+
      geom_tile() +
      scale_fill_continuous(breaks = 0:4,labels = c("not","lowly","moderatly","highly","very high"))
    
    
    ggplotly(p)
    

    enter image description here

    The function scale_fill_continuous allows you to specify manually your labels for different breaks. You have a lot of possibilities to tweak your graph with ggplot