最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

r - How to use futures to render each plot one by one rather than all at the same time? - Stack Overflow

programmeradmin0浏览0评论

I have a shiny app that uses a function to generate N number of plots (in this example 10).

Rather than use a map to create all 10 charts at the same time, below I am trying to use the futures package to render each plot one by one as they are ready.

library(shiny)
library(plotly)
library(purrr)
library(future)
library(promises)

# Enable parallel processing
plan(multisession)

# Generate 10 titles
titles <- paste0("Chart", 1:10)

# Create the list of lists
charts <- map(titles, ~ list(title = .x)) %>%
  setNames(titles)

chart_func <- function(title) {
  Sys.sleep(runif(1, 0.5, 2))  # Simulate some computation time
  plotly::plot_ly() %>%
    layout(
      title = title
    )
}

ui <- fluidPage(
  actionButton("make_plots", "Make Plots"),
  uiOutput("all_plots")
)

server <- function(input, output, session) {
  
  # Define plot names
  plot_names <- names(charts)
  
  # Render the UI with placeholders for all plots
  output$all_plots <- renderUI({
    purrr::map(plot_names, ~tags$div(class = "single-plot", plotlyOutput(.x)))
  })
  

  # Generate plots asynchronously upon button click
  observeEvent(input$make_plots, {
    
    map(charts, function(chart) {
      future({
        chart_func(chart$title)
      }, seed = TRUE) %...>% { result ->
        output[[chart$title]] <- renderPlotly({
          result
        })
      }
    })
    
  })
}

shinyApp(ui, server)

This code is close but I am getting errors and unsure what to try next. I know that method chaining is correct I just need some help with the output.

I have a shiny app that uses a function to generate N number of plots (in this example 10).

Rather than use a map to create all 10 charts at the same time, below I am trying to use the futures package to render each plot one by one as they are ready.

library(shiny)
library(plotly)
library(purrr)
library(future)
library(promises)

# Enable parallel processing
plan(multisession)

# Generate 10 titles
titles <- paste0("Chart", 1:10)

# Create the list of lists
charts <- map(titles, ~ list(title = .x)) %>%
  setNames(titles)

chart_func <- function(title) {
  Sys.sleep(runif(1, 0.5, 2))  # Simulate some computation time
  plotly::plot_ly() %>%
    layout(
      title = title
    )
}

ui <- fluidPage(
  actionButton("make_plots", "Make Plots"),
  uiOutput("all_plots")
)

server <- function(input, output, session) {
  
  # Define plot names
  plot_names <- names(charts)
  
  # Render the UI with placeholders for all plots
  output$all_plots <- renderUI({
    purrr::map(plot_names, ~tags$div(class = "single-plot", plotlyOutput(.x)))
  })
  

  # Generate plots asynchronously upon button click
  observeEvent(input$make_plots, {
    
    map(charts, function(chart) {
      future({
        chart_func(chart$title)
      }, seed = TRUE) %...>% { result ->
        output[[chart$title]] <- renderPlotly({
          result
        })
      }
    })
    
  })
}

shinyApp(ui, server)

This code is close but I am getting errors and unsure what to try next. I know that method chaining is correct I just need some help with the output.

Share Improve this question edited Nov 21, 2024 at 14:55 ismirsehregal 33.7k5 gold badges45 silver badges92 bronze badges asked Nov 19, 2024 at 22:56 Maya GansMaya Gans 811 silver badge4 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 4

To unblock the session calculating the plots you'll need to wrap the future with shiny::ExtendedTask(). Using future or future_promise without ExtendedTask will only unblock concurrent shiny sessions - please see this related but closed issue.

Please check ?shiny::ExtendedTask:

The ExtendedTask class allows you to have an expensive operation that is started by a reactive effect, and whose (eventual) results can be accessed by a regular observer, calc, or output; but during the course of the operation, the current session is completely unblocked, allowing the user to continue using the rest of the app while the operation proceeds in the background.

Applied to your example:

library(shiny)
library(plotly)
library(purrr)
library(future)
library(promises)
library(bslib)

# Enable parallel processing
plan(multisession)

# Generate 10 titles
titles <- paste0("Chart", 1:10)

# Create the list of lists
charts <- map(titles, ~ list(title = .x)) %>%
  setNames(titles)

chart_func <- function(title) {
  Sys.sleep(runif(1, 0.5, 2))  # Simulate some computation time
  plotly::plot_ly(type = "scatter", mode = "markers") %>%
    layout(
      title = title
    )
}

ui <- fluidPage(
  input_task_button("make_plots", "Make Plots"),
  uiOutput("all_plots")
)

server <- function(input, output, session) {
  # Define plot names
  plot_names <- names(charts)
  
  compute_plot <- ExtendedTask$new(function(chart) {
    future_promise({
      list(title = chart$title, plotly_obj = chart_func(chart$title))
    }, seed = TRUE)
  }) |> bind_task_button("make_plots")
  
  # Render the UI with placeholders for all plots
  output$all_plots <- renderUI({
    purrr::map(plot_names, ~tags$div(class = "single-plot", plotlyOutput(.x)))
  })
  
  # Generate plots asynchronously upon button click
  observeEvent(input$make_plots, {
    map(charts, function(chart) {
      compute_plot$invoke(chart)
    })
  })
  
  observeEvent(compute_plot$result(), {
    output[[compute_plot$result()$title]] <- renderPlotly({
      isolate(compute_plot$result()$plotly_obj)
    })
  })
}

shinyApp(ui, server)

Please also check this related article.

发布评论

评论列表(0)

  1. 暂无评论