Eu preciso gerar números aleatórios seguindo uma distribuição normal dentro do intervalo $ (a, b) $. (Estou trabalhando em R.)

Eu sei que a função rnorm(n,mean,sd) irá gerar números aleatórios seguindo a distribuição normal, mas como definir os limites de intervalo dentro disso? Existe alguma função R específica disponível para isso?

Comentários

  • Por que você deseja fazer isso? Se ‘ for limitado, pode ‘ realmente ser normal. O que você está tentando alcançar?
  • x <- rnorm(n, mean, sd); x <- x[x > lower.limit & x < upper.limit]
  • @Hugh that ‘ é ótimo … contanto que você não ‘ se importe com quantos valores aleatórios obterá.

Resposta

Parece que você deseja simular uma distribuição truncada e em seu exemplo específico , um normal truncado .

Há uma variedade de métodos para fazer isso, alguns simples, outros relativamente eficiente.

Ilustrarei algumas abordagens em seu exemplo normal.

  1. Aqui está um método muito simples para gerar um de cada vez (em algum tipo de pseudocódigo ):

    $ \ tt {repeat} $ geram $ x_i $ de N (média, sd) $ \ tt {until} $ inferior $ \ leq x_i \ leq $ superior

    insira a descrição da imagem aqui

    Se a maior parte da distribuição estiver dentro dos limites, isso é bastante razoável, mas pode ficar bem lento se você quase sempre gera fora dos limites.

    No R, você poderia evitar o loop um por vez, computando a área dentro dos limites e gerando valores suficientes para ter quase certeza de que, após jogar fora os valores fora dos limites você ainda tinha tantos valores quanto necessários.

  2. Você poderia usar aceitar-rejeitar com alguma função de majoração adequada durante o intervalo (em alguns casos, ser bom o suficiente). Se os limites fossem razoavelmente estreitos em relação ao dp. mas você não estava muito adiantado, uma majoração uniforme funcionaria bem com o normal, por exemplo.

    insira a descrição da imagem aqui

  3. Se você tiver um cdf razoavelmente eficiente e um cdf inverso (como pnorm e qnorm para o distribuição normal em R), você pode usar o método inverse-cdf descrito no primeiro parágrafo da seção de simulação da página da Wikipedia no normal truncado . [Com efeito isso é o mesmo que pegar um uniforme truncado (truncado nos quantis exigidos, que na verdade não requer rejeições, já que é apenas outro uniforme) e aplicar o cdf normal inverso a ele. Observe que isso pode falhar se você “estiver na cauda]

    insira a descrição da imagem aqui

  4. Existem outras abordagens; a mesma página da Wikipedia menciona a adaptação do método zigurate , que deve funcionar para uma variedade de distribuições.

O mesmo link da Wikipedia menciona dois pacotes específicos (ambos no CRAN) com funções para gerar normais truncados:

O pacote MSM em R tem uma função, rtnorm, que calcula as retiradas de um truncado normal. O pacote truncnorm em R também tem funções para extrair de um normal truncado.


Olhando ao redor, muito disso é coberto nas respostas de outras perguntas (mas não exatamente duplicatas, pois esta pergunta é mais geral do que apenas o normal truncado) … veja a discussão adicional em

a. Esta resposta

b. A resposta de Xi “an” aqui , que tem um link para seu artigo arXiv (junto com algumas outras respostas valiosas).

Resposta

A abordagem rápida e suja é usar a regra 68-95-99,7 .

Em uma distribuição normal, 99,7% dos valores estão dentro de 3 desvios padrão da média. Portanto, se você definir sua média para o meio de seus valores mínimo e máximo desejados, e definir seu desvio padrão para 1/3 de sua média, obterá (principalmente) valores que estão dentro do intervalo desejado. Então você pode simplesmente limpar o resto.

minVal <- 0 maxVal <- 100 mn <- (maxVal - minVal)/2 # Generate numbers (mostly) from min to max x <- rnorm(count, mean = mn, sd = mn/3) # Do something about the out-of-bounds generated values x <- pmax(minVal, x) x <- pmin(maxVal, x) 

