reactive future_map_dfr ?

I'm struggling to figure out what is that I'm doing wrong in a shiny dashboard. The error I'm getting says:

Warning: Error in <Anonymous>: ℹ In index: 1.
Caused by error in `input$size`:
! Can't access reactive value 'size' outside of reactive consumer.
ℹ Do you need to wrap inside reactive() or observe()?

But the function is inside eventReactive. This is the relevant part of the code (I think)

  sock_sim <- eventReactive(input$run_sim, {
    show_modal_spinner()  
    on.exit(remove_modal_spinner())
    set.seed(input$seed)  
    future_map_dfr(1:input$n_sims,
                   ~simulate_socks(n_picked = 11,
                                   mu = input$mu,
                                   size = input$size,
                                   shape1 = input$shape1,
                                   shape2 = input$shape2),
                   .options = furrr_options(seed = 123))
  })

On the other hand, if I fix the parameters like this:

  sock_sim <- eventReactive(input$run_sim, {
    show_modal_spinner()  
    on.exit(remove_modal_spinner())
    set.seed(input$seed)  
    future_map_dfr(1:input$n_sims,
                   ~simulate_socks(n_picked = 11,
                                   mu = 40,
                                   size = 12,
                                   shape1 = 15,
                                   shape2 = 2),
                   .options = furrr_options(seed = 123))
  })

The dashboard works (but I cannot get things to get recalculated when I change the inputs)

This is the whole dashboard:

library(shiny)
library(ggplot2)
library(dplyr)
library(furrr)
library(shinybusy)
library(shinydashboard)

# Enable parallel processing with the number of cores available
plan(multisession, workers = availableCores())

# Define the number of socks picked
n_picked <- 11

simulate_socks <- function(n_picked, mu, size, shape1, shape2) {
  # Generate total number of socks from prior
  n_socks <- rnbinom(1, mu = mu, size = size)
  
  # Generate proportion of paired socks from prior
  prop_pairs <- rbeta(1, shape1 = shape1, shape2 = shape2)
  
  # Calculate number of pairs and odd socks
  n_pairs <- round(floor(n_socks / 2) * prop_pairs)
  n_odd <- n_socks - n_pairs * 2
  
  # Simulate picking socks
  socks <- rep(seq_len(n_pairs + n_odd), rep(c(2, 1), c(n_pairs, n_odd)))
  picked_socks <- sample(socks, size = min(n_picked, n_socks))
  sock_counts <- table(picked_socks)
  
  # Return results
  tibble(
    unique = sum(sock_counts == 1),
    pairs = sum(sock_counts == 2),
    n_socks = n_socks,
    n_pairs = n_pairs,
    n_odd = n_odd,
    prop_pairs = prop_pairs
  )
}

ui <- dashboardPage(
  skin = "black",
  dashboardHeader(title = "Karl's Socks"),
  dashboardSidebar(
    numericInput(
      inputId = "seed",
      label = "Random Seed",
      value = 123,
      min = 1,
      step = 1
    ),
    numericInput(
      inputId = "n_sims",
      label = "Number of Simulations",
      value = 10000,
      min = 100,
      step = 100
    ),
    sliderInput(
      inputId = "mu",
      label = "mu",
      min = 15,
      max = 60,
      value = 40,
      step = 1
    ),
    sliderInput(
      inputId = "size",
      label = "Size",
      min = 3,
      max = 10,
      value = 4,
      step = 1
    ),
    sliderInput(
      inputId = "shape1",
      label = "Shape 1",
      min = 2,
      max = 20,
      value = 15,
      step = 1
    ),
    sliderInput(
      inputId = "shape2",
      label = "Shape 2",
      min = 2,
      max = 8,
      value = 2,
      step = 1
    ),
    actionButton("run_sim", "Run Simulation")
  ),
  dashboardBody(
    use_busy_spinner(spin = "fading-circle"),
    fluidRow(
      box(
        title = "Prior Distribution for Total Number of Socks", 
        status = "danger", solidHeader = TRUE,
        collapsible = TRUE,
        plotOutput("prior_socks")
      ),
      
      box(
        title = "Prior Distribution for Proportion of Paired Socks", 
        status = "danger", solidHeader = TRUE,
        collapsible = TRUE,
        plotOutput("prior_prop")
      )
    ),
    
    fluidRow(
      box(
        title = "Prior and Posterior Distributions of Total Socks", 
        status = "danger", solidHeader = TRUE,
        collapsible = TRUE,
        plotOutput("distribution_plot")
      ),
      
      box(
        title = "Joint Posterior Distribution of Pairs and Odd Socks", 
        status = "danger", solidHeader = TRUE,
        collapsible = TRUE,
        plotOutput("joint_plot")
      )
    ),
    
    fluidRow(
      valueBoxOutput("pr_pairs")
    )
  )
)

