1

我正在尝试绘制一个空白图,将其从 htmlWidgets 输入 onRender(),并在 onRender() 函数中添加许多行。在下面的代码中,我使用了一个包含 100 行(100 行)的数据集,当我运行应用程序时,大约一秒钟内就会在 onRender() 中绘制 100 行。但是,当我将数据集更改为具有 2000 行时,需要十秒钟才能将它们全部绘制出来。

我试图为 50,000 到 100,000 行的数据集实现这一点。由于当前代码的缓慢,这显然是有问题的!

我目前实现该功能的方式是:

  1. 在 R 中创建一个名为 pcpDat 的数据框。它有 100 行和 6 列的数值数据。
  2. 在 R 中创建一个名为 p 的空白图
  3. 将数据框 pcpDat 和绘图 p 输入 onRender()
  4. 在 onRender() 中:我有一个 xArr 对象,它只包含值 0、1、2、3、4、5。对于数据帧的每一行,我将其 6 个值重构为一个名为 yArr 的数字向量。然后,对于数据的每一行,我创建一个 Plotly 跟踪对象,其中包含要绘制的 6 个 x 和 6 个 y 值的 xArr 和 yArr。然后,此 Plotly 跟踪对象为原始数据框的每一行创建一条橙色线。

绘制这么多行似乎很愚蠢!我的理由是我试图最终添加功能,以便用户可以使用 Plotly 选择绘图上的一个区域并仅查看拦截该区域的线(其余的线将被删除)。这就是为什么我希望这些线条是“交互式的”。

这一切让我思考了几个问题:

  1. 我对 JavaScript 没有经验(这是 onRender() 函数的关键)。我想知道是否有可能期望快速绘制 50,000 到 100,000 条线(比如说 5 秒内)?
  2. 如果 (1) 的答案应该是可能的,我正在就如何“加快”下面的代码片段寻求建议。如果没有太多的 JavaScript 技能,我很难确定什么是最耗时的。我可能无法有效地重建这些数据。

我渴望听到有关此主题的任何建议或意见。谢谢!

library(plotly)
library(ggplot2)
library(shiny)
library(htmlwidgets)
library(utils)

ui <- basicPage(

  plotlyOutput("plot1")
)

server <- function(input, output) {

  set.seed(3)
  f = function(){1.3*rnorm(100)}
  pcpDat = data.frame(ID = paste0("ID", 1:100), A=f(), B=f(), C=f(), D=f(), E=f(), F=f())
  pcpDat$ID = as.character(pcpDat$ID)
  plotPCP(pcpDat = pcpDat)

  colNms <- colnames(pcpDat[, c(2:(ncol(pcpDat)))])
  nVar <- length(colNms)

  p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point(alpha=0) + xlim(0,(nVar-1)) +ylim(min(pcpDat[,2:(nVar+1)]),max(pcpDat[,2:(nVar+1)])) + xlab("Sample") + ylab("Count")
  gp <- ggplotly(p)

  output$plot1 <- renderPlotly({
    gp %>% onRender("
      function(el, x, data) {

      var origPcpDat = data.pcpDat
      var pcpDat = data.pcpDat

      var Traces = [];
      var dLength = pcpDat.length
      var vLength = data.nVar
      var cNames = data.colNms

      xArr = [];
      for (b=0; b<vLength; b++){
      xArr.push(b)
      }

      for (a=0; a<dLength; a++){
      yArr = [];
      for (b=0; b<vLength; b++){
      yArr.push(pcpDat[a][cNames[b]]);
      }
      var pcpLine = {
      x: xArr,
      y: yArr,
      mode: 'lines',
      line: {
      color: 'orange',
      width: 1
      },
      opacity: 0.9,
      }
      Traces.push(pcpLine);
      }
      Plotly.addTraces(el.id, Traces);
}", data = list(pcpDat = pcpDat, nVar = nVar, colNms = colNms))})
}

shinyApp(ui, server)

编辑:为了展示我正在尝试做的事情,我包含了 3 张图片。它们显示了一个示例,其中数据中有 10 行(行)。第一个图像是用户首先看到的(所有 10 行都存在)。然后,用户可以使用“框选”工具来创建一个矩形(灰色)。对于它包含的所有 x 值,留在矩形内的任何线都将保留。在此示例的第二张图像中,剩余 5 行。之后,用户可以创建另一个矩形(灰色)。同样,对于它包含的所有 x 值,保留在矩形内的任何行都将保留。在此示例的第三张图像中,现在只剩下 1 行。这 3 个屏幕截图来自我的功能代码。所以,我确实有一个原型工作。但是,当我添加数千行时,它太慢了。

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

4

1 回答 1

0

如果您将您的ggplotplotly javascript转换为plotly包标准,那么您将删除您当前拥有的额外步骤和计算。下面的最小示例解决方案:

output$plot1 <- renderPlotly({

  plot_ly(type = "scatter", mode = "markers") %>%
    add_trace(
      x = ~wt,
      y = ~mpg,
      data = mtcars
    ) %>%
    layout(
      xaxis = list(title = "Sample"),
      yaxis = list(title = "Count")
    )

})

在此处输入图像描述

要完成隐藏的痕迹,您可以将属性设置visible = "legendonly"为您的痕迹,用户可以打开或关闭这些痕迹。有关更多详细信息,请参阅这些答案12

您还可以使用输入反应来限制您发送到 plotly 的数据量,而不是在每次要生成时都给它一切。

于 2017-10-09T02:09:49.460 回答