Necesito generar números aleatorios siguiendo una distribución normal dentro del intervalo $ (a, b) $. (Estoy trabajando en R.)

Sé que la función rnorm(n,mean,sd) generará números aleatorios siguiendo la distribución normal, pero ¿cómo establecer los límites de intervalo dentro de eso? ¿Hay alguna función de R en particular disponible para eso?

Comentarios

  • ¿Por qué quieres hacer esto? Si ‘ está limitado, entonces ‘ no puede ser realmente normal. ¿Qué estás tratando de lograr?
  • x <- rnorm(n, mean, sd); x <- x[x > lower.limit & x < upper.limit]
  • @Hugh, eso ‘ es genial … siempre y cuando no ‘ te importe cuántos valores aleatorios obtengas.

Responder

Parece que desea simular desde una distribución truncada , y en su ejemplo específico , un normal truncado .

Hay una variedad de métodos para hacerlo, algunos simples, otros relativamente eficiente.

Ilustraré algunos enfoques en su ejemplo normal.

  1. Aquí hay un método muy simple para generar uno a la vez (en algún tipo de pseudocódigo ):

    $ \ tt {repeat} $ genera $ x_i $ desde N (mean, sd) $ \ tt {hasta} $ inferior $ \ leq x_i \ leq $ superior

    ingrese la descripción de la imagen aquí

    Si la mayor parte de la distribución está dentro de los límites, esto es bastante razonable, pero puede volverse bastante lento si casi siempre genera fuera de los límites.

    En R, podría evitar el ciclo de uno por uno calculando el área dentro de los límites y generar suficientes valores de los que podría estar casi seguro de que después de descartar los valores fuera de los límites todavía tenía tantos valores como fueran necesarios.

  2. Puede usar aceptar-rechazar con alguna función de mayorización adecuada durante el intervalo (en algunos casos, el uniforme será ser lo suficientemente bueno). Si los límites fueran razonablemente estrechos en relación con el s.d. pero no estabas muy en la cola, una mayorización uniforme funcionaría bien con lo normal, por ejemplo.

    ingresa la descripción de la imagen aquí

  3. Si tiene un CDF razonablemente eficiente y un CDF inverso (como pnorm y qnorm para distribución normal en R) puede utilizar el método de CDF inverso descrito en el primer párrafo de la sección de simulación de la página de Wikipedia en la normal truncada . [En efecto esto es lo mismo que tomar un uniforme truncado (truncado en los cuantiles requeridos, que en realidad no requiere ningún rechazo, ya que ese es solo otro uniforme) y aplicar el CDF normal inverso a eso. Tenga en cuenta que esto puede fallar si «está muy en la cola]

    ingrese la descripción de la imagen aquí

  4. Hay otros enfoques; la misma página de Wikipedia menciona la adaptación del método ziggurat , que debería funcionar para una variedad de distribuciones.

El mismo enlace de Wikipedia menciona dos paquetes específicos (ambos en CRAN) con funciones para generar normales truncadas:

El paquete MSM en R tiene una función, rtnorm, que calcula las extracciones de un truncado normal. El paquete truncnorm en R también tiene funciones para extraer de una normal truncada.


Mirando a su alrededor, mucho de esto se cubre en las respuestas a otras preguntas (pero no exactamente duplicadas, ya que esta pregunta es más general que la normal truncada) … vea la discusión adicional en

a. Esta respuesta

b. Xi «an» s answer aquí , que tiene un enlace a su artículo arXiv (junto con algunas otras respuestas útiles).

Respuesta

El enfoque rápido y sucio es usar la 68-95-99.7 regla .

En una distribución normal, el 99,7% de los valores se encuentran dentro de 3 desviaciones estándar de la media. Por lo tanto, si establece su media en la mitad de su valor mínimo y valor máximo deseados, y establece su desviación estándar en 1/3 de su media, obtiene (en su mayoría) valores que caen dentro del intervalo deseado. Luego puede limpiar el 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) 

Recientemente enfrenté este mismo problema, tratando de generar calificaciones de estudiantes al azar para los datos de las pruebas En el código anterior, «he usado pmax y pmin para reemplazar los valores fuera de los límites con los límites mínimos o máximos valor.Esto funciona para mi propósito, porque estoy generando cantidades bastante pequeñas de datos, pero para cantidades más grandes le dará golpes notables en los valores mínimo y máximo. Por lo tanto, dependiendo de sus propósitos, puede ser mejor descartar esos valores, reemplazarlos con NA s, o «repítelos» hasta que «estén dentro de los límites.

Comentarios

  • ¿Por qué molestarse en hacer esto? Es tan simple generar números aleatorios normales y eliminar aquellos que necesitan truncamiento que no es ‘ t necesario complicarse al respecto a menos que el truncamiento deseado sea cercano al 100% del área de la densidad.
  • Quizás ‘ estoy malinterpretando la pregunta original. Me encontré con esta pregunta mientras intentaba averiguar cómo lograr una tarea de programación no relacionada directamente con las estadísticas en R, y ‘ recién ahora noté que esta página es una pila de estadísticas , no un intercambio de pila de programación. 🙂 En mi caso, quería generar una cantidad específica de números enteros aleatorios, con valores que van de 0 a 100, y quería que los valores generados cayeran en una bonita curva de campana en ese rango. Desde que escribí esto, ‘ me he dado cuenta de que sample(x=min:max, prob=dnorm(...)) es quizás una forma más fácil de hacerlo.
  • @Glen_b Aaron Wells menciona sample(x=min:max, prob=dnorm(...)) que parece un poco más corto que su respuesta.
  • Pero tenga en cuenta que el truco sample() solo es útil si ‘ está intentando elegir números enteros aleatorios, o algún otro conjunto de valores predefinidos discretos.

Respuesta

Ninguna de las respuestas proporciona un método eficaz para generar variables normales truncadas que no implique el rechazo de variables arbitrariamente grandes números de valores generados. Si desea generar valores a partir de una distribución normal truncada, con límites inferiores y superiores especificados $ a < b $ , esto se puede hacer — sin rechazo — generando cuantiles uniformes sobre el rango de cuantiles permitido por el truncamiento y usando muestreo de transformación inversa para obtener los valores normales correspondientes .

Sea $ \ Phi $ el CDF de la distribución normal estándar. Queremos generar $ X_1, …, X_N $ a partir de una distribución normal truncada (con el parámetro medio $ \ mu $ y parámetro de varianza $ \ sigma ^ 2 $ ) $ ^ \ dagger $ con menor y límites superiores de truncamiento $ a < b $ . Esto se puede hacer de la siguiente manera:

$$ 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]. $$

No hay una función incorporada para los valores generados a partir de la distribución truncada, pero es trivial programar este método usando el funciones ordinarias para generar variables aleatorias. Aquí hay una R función rtruncnorm simple que implementa este método en unas pocas líneas 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 es una función vectorizada que generará N variables aleatorias IID de la distribución normal truncada. Sería fácil programar funciones para otras distribuciones truncadas mediante el mismo método. Tampoco sería demasiado difícil programar las funciones de densidad y cuantiles asociadas para la distribución truncada.


$ ^ \ dagger $ Tenga en cuenta que el truncamiento altera la media y la varianza de la distribución, por lo que $ \ mu $ y $ \ sigma ^ 2 $ son no la media y la varianza de la distribución truncada.

Respuesta

Tres formas me han funcionado:

  1. usando sample () con rnorm ():

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

  2. usando el paquete msm y la función rtnorm:

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

  3. usando rnorm () y especificando los límites inferior y superior, como Hugh ha publicado anteriormente:

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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *