3

I'm trying to add an interactive heatmap to my Shiny app, but I also have interactive graphs using ggiraph. I'm currently using the d3heatmap package, but the heatmaps don't render in the app. I've created a toy example to illustrate this:

library(shiny)
library(ggiraph)
library(d3heatmap)

ui <- fluidPage(
    d3heatmapOutput('d3'),
    ggiraphOutput('gg')
)

server <- function(input, output, session) {

    # Create heatmap
    output$d3 <- renderD3heatmap({
        d3heatmap(matrix(1:100, nrow = 100, ncol = 100))
    })

    # Create ggiraph
    output$gg <- renderggiraph({
        p <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Width,
                              color = Species, tooltip = iris$Species) ) +
             geom_point_interactive()

        ggiraph(code = {print(p)})
    })
}

shinyApp(ui =  ui, server = server)

Together, only the ggiraph renders, but the heatmap does not. However, if you comment out the ggiraph code, the heatmap renders. I tried switching the order of loading the packages, but that still didn't work.

I'm currently running on R 3.2.2 (I have to use this version because the company servers only run on this version, and neither my manager nor I have the authority to update it). I tried downloading the shinyheatmap, heatmaply, and heatmap.2 packages, but because of versioning issues, the installations were unsuccessful.

So right now, I've just used pheatmap to create the heatmaps, but they aren't interactive (i.e., I can't get values when I hover over individual cells, and I can't zoom in). Is there any workaround for this, or are there other interactive heatmap packages out there that would work? I'd like to avoid changing all of my ggiraph graphs to plotly graphs since there are a lot of them in my code.

Please let me know if there's any other information you need. Any suggestions would be much appreciated!

Melissa
  • 35
  • 2
  • You may want to look into the googleVis package: https://cran.r-project.org/web/packages/googleVis/vignettes/googleVis_examples.html. I am trying to recreate your issue currently. – Alex Dometrius Jun 22 '17 at 20:01
  • @AlexDometrius Thank you for the quick response! The googleVis package looks promising. Do you know if/how I can create a regular, rectangular heatmap that doesn't involve geographical or calendar data with it? – Melissa Jun 22 '17 at 20:46
  • I am puzzled as to the actual issue that will not allow the plots to be plotted together (I also experienced your problem when re-producing your example). I know you can use ggplot to make heatmaps without the d3heatmap package. "heatmap" is a command in base R as well but I have never used it. A good example is here: https://learnr.wordpress.com/2010/01/26/ggplot2-quick-heatmap-plotting/ . Maybe it has something to do with the javascript of each plot? – Alex Dometrius Jun 22 '17 at 20:58

2 Answers2

5

(just to let you know I am the author of ggiraph) There is a conflict between ggiraph and d3heatmap because ggiraph is using d3.js version 4 and d3heatmap is using D3.js version 3. I don't think there is a solution to solve that conflict.

However, building an interactive heatmap with ggplot2/ggiraph is not that difficult. See below:

library(dplyr)
library(tidyr)
library(ggplot2)
library(ggiraph)
library(ggdendro)


# mydata <- cor(mtcars)
mydata <- matrix(runif(2500, min = -2, max = 2), ncol = 50)
row.names(mydata) <- paste0("row_", seq_len(nrow(mydata)))
colnames(mydata) <- paste0("col_", seq_len(ncol(mydata)))

# dendrogram for rows
hc <- hclust(dist(mydata), "ave")
dhr <- as.dendrogram(hc)
order_r <- rownames(mydata)[hc$order]

# dendrogram for columns
hc <- hclust(dist(t(mydata)), "ave")
dhc <- as.dendrogram(hc)
order_c <- colnames(mydata)[hc$order]

# the data
expr_set <- bind_cols(
  data_frame(rowvar = rownames(mydata)),
  as.data.frame(mydata)
)
expr_set <- gather(expr_set, colvar, measure, -rowvar)
expr_set$rowvar <- factor( expr_set$rowvar, levels = order_r )
expr_set$colvar <- factor( expr_set$colvar, levels = order_c )
expr_set <- arrange(expr_set, rowvar, colvar)

# get data for dendrograms - IMHO, ggdendro is the hero here...
data_c <- dendro_data(dhc, type = "rectangle")
data_c <- segment(data_c) %>% mutate(
  y = y + length(order_r) + .5,
  yend = yend + length(order_r) + .5
)

data_r <- dendro_data(dhr, type = "rectangle")
data_r <- segment(data_r)
data_r <- data_r %>%
  mutate( x_ = y + length(order_c) + .5,
          xend_ = yend + length(order_c) + .5,
          y_ = x,
          yend_ = xend )

expr_set <- expr_set %>% 
  mutate( 
    tooltip = sprintf("Row: %s<br/>Col: %s<br/>measure: %.02f", 
                      rowvar, colvar, measure) ,
    data_id = sprintf("%s_%s", rowvar, colvar)
    )


# all data are tidy and can be now used with ggplot
p <- ggplot(data = expr_set, aes(x = colvar, y = rowvar) ) +
  geom_tile_interactive(aes(fill = measure, tooltip = tooltip, data_id = data_id), colour = "white") +
  scale_fill_gradient(low = "white", high = "#BC120A") +
  geom_segment(
    data = data_c,
    mapping = aes(x = x, y = yend, xend = xend, yend = y),
    colour = "gray20", size = .2) +
  geom_segment(
    data = data_r,
    mapping = aes(x = x_, y = y_, xend = xend_, yend = yend_),
    colour = "gray20", size = .2) +
  coord_equal()

