2 votes

R Processus des blocs de promesses/futurs brillants

Je n'arrive pas à trouver la faille dans la logique de mon script qui provoque le blocage du processus Shiny malgré l'essai d'utiliser l'asynchronisme. future + promises stratégie.

J'ai ce (simplifié) serveur script. Il fonctionne en principe, mais je n'expérimente pas la parallélisation. Je simule 2 déclenchements simultanés et le deuxième événement déclencheur attend que le premier se résolve.

Pouvez-vous indiquer ce qui ne va pas ici ? J'ai lu plusieurs manuels, mais j'ai toujours du mal à trouver la logique.

Minimal Example, une application Shiny à un seul fichier :

## load libs

library(shiny)
library(DT)
library(ggplot2)

ui <- navbarPage(
     tabPanel(
          " "
          , sidebarLayout(
               sidebarPanel(
                    br()
                    , actionButton(
                         "run_trends"
                         , label = "run"
                         , style="color: #fff; background-color: #337ab7; border-color: #2e6da4"
                    )
                    , br()
               )
               , mainPanel(
                    textOutput("trends_time")
                    , br()
                    , br()
                    , plotOutput('trend_plotly')
                    , br()
                    , p("results")
                    , br()
                    , DTOutput('trend_tbl')
                    , br()
                    , br()
               )
          )
     )
)

server <- function(input, output, session)
{

     dt_trend <- observeEvent(
          input$run_trends,
          {

               ## load libs

               library(data.table)
               library(ggplot2)
               library(promises)
               library(future)

               plan(multiprocess)

               dat_func <- function()
               {

                    start_time <- Sys.time()

                    dt <- data.table(x = rnorm(100), y = rnorm(100))

                    trendy_tbl <- head(dt, 10)

                    ggplo1 <- ggplot(dt) + geom_point(aes(x=x,y=y))

                    Sys.sleep(10)

                    list(
                         trendy_tbl
                         , ggplo1
                         , paste0('time: ', round(Sys.time() - start_time), ' .')
                    )
               }

               f <- future({
                    dat_func()
               })

               #res <- future::value(f)

               output$trend_tbl <- renderDT({future::value(f)[[1]]})

               output$trend_plotly <- renderPlot({future::value(f)[[2]]})

               output$trends_time <- renderText({future::value(f)[[3]]})

          })

}

# Return a Shiny app object
shinyApp(ui = ui, server = server, options = list(port = 4600, host = '0.0.0.0'))

3voto

antoine-sac Points 2477

Essayez ça :

library(data.table)
library(ggplot2)
library(promises)
library(future)

plan(multiprocess)

server <- function(input, output, session) {

     dt_trend <- eventReactive(
          input$run_trends,
          {
               dat_func <- function() {

                    start_time <- Sys.time()
                    dt <- data.table(x = rnorm(100), y = rnorm(100))
                    trendy_tbl <- head(dt, 10)
                    ggplo1 <- ggplot(dt) + geom_point(aes(x=x,y=y))
                    Sys.sleep(10)
                    list(
                         trendy_tbl
                         , ggplo1
                         , paste0('time: ', round(Sys.time() - start_time), ' .')
                    )
               }

               # Returning future
               future({
                    dat_func()
               })
     })

     output$trend_tbl <- renderDT({dt_trend()[[1]]})
     output$trend_plotly <- renderPlot({dt_trend()[[2]]})
     output$trends_time <- renderText({dt_trend()[[3]]})

}

Les idées clés sont les suivantes :

  • Assurez-vous d'utiliser shiny version > 1.1.0 (avril 2018) qui a introduit le support asynchrone.
  • ne pas utiliser future::value car il bloque et attend l'avenir, précisément ce que nous voulons éviter.
  • à la place, renvoyer le futur dans un reactive . Dans ce cas, cela signifie utiliser eventReactive au lieu de observeEvent .
  • Accédez à la valeur du futur via le réactif. Notez que votre valeur reactive est maintenant un futur ! Cela signifie que vous devez utiliser des gestionnaires de futurs pour utiliser la valeur. (*)
  • Vous pouvez également renvoyer un futur dans n'importe quel renderXXX fonction. Utile par exemple si vous avez de grands tracés qui prennent du temps.

(*) En pratique, cela signifie que vous devez faire

renderDT(dt_trend() %>% then(~.[[1]]))
# or 
renderDT(dt_trend() %>...% `[[`(1))

donde then provient de la promises et [[ est la fonction de sous-ensemble de la base R ( x[[i]] est en fait un sucre sémantique pour `[[`(x, i) !).

Dans votre exemple, vous calculez tout en une seule fois. future en dt_trend . Vous pouvez envisager d'utiliser plusieurs petits contrats à terme à la place. Vous pouvez charger les données dans un reactive avec un future, puis garder le code de sortie dans votre renderXXX enveloppées dans un future si nécessaire.

Il y a une bonne vignette sur l'utilisation des promesses avec shiny disponible en exécutant vignette("shiny", package = "promises") (**). Il est également disponible en ligne sur grue ou sur le blog rstudio.

(**) Si vous avez installé promises avec install_github("rstudio/promises") vous devrez très probablement réinstaller avec build_vignettes = TRUE d'abord.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X