英文:
How to improve the refresh rate of a dygraph in shiny?
问题
以下是代码的中文翻译部分:
我想通过一个闪亮的应用程序和dygraphs查看时间序列(有很多点,这里有2E6个点)。
下面的脚本是一个工作得很好的最小示例,除了数值输入“start”发生变化会导致显示冻结5秒之外,一切正常。相反,使用范围选择器几乎没有延迟。所以我想这应该是可行的,但我没有找到解决方法。
是否有办法改进它?
library(shiny)
library(dygraphs)
ui <- fluidPage(
# 侧边栏
sidebarLayout(
# 用于输入的侧边栏面板 ----
sidebarPanel(
numericInput("start", label = "开始(迭代)",
value = 1, step = 5E4),
numericInput("duration", label = "持续时间(迭代)",
value = 5E4)
),
# 主面板
mainPanel(
dygraphOutput("plot")
)
)
)
server <- function(input, output) {
z <- rnorm(2E6, 1, 10) # 数据
time <- 1:length(z)
df <- data.frame(time, z)
output$plot <- renderDygraph({
dygraph(df) %>% dyRangeSelector(dateWindow = c(input$start, input$start + input$duration))
})
}
shinyApp(ui = ui, server = server)
希望这对您有帮助。
英文:
I would like to go through a time series (having lots of points, here 2E6) using a shiny app and dygraphs.
The script below is a minimal example that works nicely except that a change in the numeric input start
results in a 5 seconds freeze of the display. In contrast, moving with the range selector results in almost no lag. So I guess it should be feasible but I had no luck sorting this out.
Is there a way to improve it ?
library(shiny)
library(dygraphs)
ui <- fluidPage(
# Sidebar
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
numericInput("start", label = "Start (iteration)",
value = 1, step = 5E4),
numericInput("duration", label = "Duration (iterations)",
value = 5E4)
),
# Main
mainPanel(
dygraphOutput("plot")
)
)
)
server <- function(input, output) {
z<-rnorm(2E6,1,10) # data
time<-1:length(z)
df<-data.frame(time,z)
output$plot <- renderDygraph({
dygraph(df) %>% dyRangeSelector(dateWindow = c(input$start,input$start+input$duration) )
})
}
shinyApp(ui = ui, server = server)
答案1
得分: 2
以下是您要翻译的内容:
"这种行为的原因是,每当您更改 input$start
(input$duration
),整个图表都会重新渲染。当您仅仅更改选择器的手柄时,dygraph
会确保显示相关数据,而不重新渲染整个图表(实际上,这是 dygraph
的一个特性之一)。
您可以借助一点 JavaScript 的帮助来利用这种行为。思路如下:
-
在您的
renderDygraph
函数中,不要依赖于input$start
(input$duration
)。这样可以避免在这些值更改时重新渲染整个图表。 -
包含一个
observer
,它调用dygraph
的 JavaScript 函数updateOptions
,从而更新 x 轴上的可见范围(基本上就是手动更改范围手柄的效果)。 -
为此,您需要:
- 在渲染时存储
dygraph
对象(我们使用htmlwidgtes::onRender
来实现),以便稍后调用updateOptions
。 - 添加一个自定义消息处理程序,以从 shiny 中调用 JavaScript(您也可以使用
shinyjs
,它为您封装了这一功能)。
- 在渲染时存储
代码胜过千言万语,下面是一个可工作的示例:"
英文:
The reason for this behaviour is that whenever you change input$start
(input$duration
) the whole plot is re-rendered. When you simply change the selector handles, dygraph
makes sure the relevant data is shown, without re-rendering the whole graph (that is in fact one of the features of dygraph).
You can piggy-back on this behaviour with the help of a bit of JavaScript. The idea is the following:
-
In your
renderDygraph
function you do not take dependencies oninput$start
(input$duration
). This avoids that the whole graph is re-rendered whenever one of those values changes. -
You include an
observer
, which calls the dygraph JavaScript functionupdateOptions
, which in turn updates the visible range on the x-axis (so basically the same as changing the range handles by hand). -
In order to do so you need to:
- Store the dygraph object upon rendering (we use
htmlwidgtes::onRender
to so so), such that we can callupdateOptions
later. - Add a custom message handler, to invoke JavaScript from shiny (you could also use
shinyjs
which encapsulates this for you).
- Store the dygraph object upon rendering (we use
Code speaks a thousand words, so here's a working example:
library(shiny)
library(dygraphs)
library(htmlwidgets)
ch <- JS("
var plots = [];
Shiny.addCustomMessageHandler('adjust-x-axis', function(limits) {
const dg = plots['plot'];
if (dg) {
dg.updateOptions({dateWindow: [limits.xmin, limits.xmax]});
}
});")
ui <- fluidPage(
# Sidebar
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
numericInput("start", label = "Start (iteration)",
value = 1, step = 5E4),
numericInput("duration", label = "Duration (iterations)",
value = 5E4)
),
# Main
mainPanel(
tags$head(
tags$script(
ch,
type = "text/javascript"
)
),
dygraphOutput("plot")
)
)
)
server <- function(input, output, session) {
z <- rnorm(2E6, 1, 10) # data
time <- 1:length(z)
df <- data.frame(time, z)
output$plot <- renderDygraph({
dygraph(df) %>%
dyRangeSelector(dateWindow = c(1, 1 + 5E4)) %>%
onRender("function(el, x) {
plots['plot'] = this.dygraph;
}")
})
observe({
start <- req(input$start)
duration <- req(input$duration)
session$sendCustomMessage(
"adjust-x-axis",
list(xmin = start, xmax = start + duration)
)
})
}
shinyApp(ui = ui, server = server)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论