# cosmetics
p <- p + theme_minimal() +
  theme(
    legend.position = "right",
    panel.grid.minor = element_line(color = "transparent"),
    panel.grid.major = element_line(color = "transparent"),
    axis.ticks.length   = unit(2, units = "mm"),
    plot.title = element_text(face = "bold", hjust = 0.5, size = 12),
    axis.title = element_text(size = 9, colour = "gray30"),
    axis.text.y = element_text(hjust = 1, size = 5, colour = "gray40"),
    axis.text.x = element_text(angle = 90, hjust = 1, size = 5, colour = "gray40"),
    legend.title=element_text(face = "bold", hjust = 0.5, size=8),
    legend.text=element_text(size=6)
  )



ggiraph(ggobj = p)

enter image description here

Hope it helps

David Gohel
  • 9,180
  • 2
  • 16
  • 34
2

I know that this question is answered some time ago but I've ran into the same problem and i was not able to use ggplot2 because it was just to slow to work with my Shiny application. The heatmaply package is allot faster and easier to implement. I performed a mini-benchmark (n= 20). with ggplot2 took an average time of 64 seconds. With heatmaply it took only 2 seconds. both methods use the 'ave' method of hclust.I hope this is helpfull.


mini-benchmark n= 20 of ggplot vs heatmaply

here is the code i used:

library(tidyr)
library(ggplot2)
library(ggiraph)
library(ggdendro)
library(heatmaply)

# mydata <- cor(mtcars)
create_data <- function(){
df <- matrix(runif(2500, min = -2, max = 2), ncol = 50)
row.names(df) <- paste0("row_", seq_len(nrow(df)))
colnames(df) <- paste0("col_", seq_len(ncol(df)))
return(df)
}

gg2heat <- function(mydata){
# dendrogram for rows
hc <- hclust(dist(mydata), "ave")
dhr <- as.dendrogram(hc)
order_r <- rownames(mydata)[hc$order]

# dendrogram for columns
hc <- hclust(dist(t(mydata)), "ave")
dhc <- as.dendrogram(hc)
order_c <- colnames(mydata)[hc$order]

# the data
expr_set <- bind_cols(
data_frame(rowvar = rownames(mydata)),
as.data.frame(mydata)
)
expr_set <- gather(expr_set, colvar, measure, -rowvar)
expr_set$rowvar <- factor( expr_set$rowvar, levels = order_r )
expr_set$colvar <- factor( expr_set$colvar, levels = order_c )
expr_set <- arrange(expr_set, rowvar, colvar)

# get data for dendrograms - IMHO, ggdendro is the hero here...
data_c <- dendro_data(dhc, type = "rectangle")
data_c <- segment(data_c) %>% mutate(
y = y + length(order_r) + .5,
yend = yend + length(order_r) + .5
)

data_r <- dendro_data(dhr, type = "rectangle")
data_r <- segment(data_r)
data_r <- data_r %>%
mutate( x_ = y + length(order_c) + .5,
       xend_ = yend + length(order_c) + .5,
       y_ = x,
       yend_ = xend )

expr_set <- expr_set %>% 
mutate( 
 tooltip = sprintf("Row: %s<br/>Col: %s<br/>measure: %.02f", 
                   rowvar, colvar, measure) ,
 data_id = sprintf("%s_%s", rowvar, colvar)
)


# all data are tidy and can be now used with ggplot
p <- ggplot(data = expr_set, aes(x = colvar, y = rowvar) ) +
geom_tile_interactive(aes(fill = measure, tooltip = tooltip, data_id = data_id), colour = "white") +
scale_fill_gradient(low = "white", high = "#BC120A") +
geom_segment(
 data = data_c,
 mapping = aes(x = x, y = yend, xend = xend, yend = y),
 colour = "gray20", size = .2) +
geom_segment(
 data = data_r,
 mapping = aes(x = x_, y = y_, xend = xend_, yend = yend_),
 colour = "gray20", size = .2) +
coord_equal()

# cosmetics
p <- p + theme_minimal() +
theme(
 legend.position = "right",
 panel.grid.minor = element_line(color = "transparent"),
 panel.grid.major = element_line(color = "transparent"),
 axis.ticks.length   = unit(2, units = "mm"),
 plot.title = element_text(face = "bold", hjust = 0.5, size = 12),
 axis.title = element_text(size = 9, colour = "gray30"),
 axis.text.y = element_text(hjust = 1, size = 5, colour = "gray40"),
 axis.text.x = element_text(angle = 90, hjust = 1, size = 5, colour = "gray40"),
 legend.title=element_text(face = "bold", hjust = 0.5, size=8),
 legend.text=element_text(size=6)
)
ggiraph(ggobj = p)
}

htmp_gg <- c()
htmp_maply <-c() 


for (i in 1:20){
df <- create_data()
time_gg <- (system.time(gg2heat(df)))[3]
htmp_gg<- append(htmp_gg, values = time_gg)
time_heatmaply <- (system.time(heatmaply::heatmaply(df, hclust_method = 'ave')))[3]
htmp_maply<- append(htmp_maply, values = time_heatmaply)
rm(df)
}

score <- data.frame(htmp_gg, htmp_maply)%>% gather(key = 'method', value = 'time')


p <- ggplot(score, aes(x = method, y = time, fill = method))+geom_violin()+ stat_summary(fun.y=median, geom="point", size=2, color="black")
print(p)