绘制时间序列热图时提高 ggplotly 的性能

Posted

技术标签:

【中文标题】绘制时间序列热图时提高 ggplotly 的性能【英文标题】:Improve performance of ggplotly when plotting time-series heatmap 【发布时间】:2020-06-30 22:22:18 【问题描述】:

我正在使用 Plotly 和 Shiny 构建一个 interactive time-series heatmap in R。作为此过程的一部分,我将热图值从连续格式重新编码为有序格式 - 所以我有一个热图,其中六种颜色代表特定的计数类别,这些类别是从聚合的计数值创建的。但是,这会导致使用ggplotly() 创建热图的速度出现重大性能问题。我已经将其追溯到 Plotly 的 tooltip() 函数,该函数呈现交互式框。来自我的热图中的标签数据以某种方式使这个函数过载,它执行得非常缓慢,即使我只是将一个标签组件添加到tooltip()。我正在使用来自 Johns Hopkins CSSE repository 的 COVID-19 爆发数据的处理子集。这是一个简化的热图代码,同样使用The Simpsons colour theme from ggsci

#Load packages
library(shiny)
library(plotly)
library(tidyverse)
library(RCurl)
library(ggsci)

#Read example data from Gist
confirmed <- read_csv("https://gist.githubusercontent.com/GeekOnAcid/5638e37c688c257b1c381a15e3fb531a/raw/80ba9704417c61298ca6919343505725b8b162a5/covid_selected_europe_subset.csv")

#Wrap ggplot of time-series heatmap in ggplotly, call "tooltip"  
ggplot_ts_heatmap <- confirmed %>%
  ggplot(aes(as.factor(date), reorder(`Country/Region`,`cases count`), 
             fill=cnt.cat, label = `cases count`, label2 = as.factor(date), 
             text = paste("country:", `Country/Region`))) + 
  geom_tile(col=1) +
  theme_bw(base_line_size = 0, base_rect_size = 0, base_size = 10) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),legend.title = element_blank()) +
  scale_fill_manual(labels = levels(confirmed$cnt.cat),
                    values = pal_simpsons("springfield")(7)) +
  labs(x = "", y = "")
ggplotly(ggplot_ts_heatmap, tooltip = c("text","label","label2"))

一旦减少tooltip = c("text","label","label2")(例如减少tooltip = c("text")),性能就会提高。现在,我知道延迟不是“巨大的”,但我正在将它与 Shiny 应用程序集成。一旦它与 Shiny 集成并使用更多数据进行扩展,它真的非常非常慢。我什至没有在 tooltip 中显示所有变量,而且它仍然很慢 - 当您单击“已确认”案例时,您可以在 the current version of the app 中看到它。

有什么建议吗?我考虑过替代的交互式热图包,如 d3heatmapheatmaplyshinyHeatmaply,但所有这些解决方案都更适用于相关热图,并且它们缺少 ggplot 的自定义选项。

【问题讨论】:

它是一个很好的图形,但是,我会根据案例的数量考虑订购标签和颜色(例如,超过 1000 个案例应该是第一个标签)。另外我认为灰色和蓝色应该被一些颜色代替,让我们看到一种渐变。 感谢您的回答,但您没有解决我的问题。上面的代码只是一个示例占位符代码,用于说明 Plotly 的性能问题。正如我在问题中指出的那样,您可以查看我正在在线制作的应用程序原型。 是否可以重写为“纯”情节代码?也许从 ggplot 到 plotly 的转换需要一些时间?你看过这个link吗? Here 你可以阅读关于底层 plotly JS 库的问题。 【参考方案1】:

如果您将其重写为“纯”情节(没有ggplotly 转换),它会快得多。甚至大约3000次。这是一个非常小的基准测试的结果:

Unit: milliseconds
 expr       min        lq       mean     median        uq       max neval
    a 9929.8299 9929.8299 9932.49130 9932.49130 9935.1527 9935.1527     2
    b    3.1396    3.1396    3.15665    3.15665    3.1737    3.1737     2

ggplotly 慢得多的原因是它不将输入识别为热图并创建一个散点图,其中每个矩形都使用所有必要的属性单独绘制。如果将ggplotlyplot_ly 的结果包装在plotly_json() 中,则可以查看生成的JSON。

您还可以检查绘图的object.size,您会看到ggplotly 对象在4616.4 Kb 左右,plotly-heatmap 仅为40.4 Kb 大。

df_colors = data.frame(range=c(0:13), colors=c(0:13))
color_s <- setNames(data.frame(df_colors$range, df_colors$colors), NULL)
for (i in 1:14) 
  color_s[[2]][[i]] <- pal_simpsons("springfield")(13)[[(i + 1) / 2]]
  color_s[[1]][[i]] <-  i / 14 - (i %% 2) / 14


plot_ly(data = confirmed, text = text) %>%
  plotly::add_heatmap(x = ~as.factor(date), 
                      y = ~reorder(`Country/Region`, `cases count`),
                      z = ~as.numeric(factor(confirmed$`cnt.cat`, ordered = T, 
                                             levels = unique(confirmed$`cnt.cat`))),
                      xgap = 0.5,
                      ygap = 0.5,
                      colorscale = color_s,
                      colorbar = list(tickmode='array',
                                      title = "Cases",
                                      tickvals=c(1:7),
                                      ticktext=levels(factor(x = confirmed$`cnt.cat`,
                                                             levels = unique(confirmed$`cnt.cat`),
                                                             ordered = TRUE)), len=0.5),
                      text = ~paste0("country: ", `Country/Region`, "<br>",
                                    "Number of cases: ", `cases count`, "<br>",
                                    "Category:  ", `cnt.cat`),
                      hoverinfo ="text"
  ) %>% 
  layout(plot_bgcolor='black',
         xaxis = list(title = ""),
         yaxis = list(title = ""));

【讨论】:

谢谢,乍一看效果更好,我们说话时我正在 Shiny 中进行测试。关于您的解决方案的两个问题:(1)在使用plot_ly() 时,我是否有机会获得颜色的分类图例(如在我的示例和应用程序中),而不是连续的颜色图例? (2) 关于使用 Plotly 电子书中的 toWebGL()partial_bundle() 等性能优化器的建议 - 您是否只是将 plot_ly() 包装在其他函数中以提高性能? 我尝试了几个选项,不幸的是没有成功。当我弄清楚时,我会编辑我的答案。我找到了另一个SO question 实现了这一目标,但我没能做到。我也认为目前的值是错误的,虽然颜色看起来不错。这两个调用只是通过管道连接在一起,因此您可以将它们添加到末尾,但它并不总是有效,特别是如果您的 Shiny App 中有其他情节。 我更新了我的答案。我认为现在值和工具提示是正确的,但图例仍然是连续的。 另一个更新,现在带有离散图例。您必须定义自定义色阶。嗯,这很难:D 在这里找到关键问题的工作很棒。我已将您的代码与我的 Shiny 应用程序集成,它极大地提高了性能。谢谢 - 这个声誉是当之无愧的。

以上是关于绘制时间序列热图时提高 ggplotly 的性能的主要内容,如果未能解决你的问题,请参考以下文章

使用 ggplot 绘制 300 多个单位的热图

ggplot2绘制环状聚类热图

ggplot中带有圆圈而不是瓷砖的热图

R 数据可视化 —— 聚类热图 pheatmap

R语言可视化分别使用lattice包和ggplot2包可视化热图(heatmap)并绘制热力图对应的系统树图(dendrogram)实战

基于非数字第三变量的热图轴重新排序 - ggplot2