server <- function(input, output) {
  
  sock_sim <- eventReactive(input$run_sim, {
    show_modal_spinner()  
    on.exit(remove_modal_spinner())
    set.seed(input$seed)  
    future_map_dfr(1:input$n_sims,
                   ~simulate_socks(n_picked = 11,
                                   mu = input$mu,
                                   size = input$size,
                                   shape1 = input$shape1,
                                   shape2 = input$shape2),
                   .options = furrr_options(seed = 123))
  })
  
  # Filter for matching simulations (11 unique socks, 0 pairs)
  post_samples <- reactive({
    sock_sim() %>% 
      filter(unique == 11, pairs == 0)
  })
  
  output$pr_pairs <- renderValueBox({
    req(post_samples())
    valueBox(
      scales::percent(mean(post_samples()$n_pairs > 14)),
      "Pr[# Pairs > 14]", icon = icon("socks"),
      color = "red"
    )
  })
  
  # Prepare data for plotting
  prior_data <- reactive({
    sock_sim() %>%
      count(n_socks) %>%
      mutate(prop = n / sum(n),
             type = "Prior")
  })
  
  posterior_data <- reactive({
    if(nrow(post_samples()) == 0) {
      return(tibble(n_socks = numeric(), prop = numeric(), type = character())) 
    }
    post_samples() %>%
      count(n_socks) %>%
      mutate(prop = n / sum(n),
             type = "Posterior")
  })
  
  output$prior_socks <- renderPlot({
    # Dataframe of possible sock counts
    sock_counts <- data.frame(n_socks = 0:100)
    
    # Calculate probabilities from the negative binomial distribution
    sock_counts$probability <- dnbinom(sock_counts$n_socks,
                                       mu = input$mu,
                                       size = input$size)
    
    # Create the histogram
    ggplot(sock_counts, aes(x = n_socks, y = probability)) +
      geom_col(fill = "skyblue", color = "black")+ 
      scale_y_continuous(labels = scales::percent) +
      theme_minimal() +
      labs(x = "Total Number of Socks", y = "Probability")
  })
  
  output$prior_prop <- renderPlot({
    # Create a sequence of proportions from 0 to 1
    proportions <- seq(0, 1, length.out = 100)
    
    # Calculate density values from the beta distribution
    density_values <- dbeta(proportions, shape1 = input$shape1,
                            shape2 = input$shape2)
    
    # Create the density plot
    ggplot(data.frame(proportion = proportions, density = density_values),
           aes(x = proportion, y = density)) +
      geom_line(color = "darkgreen") +
      theme_minimal() +
      labs(x = "Proportion of Paired Socks", y = "Density")
  })
  
  output$distribution_plot <- renderPlot({
    req(prior_data(), posterior_data())
    plot_data <- bind_rows(prior_data(), posterior_data())
    
    ggplot(plot_data, aes(x = n_socks, y = prop, fill = type)) +
      geom_col(position = "dodge", alpha = 0.7) +
      scale_fill_manual(values = c("Prior" = "lightgreen", "Posterior" = "skyblue")) +
      scale_y_continuous(labels = scales::percent) +
      labs(x = "Number of Socks", y = "Probability", fill = "Distribution") +
      theme_minimal() +
      theme(legend.position = "top")
  })
  
  output$joint_plot <- renderPlot({
    req(post_samples())
    if(nrow(post_samples()) > 0) {
      ggplot(post_samples(), aes(x = n_pairs, y = n_odd)) +
        geom_hex(bins = 30) +
        scale_fill_viridis_c() +
        labs(x = "Number of Pairs", y = "Number of Odd Socks") +
        theme_minimal()
    } else {
      ggplot() + 
        annotate("text", x = 0.5, y = 0.5, label = "No matching simulations found") +
        theme_void()
    }
  })
}

shinyApp(ui, server)

How can I fix this?

I haven't tried future's in my work, but I can understand the issue if its something like the future function lazily evaluates its expression, and then this is happening in another context which shiny considers out of the reactive scope. My instincts are to simply make the value local in the expression that calls the future.
maybe

 sock_sim <- eventReactive(input$run_sim, {
    show_modal_spinner()  
    on.exit(remove_modal_spinner())
    set.seed(input$seed)  
    local_size <-  input$size #  <<---
     local_mu <- input$mu     #  <<--
    future_map_dfr(1:input$n_sims,
                   ~simulate_socks(n_picked = 11,
                                   mu = local_mu, #  <<---
                                   size = local_size,   #  <<--
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.