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

Shiny应用样本生成无随机性问题排查求助

中心极限定理Shiny应用样本重复问题解决

我正在开发一个演示**中心极限定理(样本均值分布场景)**的Shiny应用。目前能生成数量和尺寸符合要求的样本,但所有样本完全相同——直方图显示所有样本的均值完全一致。

自测后怀疑问题出在sample_i()响应式表达式,后续响应式表达式运行正常。请问是否需要新增响应式表达式来修复该问题?

原应用代码如下:

library(shiny)

ui <- fluidPage(
  titlePanel("Demonstration of the Central Limit Theorem"),
  fluidRow(
    column(4, selectInput("dist", "Distribution",
                          c("Normal", "Uniform", "Poisson", "Binomial"))),
    column(4, numericInput("n_sample", "Number of samples", value = 50)),
    column(4, numericInput("size", "Sample size", value = 100))
  ), 
  tabsetPanel(
    id = "params",
    type = "hidden",
    tabPanel("Normal",
             numericInput("mean", "Mean", value = 0),
             numericInput("sd", "SD", value = 1)
    ),
    tabPanel("Uniform",
             numericInput("min", "Min", value = 0),
             numericInput("max", "Max", value = 1)
    ),
    tabPanel("Poisson",
             numericInput("r", "Rate", value = 1)
    ),
    tabPanel("Binomial",
             numericInput("p", "Probability of success", value = 0.5),
             numericInput("n", "Number of trials", value = 10)
    )
  ),
  plotOutput("hist"),
  verbatimTextOutput("length")
)

server <- function(input, output, session) {
  observeEvent(input$dist, {
    updateTabsetPanel(inputId = "params", selected = input$dist)
  })
  
  sample_i <- reactive({
    switch(input$dist, 
      Normal = rnorm(input$size, input$mean, input$sd),
      Uniform = runif(input$size, input$min, input$max), 
      Poisson = rpois(input$size, input$r), 
      Binomial = rbinom(input$size, input$n, input$p))
  })
  sample_dist <- reactive({
    replicate(n = input$n_sample, sample_i())
  })
  sample_dist_mean <- reactive({
      apply(sample_dist(), MARGIN = 2, mean) |>
        unlist() |> 
        as.numeric()
  })
  
  output$hist <- renderPlot(hist(sample_dist_mean()))
  output$length <- renderPrint(head(sample_dist(), n = 5))
}

shinyApp(ui, server)

注:当样本数量设为12时,控制台通过length组件输出如下内容:

[,1]       [,2]       [,3]       [,4]       [,5]       [,6]
[1,]  0.5953571  0.5953571  0.5953571  0.5953571  0.5953571  0.5953571
[2,]  0.8323953  0.8323953  0.8323953  0.8323953  0.8323953  0.8323953
[3,] -1.0366900 -1.0366900 -1.0366900 -1.0366900 -1.0366900 -1.0366900
[4,]  2.1517537  2.1517537  2.1517537  2.1517537  2.1517537  2.1517537
[5,] -1.2565259 -1.2565259 -1.2565259 -1.2565259 -1.2565259 -1.2565259
           [,7]       [,8]       [,9]      [,10]      [,11]      [,12]
[1,]  0.5953571  0.5953571  0.5953571  0.5953571  0.5953571  0.5953571
[2,]  0.8323953  0.8323953  0.8323953  0.8323953  0.8323953  0.8323953
[3,] -1.0366900 -1.0366900 -1.0366900 -1.0366900 -1.0366900 -1.0366900
[4,]  2.1517537  2.1517537  2.1517537  2.1517537  2.1517537  2.1517537
[5,] -1.2565259 -1.2565259 -1.2565259 -1.2565259 -1.2565259 -1.2565259

问题原因

sample_i()作为响应式表达式,在replicate调用时只会被求值一次,返回的是同一个样本向量,replicate只是把这个向量重复了input$n_sample次,导致所有列(样本)完全相同。不需要新增响应式表达式,只需调整sample_dist的实现逻辑即可。

修复方案

方案1:将sample_i改为普通函数

把响应式表达式改成普通函数,让replicate每次迭代都调用函数生成新样本:

server <- function(input, output, session) {
  observeEvent(input$dist, {
    updateTabsetPanel(inputId = "params", selected = input$dist)
  })
  
  # 改为普通函数而非响应式表达式
  sample_i <- function() {
    switch(input$dist, 
      Normal = rnorm(input$size, input$mean, input$sd),
      Uniform = runif(input$size, input$min, input$max), 
      Poisson = rpois(input$size, input$r), 
      Binomial = rbinom(input$size, input$n, input$p))
  }
  
  sample_dist <- reactive({
    replicate(n = input$n_sample, sample_i())
  })
  
  sample_dist_mean <- reactive({
      apply(sample_dist(), MARGIN = 2, mean) |>
        unlist() |> 
        as.numeric()
  })
  
  output$hist <- renderPlot(hist(sample_dist_mean()))
  output$length <- renderPrint(head(sample_dist(), n = 5))
}

方案2:直接在replicate中写生成逻辑

去掉单独的sample_i,直接把生成逻辑放到replicate的迭代体里:

server <- function(input, output, session) {
  observeEvent(input$dist, {
    updateTabsetPanel(inputId = "params", selected = input$dist)
  })
  
  sample_dist <- reactive({
    replicate(n = input$n_sample, {
      switch(input$dist, 
        Normal = rnorm(input$size, input$mean, input$sd),
        Uniform = runif(input$size, input$min, input$max), 
        Poisson = rpois(input$size, input$r), 
        Binomial = rbinom(input$size, input$n, input$p))
    })
  })
  
  sample_dist_mean <- reactive({
      apply(sample_dist(), MARGIN = 2, mean) |>
        unlist() |> 
        as.numeric()
  })
  
  output$hist <- renderPlot(hist(sample_dist_mean()))
  output$length <- renderPrint(head(sample_dist(), n = 5))
}

原理说明

响应式表达式会缓存结果,只要依赖(如input$dist、input$size等)没有变化,就不会重新计算。改成普通函数或直接写入replicate后,每次迭代都会执行随机生成逻辑,得到不同的样本集合,均值也会呈现符合中心极限定理的正态分布。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:55:17