Генерування випадкових змінних із суміші нормальних розподілів


20

Як я можу взяти вибірку з розподілу суміші, зокрема суміші звичайних розподілів R? Наприклад, якщо я хотів зробити вибірку з:

0.3×N(0,1)+0.5×N(10,1)+0.2×N(3,.1)

як я міг це зробити?


3
Мені дуже не подобається такий спосіб позначення суміші. Я знаю, що це звичайно робиться так, але я вважаю, що це вводить в оману. Повідомлення говорить про те, що для вибірки потрібно відібрати всі три нормалі та зважити результати за тими коефіцієнтами, які, очевидно, не були б правильними. Хтось знає краще позначення?
StijnDeVuyst

Я ніколи не склав такого враження. Я думаю, що розподіли (в даному випадку три звичайні розподіли) як функції, а результат - інша функція.
круглий квадрат

@StijnDeVuyst, можливо, ви захочете відвідати це запитання, виникне
ankii

@ankii: дякую за вказівку на це!
StijnDeVuyst

Відповіді:


32

Доцільно уникати forциклів Rз міркувань продуктивності. Альтернативне рішення, яке використовує факт, rnormє векторизованим:

N <- 100000

components <- sample(1:3,prob=c(0.3,0.5,0.2),size=N,replace=TRUE)
mus <- c(0,10,3)
sds <- sqrt(c(1,1,0.1))

samples <- rnorm(n=N,mean=mus[components],sd=sds[components])

3
Крім того, ви можете використовувати властивості звичайного розподілу для заміни останнього рядка на samples <- rnorm(N)*sds[components]+mus[components]. Мені легше читати :)
Елвіс,

Дуже елегантний (куб.см @ Елвіс)!
Ітамар

18

Загалом, одним із найпростіших способів вибірки з розподілу суміші є наступний:

Кроки алгоритму

1) Створити випадкову змінну UУніформа(0,1)

2) Якщо інтервал, де p k відповідає ймовірності k t h компонента моделі суміші, то генеруємо з розподіл k t h компонентаU[i=1кpк,i=1к+1pк+1)pкктгодктгод

3) Повторюйте кроки 1) та 2), поки у вас не з’явиться потрібна кількість зразків з розподілу суміші

Тепер, використовуючи загальний алгоритм, наведений вище, ви можете взяти вибірку із прикладу суміші нормалів, використовуючи наступний Rкод:

#The number of samples from the mixture distribution
N = 100000                 

#Sample N random uniforms U
U =runif(N)

#Variable to store the samples from the mixture distribution                                             
rand.samples = rep(NA,N)

#Sampling from the mixture
for(i in 1:N){
    if(U[i]<.3){
        rand.samples[i] = rnorm(1,0,1)
    }else if(U[i]<.8){
        rand.samples[i] = rnorm(1,10,1)
    }else{
        rand.samples[i] = rnorm(1,3,.1)
    }
}

#Density plot of the random samples
plot(density(rand.samples),main="Density Estimate of the Mixture Model")

#Plotting the true density as a sanity check
x = seq(-20,20,.1)
truth = .3*dnorm(x,0,1) + .5*dnorm(x,10,1) + .2*dnorm(x,3,.1)
plot(density(rand.samples),main="Density Estimate of the Mixture Model",ylim=c(0,.2),lwd=2)
lines(x,truth,col="red",lwd=2)

legend("topleft",c("True Density","Estimated Density"),col=c("red","black"),lwd=2)

Що породжує:

введіть тут опис зображення

і як перевірка здорового стану:

введіть тут опис зображення


Привіт! Дуже дякую! Ця відповідь мені дуже допомогла. Я використовую це в дослідницькому проекті. Я хочу навести посилання на вищезазначене. Чи можете ви запропонувати цитати дослідницької статті.
Абхішек Бхатія

7

Концептуально ви вибираєте лише один розподіл (від кможливості) з деякою ймовірністю, а потім генерувати псевдовипадкові змінні з цього розподілу. В R, це буде (наприклад):

set.seed(8)               # this makes the example reproducible
N     = 1000              # this is how many data you want
probs = c(.3,.8)          # these are *cumulative* probabilities; since they 
                          #   necessarily sum to 1, the last would be redundant
dists = runif(N)          # here I'm generating random variates from a uniform
                          #   to select the relevant distribution

# this is where the actual data are generated, it's just some if->then
#   statements, followed by the normal distributions you were interested in
data = vector(length=N)
for(i in 1:N){
  if(dists[i]<probs[1]){
    data[i] = rnorm(1, mean=0, sd=1)
  } else if(dists[i]<probs[2]){
    data[i] = rnorm(1, mean=10, sd=1)
  } else {
    data[i] = rnorm(1, mean=3, sd=.1)
  }
}

# here are a couple of ways of looking at the results
summary(data)
#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# -3.2820  0.8443  3.1910  5.5350 10.0700 13.1600 

plot(density(data))

введіть тут опис зображення


Приємна відповідь, ви побили мене до публікації: P

1
Дякую за пораду, @BabakP. Я не впевнений, що це було. Це було щось у ifelse()заяві, але мені доведеться це з'ясувати пізніше. Я замінив цей код без циклу.
gung - Відновіть Моніку

6
(cc @BabakP) Це обидва хороші відповіді і очевидно правильні (+ 1s). Просто Rфокус програмування: ви також можете використовувати findInterval()іcumsum() команди команди для спрощення коду і, що ще важливіше, полегшити узагальнення до різної кількості вимірів. Наприклад, для вхідного вектора засобівмк( mu) та дисперсіїσ2( s) та ймовірності суміші ( p), простою функцією для генерування n зразків з цієї суміші будеmix <- function(n,mu,s,p) { ii <- findInterval(runif(n),cumsum(p))+1; x <- rnorm(n,mean=mu[ii],sd=sqrt(s[ii])); return(x); }
Макрос

1
@Macro, дуже правдивий і дуже приємний код! Я раніше не бачив findInterval()команди, однак мені подобається писати код настільки спрощено, наскільки я можу, бо хочу, щоб це був інструмент для розуміння, а не ефективності.

1
Я сказав, що це хороші відповіді. Моя мета полягала не в тому, щоб критикувати вас, а запропонувати підхід, який легко узагальнює більш ніж три виміри, змінюючи лише один аргумент, а не будь-який код. Мені не зрозуміло, чому те, що ви написали, є більш прозорим, ніж те, що я написав, але я, безумовно, не хочу сперечатися з цим. Ура.
Макрос

0

Вже дано ідеальні відповіді, тому для тих, хто хоче досягти цього в Python, ось моє рішення:

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

mu = [0, 10, 3]
sigma = [1, 1, 1]
p_i = [0.3, 0.5, 0.2]
n = 10000

x = []
for i in range(n):
    z_i = np.argmax(np.random.multinomial(1, p_i))
    x_i = np.random.normal(mu[z_i], sigma[z_i])
    x.append(x_i)

def univariate_normal(x, mean, variance):
    """pdf of the univariate normal distribution."""
    return ((1. / np.sqrt(2 * np.pi * variance)) * 
            np.exp(-(x - mean)**2 / (2 * variance)))

a = np.arange(-7, 18, 0.01)
y = p_i[0] * univariate_normal(a, mean=mu[0], variance=sigma[0]**2) + p_i[1] * univariate_normal(a, mean=mu[1], variance=sigma[0]**2)+ p_i[2] * univariate_normal(a, mean=mu[2], variance=sigma[0]**2)

fig, ax = plt.subplots(figsize=(8, 4))

ax.hist(x, bins=100, density=True)
ax.plot(a, y)

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.