Recentemente, enfrentei esse mesmo problema, tentando gerar notas aleatórias dos alunos para dados de teste. No código acima, usei pmax e pmin para substituir os valores fora dos limites pelos limites mínimo ou máximo valor.Isso funciona para o meu propósito, porque estou gerando pequenas quantidades de dados, mas para quantidades maiores, isso resultará em saliências perceptíveis nos valores mínimo e máximo. Portanto, dependendo de seus objetivos, pode ser melhor descartar esses valores, substituí-los com NA s ou “role-os novamente” até que eles “estejam dentro dos limites.

Comentários

  • Por que se preocupar em fazer isso? É tão simples gerar números aleatórios normais e eliminar aqueles que precisam de truncamento que não é ‘ necessário ser complicado sobre isso, a menos que o truncamento desejado esteja próximo a 100% da área da densidade.
  • Talvez eu ‘ m interpretando mal a pergunta original. Eu me deparei com essa pergunta ao tentar descobrir como realizar uma tarefa de programação não diretamente relacionada às estatísticas em R, e eu ‘ só agora notei que esta página é uma troca de pilha de estatísticas , não uma troca de pilha de programação. 🙂 No meu caso, eu queria gerar uma quantidade específica de inteiros aleatórios, com valores variando de 0 a 100, e queria que os valores gerados caíssem em uma bela curva em forma de sino nessa faixa. Desde que escrevi isso, ‘ percebi que sample(x=min:max, prob=dnorm(...)) talvez seja uma maneira mais fácil de fazer isso.
  • @Glen_b Aaron Wells menciona sample(x=min:max, prob=dnorm(...)) que parece um pouco mais curto do que sua resposta.
  • Mas observe que o sample() truque só é útil se você ‘ estiver tentando escolher números inteiros aleatórios ou algum outro conjunto de valores discretos predefinidos.

Resposta

Nenhuma das respostas aqui fornece um método eficiente de gerar variáveis normais truncadas que não envolvem rejeição de variáveis arbitrariamente grandes número de valores gerados. Se você deseja gerar valores de uma distribuição normal truncada, com limites inferior e superior especificados $ a < b $ , este pode ser feito — sem rejeição — gerando quantis uniformes no intervalo de quantis permitido pelo truncamento e usando amostragem de transformação inversa para obter os valores normais correspondentes .

Deixe $ \ Phi $ denotar o CDF da distribuição normal padrão. Queremos gerar $ X_1, …, X_N $ a partir de uma distribuição normal truncada (com parâmetro médio $ \ mu $ e parâmetro de variância $ \ sigma ^ 2 $ ) $ ^ \ dagger $ com inferior e limites de truncamento superiores $ a < b $ . Isso pode ser feito da seguinte forma:

$$ X_i = \ mu + \ sigma \ cdot \ Phi ^ {- 1} (U_i) \ quad \ quad \ quad U_1, …, U_N \ sim \ text {IID U} \ Big [\ Phi \ Big (\ frac {a- \ mu} {\ sigma} \ Big), \ Phi \ Big (\ frac {b- \ mu} {\ sigma} \ Big) \ Big]. $$

Não há função embutida para valores gerados a partir da distribuição truncada, mas é trivial programar este método usando o funções comuns para gerar variáveis aleatórias. Aqui está uma R função rtruncnorm simples que implementa esse método em algumas linhas de código.

rtruncnorm <- function(N, mean = 0, sd = 1, a = -Inf, b = Inf) { if (a > b) stop("Error: Truncation range is empty"); U <- runif(N, pnorm(a, mean, sd), pnorm(b, mean, sd)); qnorm(U, mean, sd); } 

Esta é uma função vetorizada que irá gerar N variáveis aleatórias IID a partir da distribuição normal truncada. Seria fácil programar funções para outras distribuições truncadas pelo mesmo método. Também não seria muito difícil programar funções associadas de densidade e quantil para a distribuição truncada.


$ ^ \ dagger $ Observe que o truncamento altera a média e a variância da distribuição, então $ \ mu $ e $ \ sigma ^ 2 $ são não a média e a variância da distribuição truncada.

Resposta

Três maneiras funcionaram para mim:

  1. usando sample () com rnorm ():

    sample(x=min:max, replace= TRUE, rnorm(n, mean))

  2. usando o pacote msm e a função rtnorm:

    rtnorm(n, mean, lower=min, upper=max)

  3. usando o rnorm () e especificando os limites inferior e superior, como Hugh postou acima:

    sample <- rnorm(n, mean=mean); sample <- sample[x > min & x < max]

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *