Riksrevisjonen / pioneeR

R package for running a Shiny app for DEA analysis
https://riksrevisjonen.github.io/pioneeR/
GNU General Public License v3.0
6 stars 2 forks source link

Add warning if the power of the analysis is low #83

Closed ohjakobsen closed 3 weeks ago

ohjakobsen commented 5 months ago

As a rule of thumb, a DEA model should have at least as many DMUs as the product of the number of inputs and outputs or three times the number of combined inputs and outputs (either n_inputs*n_outputs or (n_inputs+n_outputs)* 3. The app should issue a warning if the number of DMUs are below these numbers.

Aeilert commented 4 months ago

We put this warning as part of get_dims() and then just forward it to the UI.

ohjakobsen commented 3 months ago

@Aeilert: since get_dims() can be called from an interactive session as well, we should be mindful of where we put the code to notify the UI. Notification could be done with a warning banner (as with the list-wise deletion warning) or a toast message (I prefer the former for consistency). This logic is probably better to put in the server() function. That said, we should look into how warnings from either warning() or cli::cli_warn() can bubble up to the UI.

ohjakobsen commented 2 months ago

This could be a way to implement a solution where we catch warnings (and errors) from R functions and inform the user in the UI:

library(bslib)

ui <- bslib::page_fluid(
  actionButton('warn', 'Warn me!'),
  actionButton('stop', 'Stop me!'),
  uiOutput('messages')
)

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

  my_warning_fun <- function() {
    cli::cli_warn('I must warn you!')
  }

  my_abort_fun <- function() {
    cli::cli_abort('I must stop you right there!')
  }

  send_to_ui <- function(level, message) {
    classes <- switch(
      level,
      warning = 'alert alert-warning',
      error = 'alert alert-danger',
      'alert alert-info'
    )
    div <- div(class = classes, message)
    div
  }

  catch_and_inform <- function(fun) {
    tryCatch(
      fun(),
      warning = \(e) {
        if (shiny::isRunning()) {
          return(cli::ansi_strip(e$message))
        }
        return(NA)
      },
      error = \(e) {
        if (shiny::isRunning()) {
          return(cli::ansi_strip(e$message))
        }
        return(NA)
      }
    )
  }

  messages <- reactiveVal(tagList())

  observeEvent(input$warn, {
    test <- catch_and_inform(my_warning_fun)
    messages(send_to_ui('warning', test))
  })

  observeEvent(input$stop, {
    test <- catch_and_inform(my_abort_fun)
    messages(send_to_ui('error', test))
  })

  output$messages <- renderUI(messages())

}

shinyApp(ui, server)