การสร้างตัวแปรสุ่มจากส่วนผสมของการแจกแจงแบบปกติ


20

ฉันจะได้ลิ้มลองจากการกระจายส่วนผสมและในส่วนผสมโดยเฉพาะอย่างยิ่งของการกระจายปกติในR? ตัวอย่างเช่นถ้าฉันต้องการตัวอย่างจาก:

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

ฉันจะทำอย่างนั้นได้อย่างไร


3
ฉันไม่ชอบวิธีนี้ในการบอกส่วนผสม ฉันรู้ว่ามันทำตามอัตภาพเช่นนี้ แต่ฉันพบว่ามันทำให้เข้าใจผิดสัญลักษณ์แสดงให้เห็นว่าในการสุ่มตัวอย่างคุณต้องสุ่มตัวอย่างทั้งสามบรรทัดฐานและชั่งน้ำหนักผลลัพธ์โดยสัมประสิทธิ์เหล่านั้นซึ่งเห็นได้ชัดว่าไม่ถูกต้อง ใครรู้เครื่องหมายที่ดีกว่า
StijnDeVuyst

ฉันไม่เคยได้รับความประทับใจนั้น ฉันคิดว่าการแจกแจง (ในกรณีนี้คือการแจกแจงปกติสามค่า) เป็นฟังก์ชันแล้วผลลัพธ์ก็คือฟังก์ชันอื่น
roundsquare

@StijnDeVuyst คุณอาจต้องการเยี่ยมชมคำถามนี้มาจากความคิดเห็นของคุณ: stats.stackexchange.com/questions/431171/…
ankii

@ankii: ขอบคุณที่ชี้ให้เห็น!
StijnDeVuyst

คำตอบ:


32

เป็นวิธีปฏิบัติที่ดีในการหลีกเลี่ยงการforวนRซ้ำด้วยเหตุผลด้านประสิทธิภาพ โซลูชันทางเลือกซึ่งใช้ประโยชน์จากความจริงrnormคือ vectorized:

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]หรือคุณสามารถใช้คุณสมบัติของการแจกแจงแบบปกติที่จะเปลี่ยนบรรทัดสุดท้ายโดย ฉันคิดว่ามันง่ายต่อการอ่าน :)
เอลวิส

สง่างามมาก (cc @Elvis)!
Itamar

18

โดยทั่วไปหนึ่งในวิธีที่ง่ายที่สุดในการสุ่มตัวอย่างจากการกระจายแบบผสมมีดังต่อไปนี้:

ขั้นตอนวิธี

1) สร้างตัวแปรสุ่มUUniform(0,1)

2) ถ้าช่วงเวลาโดยที่สอดคล้องกับความน่าจะเป็นขององค์ประกอบของรูปแบบผสมแล้วสร้างจาก thedistribution ของส่วนประกอบ p k k t h k t hU[i=1kpk,i=1k+1pk+1)pkkthkth

3) ทำซ้ำขั้นตอนที่ 1) และ 2) จนกว่าคุณจะมีจำนวนตัวอย่างที่ต้องการจากการกระจายตัวของส่วนผสม

ตอนนี้ใช้อัลกอริทึมทั่วไปที่ให้ไว้ข้างต้นคุณสามารถสุ่มตัวอย่างจากตัวอย่างของคุณผสมของ normals โดยใช้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)

ซึ่งสร้าง:

ป้อนคำอธิบายรูปภาพที่นี่

และเป็นการตรวจสุขภาพจิต:

ป้อนคำอธิบายรูปภาพที่นี่


Hi! ขอบคุณมาก! คำตอบนี้ช่วยฉันอย่างมาก ฉันกำลังใช้สิ่งนี้ในโครงการวิจัย ฉันต้องการเสนอราคาอ้างอิงสำหรับข้างต้น คุณช่วยแนะนำการอ้างอิงบทความวิจัยได้ไหม
Abhishek Bhatia

7

ตามแนวคิดคุณเพียงแค่เลือกการแจกแจงหนึ่งครั้ง (จากความเป็นไปได้ ) ด้วยความน่าจะเป็นแล้วจึงสร้างตัวแปรสุ่มหลอกจากการแจกแจงนั้น ในนี้จะเป็น (เช่น): kR

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 - Reinstate Monica

6
(cc @BabakP) เหล่านี้เป็นคำตอบที่ดีและเห็นได้ชัดว่าถูกต้อง (+1) เพียงแค่Rเคล็ดลับการเขียนโปรแกรม: คุณยังสามารถใช้findInterval()และcumsum()คำสั่งเพื่อทำให้รหัสง่ายขึ้นและที่สำคัญทำให้ง่ายขึ้นในการทำให้ขนาดทั่วไปเป็นมิติที่แตกต่างกัน ตัวอย่างเช่นสำหรับเวกเตอร์การป้อนข้อมูลของค่าเฉลี่ย ( ) และความแปรปรวน ( ) และความน่าจะเป็นของการผสม ( ) ฟังก์ชันที่ง่ายในการสร้างตัวอย่าง n จากการผสมนี้จะเป็นσ 2μmuσ2spmix <- 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)

ป้อนคำอธิบายรูปภาพที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.