You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shiny变量隔离(isolate)用法疑问:基因型表型模拟应用的响应逻辑优化需求

Fixing Reactive Dependencies for Genotype & Effect Size Updates in Shiny

Hey there! Let's work through this issue—controlling when your genotype matrix regenerates vs when only effect sizes update is a classic reactive programming challenge in Shiny. Here's how to adjust your code to match your desired behavior:

Key Problem with Your Current Code

Right now, your currG() and currBetas() are regular functions, not reactive objects. That means every time any input changes (including beta parameters), these functions re-run from scratch, regenerating the genotype matrix G even when you only want to update effect sizes. We need to explicitly define which inputs trigger G to refresh, and separate that from the inputs that only affect effect sizes.

Step-by-Step Solution

We'll use Shiny's reactive() to create a genotype matrix that only updates when N or m change, and a separate reactive for effect sizes that updates when beta-related inputs change (without regenerating G).

Here's the revised server code with explanations:

library(shiny)
library(tidyverse) # Make sure to load this for pivot_longer!

ui <- fluidPage(
  titlePanel("Phenotype simulator"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("N", "Number of individuals:", min=1000, max=10000, step = 1000, value=5000),
      sliderInput("m", "Number of markers:", min=1, max=10, step=1, value=5),
      sliderInput("betas_mu", "Mean effect:", min=0, max=5, step=.5, value=1),
      sliderInput("betas_sd", "Effect SD:", min=0, max=3, step=0.1, value=1),
      sliderInput("e_mu", "Mean error:", min=0, max=0.5, step=0.01, value=0.25),
      sliderInput("e_sd", "Error SD:", min=0, max=1, step=0.01, value=1),
      sliderInput("m_neg", "Number of markers with negative effect:", value = 0, step = 1, min = 0, max = 5 ),
      sliderInput("q", "Minor allele frequency:", min=0, max=1, step=0.01, value=.33),
      sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30)
    ),
    mainPanel(
      p("Error and simulated trait distributions", style="font-size:15pt"),
      plotOutput("distPlot"),
      p("Genotypes for the first 5 individuals", style="font-size:15pt"),
      tableOutput("genos"),
      p("Distribution of effect sizes", style="font-size:15pt"),
      plotOutput("eff_distPlot")
    )
  )
)

server <- function(input, output) {
  # 1. Reactive genotype matrix: ONLY updates when N or m change
  G <- reactive({
    p <- 1 - input$q
    q_val <- input$q
    m <- input$m
    N <- input$N
    gt <- sample(x = c(0,1,2), size = N * m, replace = T, prob = c(p^2, p*q_val, q_val^2))
    matrix(gt, ncol = m, byrow = T)
  })
  
  # 2. Reactive effect sizes: Updates when beta parameters change (uses current G without re-generating it)
  currBetas <- reactive({
    m <- ncol(G()) # Use current G's marker count (no need to read input$m directly)
    betas <- abs(rnorm(n = m, mean = input$betas_mu, sd = input$betas_sd))
    negative_idx <- sample(1:m, size=input$m_neg, replace=F)
    betas[negative_idx] <- -betas[negative_idx]
    list(betas=betas)
  })
  
  # 3. Reactive phenotype data: Uses reactive G and betas
  currPheno <- reactive({
    G_mat <- G()
    betas <- currBetas()$betas
    N <- nrow(G_mat)
    errors <- rnorm(n = N, mean = input$e_mu, sd = input$e_sd)
    y <- G_mat %*% betas + errors
    y2 <- round(y, digits = 3)
    data <- data.frame(error = errors, y = y2)
    dat <- data %>% pivot_longer(cols = c(error, y))
    list(dat=dat, betas=betas, G=G_mat)
  })
  
  output$eff_distPlot <- renderPlot({
    dat <- data.frame(value=rnorm(n = 1000, mean = input$betas_mu, sd = input$betas_sd))
    ggplot(dat, aes(x=value)) + 
      geom_histogram(fill='orange') + 
      theme_bw()
  })
  
  output$distPlot <- renderPlot({
    new_data <- currPheno()
    ggplot(new_data$dat, aes(x=value, fill=name)) + 
      geom_histogram(bins = input$bins) + 
      theme_bw() + 
      facet_wrap(~name)
  })
  
  output$genos <- renderTable({
    G_mat <- G() # Gets the current genotype matrix (only changes if N/m change)
    B <- currBetas() # Gets updated effect sizes when beta parameters change
    # Take first 5 individuals
    G_subset <- G_mat[1:5,]
    # Combine effect row with genotype rows
    tmp <- rbind(round(B$betas, 3), G_subset)
    # Add row labels
    tmp <- cbind(c('effect', paste0("ind", 1:nrow(G_subset))), tmp)
    colnames(tmp) <- c(" ", paste0("SNP", 1:ncol(G_subset)))
    tmp
  }, colnames = TRUE )
}

shinyApp(ui = ui, server = server)

What Changed & Why

  • Reactive G object: Wrapped the genotype generation in reactive({}), which only re-runs when input$N or input$m change (these are the only inputs inside the reactive block). This ensures G stays static when adjusting beta parameters.
  • Reactive currBetas: Now depends on G() (but since G only updates when N/m change, this doesn't trigger unnecessary G regeneration) and the beta-related inputs (betas_mu, betas_sd, m_neg). When those inputs change, only the effect sizes recalculate.
  • Table output: Pulls the static (until N/m change) G_mat and the dynamic currBetas(), so the genotype rows stay the same while the first row (effect sizes) updates when beta parameters are adjusted.
  • Fixed dependency issues: Removed redundant input reads (like reading input$m when we can get it from ncol(G())) to keep dependencies clean.

Why Your Original isolate() Attempt Didn't Work

When you used isolate() inside regular functions, Shiny still tracks all inputs accessed anywhere in the function call chain. By using reactive objects, we explicitly define which inputs trigger updates, which gives you precise control over when each part of your app refreshes.

内容的提问来源于stack exchange,提问作者kiero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 23:13:10