Search code examples
rggplot2plotlytidyrheatmaply

Interactive heatmap in R


My dataframe looks like:

      rankA      rankB     V1       V2
1     0-1 w      1-2 t     8.636042 10.43002
2     0-1 w      3-5 t     6.495266 10.52126
3     0-1 w      6-10 t    5.480639 10.56230
4     0-1 w      +10 t     4.897840 10.64759
5     2-3 w      1-2 t     7.677400 10.45409
6     2-3 w      3-5 t     5.420535 10.47965
7     2-3 w      6-10 t    4.499810 10.51640
8     2-3 w      +10 t     3.496508 10.44883

I need an interactive heatmap. I say interactive because of:

  • I need to plot rank A on X-axis
  • I need to plot rank B on Y-axis
  • I need to colour squares by V1
  • When the mouse pointer is over one square, a message box must show the V2 value.

I have just seen the heatmaply documentation, but I don't know how reproducing mtcars heatmaply to my case (tidyverse library?).

Any ideas? Are there other packages?


Solution

  • If you don't need dendrograms at the sides of the heatmap, the following are possible options:

    Option 1. Create ggplot object & convert using plotly::ggplotly:

    library(ggplot2)
    
    p <- ggplot(df,
           aes(x = rankA, y = rankB, fill = V1, label = V2)) +
      geom_tile() +
      geom_label(fill = "white") +
      viridis::scale_fill_viridis() # to match other packages' default palettes
    
    plotly::ggplotly(p)
    

    ggplot

    Option 2. Create plotly object:

    plotly::plot_ly(
      data = df,
      x = ~rankA, y = ~rankB, z = ~V1, text = ~paste('V2: ', V2),
      hoverinfo = "text",
      type = "heatmap"
    )
    

    (This version shows "V2: /value of V2/" on hover. But it's not captured by the exported screenshot.)

    plotly

    If you really need the dendrograms, I'm afraid I haven't found a way to feed that into heatmaply's arguments. But times are desperate, you can consider the following...

    Option 3. Create plotly object using heatmaply library & change the underlying code

    # create the heatmap object hm (on hover, it will show the rankA / rankB / V1 values)
    hm <- heatmaply::heatmaply(long_data = df %>% 
                                 select(rankB, rankA, V1) %>%
                                 rename(name = rankB, variable = rankA, value = V1))
    
    # look through hm's structure for hover text. I found it below, but I haven't 
    # used plotly enough to know if it's always going to be in the same place
    str(hm)
    
    > hm$x$data[4][[1]]$text
         [,1]                                                  [,2]                                                 
    [1,] "value: 8.636042<br />column: 0-1 w<br />row: 1-2 t"  "value: 7.677400<br />column: 2-3 w<br />row: 1-2 t" 
    [2,] "value: 6.495266<br />column: 0-1 w<br />row: 3-5 t"  "value: 5.420535<br />column: 2-3 w<br />row: 3-5 t" 
    [3,] "value: 5.480639<br />column: 0-1 w<br />row: 6-10 t" "value: 4.499810<br />column: 2-3 w<br />row: 6-10 t"
    [4,] "value: 4.897840<br />column: 0-1 w<br />row: +10 t"  "value: 3.496508<br />column: 2-3 w<br />row: +10 t" 
    attr(,"apiSrc")
    [1] TRUE
    
    # replace with V2's values, making sure the rows & columns are matched correctly
    df %>% 
      select(-V1) %>% 
      mutate(V2 = paste("V2:", V2)) %>%
      tidyr::spread(rankA, V2) %>% 
      arrange(factor(rankB, levels = c("1-2 t", "3-5 t", "6-10 t", "+10 t"))) %>%
      select(-rankB) %>% as.matrix() -> hm$x$data[4][[1]]$text
    
    hm
    

    (As before, this version shows "V2: /value of V2/" on hover. But it's not captured by the exported screenshot.)

    heatmaply

    Data:

    df <- read.table(header = T, stringsAsFactors = F, 
                     text = '      rankA      rankB     V1       V2
                     1     "0-1 w"     "1-2 t"     8.636042 10.43002
                     2     "0-1 w"      "3-5 t"     6.495266 10.52126
                     3     "0-1 w"      "6-10 t"    5.480639 10.56230
                     4     "0-1 w"      "+10 t"     4.897840 10.64759
                     5     "2-3 w"      "1-2 t"     7.677400 10.45409
                     6     "2-3 w"      "3-5 t"     5.420535 10.47965
                     7     "2-3 w"      "6-10 t"    4.499810 10.51640
                     8     "2-3 w"      "+10 t"     3.496508 10.44883')