r - ggplot no funciona correctamente en eventReactive() en shiny

CorePress2024-01-25  6

Perdí horas para descubrir por qué mi gráfico se actualiza automáticamente cuando cambio las entradas mientras se suponía que debía esperar el botón Ejecutar, pero simplemente ignoró ese paso y finalmente terminé encontrando a ggplot como el causante del problema. Este es mi código mínimo:

library(ggplot2)
library(tidyverse)

varnames <- names(cars)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      fluidRow(
        column(
          width = 12,

          # Variables Inputs:
          varSelectInput("variables", "Select Input Variables", cars, multiple = TRUE),
          selectizeInput("outvar", "Select Output Variable", choices = varnames, "speed", multiple = F),

          # Run Button
          actionButton(inputId = "run", label = "Run")
        )
      )
    ),

    # Main panel for displaying outputs ----
    mainPanel(
      plotOutput("plot")
    )
  )
)

server <- function(input, output, session) {
  
  df <- reactive({
    cars %>% dplyr::select(!!!input$variables, input$outvar)
  })


  plt <- eventReactive(input$run, {
    
    #Just creating lm formula
    current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
    current_formula <- as.formula(current_formula)
    #Fitting lm
    fit <- lm(current_formula, data = df())
    pred <- predict(fit, newdata = df())

    #Plotting
    ggplot(df(), aes(df()[, input$outvar], pred)) +
      labs(x = "Observed", y = "Predicted") +
      geom_point() +
      theme_bw()

     #plot(df()[, input$outvar], pred)       #This one works fine!!!!
  })


  output$plot <- renderPlot({
     plt()
  })
}

# Run the application
shinyApp(ui = ui, server = server)

¡Si ejecutas esto, notarás que a ggplot ya no le importa el botón Ejecutar después de la primera ejecución y continúa actualizándose a medida que cambias las entradas! Sin embargo, si usa la función de trazado base simple (que puse en un comentario en el código), no habrá ningún problema y ¡funciona bien! Lamentablemente necesito ggplot en mi aplicación porque el gráfico base es feo. Veo sugerencias para usar isola() para resolver este problema, pero no tengo idea de dónde debería colocarse isola() para solucionar mi problema y tampoco funciona.Tiene sentido usar aislante() cuando la función de trazado base funciona bien sin ella y es el ggplot el que genera el problema. Cualquier explicación sería apreciada.

1

Le sugiero que elimine las partes de queja de su mensaje "Estimado administrador", específicamente "no tiene ningún derecho" a escribir. y un poco menos "cuando no puedes explicarlo". Entiendo tu frustración, pero "no hay derecho" Es claramente un malentendido cómo funciona la moderación del foro. Yo sugeriría una postura menos agresiva, tal vez algo como "PrefieroNo utilice soluciones que dependan del aislamiento debido a XYZ" (sea específico) y tal vez "Soy muy interesante en soluciones que expliquen por qué la trama funciona como se desea pero ggplot no". (Por cierto, lo estoy investigando)

-r2evans

28/03/2021 a las 13:34



------------------------------------

Parece funcionar bien si sigues el método preferido de ggplot para pasar nombres de columnas, es decir, usando .data.

library(ggplot2)
library(shiny)

server <- function(input, output, session) {
  
  df <- reactive({
    cars %>% dplyr::select(!!!input$variables, input$outvar)
  })
  
  
  plt <- eventReactive(input$run, {
    
    #Just creating lm formula
    current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
    current_formula <- as.formula(current_formula)
    #Fitting lm
    fit <- lm(current_formula, data = df())
    pred <- predict(fit, newdata = df())
    

    #Plotting
    ggplot(df(), aes(.data[[input$outvar]], pred)) +
      labs(x = "Observed", y = "Predicted") +
      geom_point() +
      theme_bw()
  })
  
  
  output$plot <- renderPlot({
    plt()
  })
}

# Run the application
shinyApp(ui = ui, server = server)

1

Creo que el uso sugerido de .data está haciendo lo que hice en mi respuesta (identificando más claramente aes() como el culpable de realizar una evaluación diferida de df(), no de ggplot en sí), y es el método más canónico dentro de ggplot2. para usar .datos. Bien.

-r2evans

28/03/2021 a las 14:53



------------------------------------

Creo que el problema es que ggplot está procesando las cosas con pereza. Si realiza un cambio en su código para extraer previamente df() e ingresar$outvar, la sobrereactividad se soluciona:

  plt <- eventReactive(input$run, {
    #Just creating lm formula
    current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
    current_formula <- as.formula(current_formula)
    #Fitting lm
    fit <- lm(current_formula, data = df())
    pred <- predict(fit, newdata = df())

    #Plotting
    dat <- df()
    outv <- input$outvar
    ggplot(dat, aes(dat[, outv], pred)) +
      labs(x = "Observed", y = "Predicted") +
      geom_point() +
      theme_bw()

     #plot(df()[, input$outvar], pred)       #This one works fine!!!!
  })

El problema es que ggplot es algoehow preservando internamente parte de la reactividad.

(Coloqué la asignación a dat y outv inmediatamente antes de ggplot, solo para demostración. Podría ser más sensato asignarlos primero en el bloque eventReactive y usar dat para todo, solo para mantener la coherencia en el código (ninguno de los otro código funciona según principios vagos).

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare