R Shiny:在 plotOutput 中创建非反应性背景
R Shiny: create non-reactive background in plotOutput
我正在尝试构建一个闪亮的应用程序,我可以在其中以交互方式更改情节。我希望情节在几毫秒内发生变化,因为变化只包括添加一些点,这实际上是可能的。
可重现的例子包含了这个想法的抽象。第一个示例绘制了一个散点图,我可以交互式地更改点数。这基本上立即发生。我将把这部分情节称为“反应层”。
library(shiny)
ui <- fluidPage(
sliderInput(inputId = "slider_input", label = "Reactive values:", min = 1, max = 100, value = 10),
plotOutput(outputId = "plotx")
)
quick_server <- function(input, output, session){
output$plotx <- renderPlot({
# reactive layer
plot(
x = sample(x = -4:4, size = input$slider_input, replace = T),
y = sample(x = -4:4, size = input$slider_input, replace = T)
)
})
}
shinyApp(ui = ui, server = quick_server)
问题是我想交互更改的图总是包含一个“慢速非反应层”,其中包含许多不反应且从不改变的数据点。由于此数据集的大小和 renderPlot()
总是重新绘制它,我交互更改“反应层”的速度急剧下降。
library(shiny)
ui <- fluidPage(
sliderInput(inputId = "slider_input", label = "Reactive values:", min = 1, max = 100, value = 10),
plotOutput(outputId = "plotx")
)
slow_server <- function(input, output, session){
base_data <- reactiveVal(value = data.frame(x = rnorm(n = 200000), y = rnorm(n = 200000)))
output$plotx <- renderPlot({
# slow non reactive layer
plot(x = base_data()$x, y = base_data()$y)
# reactive layer
points(
x = sample(x = -4:4, size = input$slider_input, replace = T),
y = sample(x = -4:4, size = input$slider_input, replace = T),
col = "red",
cex = 5,
pch = 19
)
})
}
shinyApp(ui = ui, server = slow_server)
由于基础数据和生成的图(这里是点云)永远不会改变,所以 renderPlot()
总是重新绘制所有内容(都是“图层”),这很烦人。有没有办法“隔离”从非反应性数据集创建的图?这样就不会重新绘制非反应性层?
我已经尝试使用 ggplot2 并创建一个稳定层
big_data_frame <- data.frame(x = rnorm(n = 200000), y = rnorm(n = 200000))
steady_layer <- reactiveVal(value = geom_point(data = big_data_frame, mapping = aes(x = x, y = y))
然后创建这样的情节
output$plotx <- renderPlot({
small_df <-
data.frame(
x = sample(x = -4:4, size = input$slider_input, replace = T),
y = sample(x = -4:4, size = input$slider_input, replace = T)
)
ggplot() +
steady_layer() +
geom_point(data = small_df, mapping = aes(x = x, y = y)
})
但这并没有帮助,因为重新绘制过程需要时间而不是创建 ggplot 图层本身。
虽然我可以想象解决方案可能是创建一个大图的 .png 并将其用作 HTML 的背景和 output$plotx
的 CSS 通过使其成为反应式 UI 我还没有成功地操纵 HTML 和 CSS。
感谢任何帮助。提前致谢!
您需要了解 renderPlot
的工作原理。它首先使用R png
函数创建一个png,然后将其发送到客户端浏览器。当绘图的数据发生变化时,将重新创建此 png。因此,不可能重新绘制 png 的一部分。所以给一个已经存在的plot加点总是会用到slow points + new points
的时间,所以在目前的闪亮机制下,不重新计算这些慢点是不可能的。一个可能的选择是使用 plotly
和 proxy 。它由 HTML 和 Javascript 组成,所以是的,您可以通过部分更新来完成。详见link,此处不再赘述。根据我的个人经验,它的速度并不像您想要的那样快milliseconds-level。
所以在这里我有一个聪明的技巧给你:为什么我们在同一个情节上更新?我们可以用一个图作为背景,它很慢,但我们只渲染一次,我们再也不会碰它了。然后我们更新另一个只有几个点的图,我们将这个图堆叠在慢图的顶部。
方法如下:
- 添加一些 CSS 技巧来进行堆叠
- 渲染慢情节
- 使用透明渲染快速绘图
library(shiny)
library(ggplot2)
ui <- fluidPage(
sliderInput(inputId = "slider_input", label = "Reactive values:", min = 1, max = 100, value = 10),
div(
class = "large-plot",
plotOutput(outputId = "plot_bg"),
plotOutput(outputId = "plotx")
),
tags$style(
"
.large-plot {
position: relative;
}
#plot_bg {
position: absolute;
}
#plotx {
position: absolute;
}
"
)
)
slow_server <- function(input, output, session){
base_data <- reactiveVal(value = data.frame(x = rnorm(n = 200000), y = rnorm(n = 200000)))
output$plot_bg <- renderPlot({
ggplot(base_data()) +
geom_point(aes(x,y)) +
scale_x_continuous(breaks = -4:4) +
scale_y_continuous(breaks = -4:4) +
xlim(-5, 5) +
ylim(-5, 5)
})
output$plotx <- renderPlot({
data.frame(
x = sample(x = -4:4, size = input$slider_input, replace = T),
y = sample(x = -4:4, size = input$slider_input, replace = T)
) %>%
ggplot() +
geom_point(aes(x,y), color = "red", size = 3) +
scale_x_continuous(breaks = -4:4) +
scale_y_continuous(breaks = -4:4) +
theme(
panel.background = element_rect(fill = "transparent"),
plot.background = element_rect(fill = "transparent", color = NA),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.background = element_rect(fill = "transparent"),
legend.box.background = element_rect(fill = "transparent")
)+
xlim(-5, 5) +
ylim(-5, 5)
}, bg="transparent")
}
shinyApp(ui = ui, server = slow_server)
我正在尝试构建一个闪亮的应用程序,我可以在其中以交互方式更改情节。我希望情节在几毫秒内发生变化,因为变化只包括添加一些点,这实际上是可能的。
可重现的例子包含了这个想法的抽象。第一个示例绘制了一个散点图,我可以交互式地更改点数。这基本上立即发生。我将把这部分情节称为“反应层”。
library(shiny)
ui <- fluidPage(
sliderInput(inputId = "slider_input", label = "Reactive values:", min = 1, max = 100, value = 10),
plotOutput(outputId = "plotx")
)
quick_server <- function(input, output, session){
output$plotx <- renderPlot({
# reactive layer
plot(
x = sample(x = -4:4, size = input$slider_input, replace = T),
y = sample(x = -4:4, size = input$slider_input, replace = T)
)
})
}
shinyApp(ui = ui, server = quick_server)
问题是我想交互更改的图总是包含一个“慢速非反应层”,其中包含许多不反应且从不改变的数据点。由于此数据集的大小和 renderPlot()
总是重新绘制它,我交互更改“反应层”的速度急剧下降。
library(shiny)
ui <- fluidPage(
sliderInput(inputId = "slider_input", label = "Reactive values:", min = 1, max = 100, value = 10),
plotOutput(outputId = "plotx")
)
slow_server <- function(input, output, session){
base_data <- reactiveVal(value = data.frame(x = rnorm(n = 200000), y = rnorm(n = 200000)))
output$plotx <- renderPlot({
# slow non reactive layer
plot(x = base_data()$x, y = base_data()$y)
# reactive layer
points(
x = sample(x = -4:4, size = input$slider_input, replace = T),
y = sample(x = -4:4, size = input$slider_input, replace = T),
col = "red",
cex = 5,
pch = 19
)
})
}
shinyApp(ui = ui, server = slow_server)
由于基础数据和生成的图(这里是点云)永远不会改变,所以 renderPlot()
总是重新绘制所有内容(都是“图层”),这很烦人。有没有办法“隔离”从非反应性数据集创建的图?这样就不会重新绘制非反应性层?
我已经尝试使用 ggplot2 并创建一个稳定层
big_data_frame <- data.frame(x = rnorm(n = 200000), y = rnorm(n = 200000))
steady_layer <- reactiveVal(value = geom_point(data = big_data_frame, mapping = aes(x = x, y = y))
然后创建这样的情节
output$plotx <- renderPlot({
small_df <-
data.frame(
x = sample(x = -4:4, size = input$slider_input, replace = T),
y = sample(x = -4:4, size = input$slider_input, replace = T)
)
ggplot() +
steady_layer() +
geom_point(data = small_df, mapping = aes(x = x, y = y)
})
但这并没有帮助,因为重新绘制过程需要时间而不是创建 ggplot 图层本身。
虽然我可以想象解决方案可能是创建一个大图的 .png 并将其用作 HTML 的背景和 output$plotx
的 CSS 通过使其成为反应式 UI 我还没有成功地操纵 HTML 和 CSS。
感谢任何帮助。提前致谢!
您需要了解 renderPlot
的工作原理。它首先使用R png
函数创建一个png,然后将其发送到客户端浏览器。当绘图的数据发生变化时,将重新创建此 png。因此,不可能重新绘制 png 的一部分。所以给一个已经存在的plot加点总是会用到slow points + new points
的时间,所以在目前的闪亮机制下,不重新计算这些慢点是不可能的。一个可能的选择是使用 plotly
和 proxy 。它由 HTML 和 Javascript 组成,所以是的,您可以通过部分更新来完成。详见link,此处不再赘述。根据我的个人经验,它的速度并不像您想要的那样快milliseconds-level。
所以在这里我有一个聪明的技巧给你:为什么我们在同一个情节上更新?我们可以用一个图作为背景,它很慢,但我们只渲染一次,我们再也不会碰它了。然后我们更新另一个只有几个点的图,我们将这个图堆叠在慢图的顶部。
方法如下:
- 添加一些 CSS 技巧来进行堆叠
- 渲染慢情节
- 使用透明渲染快速绘图
library(shiny)
library(ggplot2)
ui <- fluidPage(
sliderInput(inputId = "slider_input", label = "Reactive values:", min = 1, max = 100, value = 10),
div(
class = "large-plot",
plotOutput(outputId = "plot_bg"),
plotOutput(outputId = "plotx")
),
tags$style(
"
.large-plot {
position: relative;
}
#plot_bg {
position: absolute;
}
#plotx {
position: absolute;
}
"
)
)
slow_server <- function(input, output, session){
base_data <- reactiveVal(value = data.frame(x = rnorm(n = 200000), y = rnorm(n = 200000)))
output$plot_bg <- renderPlot({
ggplot(base_data()) +
geom_point(aes(x,y)) +
scale_x_continuous(breaks = -4:4) +
scale_y_continuous(breaks = -4:4) +
xlim(-5, 5) +
ylim(-5, 5)
})
output$plotx <- renderPlot({
data.frame(
x = sample(x = -4:4, size = input$slider_input, replace = T),
y = sample(x = -4:4, size = input$slider_input, replace = T)
) %>%
ggplot() +
geom_point(aes(x,y), color = "red", size = 3) +
scale_x_continuous(breaks = -4:4) +
scale_y_continuous(breaks = -4:4) +
theme(
panel.background = element_rect(fill = "transparent"),
plot.background = element_rect(fill = "transparent", color = NA),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.background = element_rect(fill = "transparent"),
legend.box.background = element_rect(fill = "transparent")
)+
xlim(-5, 5) +
ylim(-5, 5)
}, bg="transparent")
}
shinyApp(ui = ui, server = slow_server)