正規分布の混合からランダム変数を生成する


20

混合分布、特にの正規分布の混合からサンプリングするにはどうすればよいRですか?たとえば、次のものからサンプリングしたい場合:

0.3×N01+0.5×N101+0.2×N3.1

どうすればそれができますか?


3
私は、混合物を表すこの方法が本当に好きではありません。表記法では、サンプリングするには、3つの法線すべてをサンプリングし、明らかに正しくない係数で結果を比較する必要があることが示唆されています。誰もがより良い表記法を知っていますか?
StijnDeVuyst

そんな印象は一度もありませんでした。分布(この場合は3つの正規分布)を関数と考え、結果は別の関数になります。
ラウンドスクエア

あなたはこの質問を訪問したいと思うかもしれません@StijnDeVuystあなたのコメント由来:stats.stackexchange.com/questions/431171/...
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]。読みやすいと思います:)
エルビス

非常にエレガント(cc @Elvis)!
イタマル

18

一般に、混合分布からサンプリングする最も簡単な方法の1つは次のとおりです。

アルゴリズムの手順

1)ランダム変数生成しますUUniform(0,1

2)間隔の場合、の確率に対応する混合モデルのコンポーネントは、その後のthedistributionから生成成分 P K K T H k個のT Hうん[=1kpk=1k+1pk+1pkkthkth

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

概念的には、ある確率で(可能性から)1つの分布を選択し、その分布から擬似乱数変量を生成するだけです。で、これは(例)になります: 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 -復活モニカ

6
RfindInterval()cumsum()μ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
これらは良い答えだと言った。私の目的はあなたを批判することではなく、コードではなく単一の引数を変更するだけで、3次元以上に簡単に一般化するアプローチを提供することでした。なぜあなたが書いたものが私が書いたものよりも透明であるのかは私には明らかではありませんが、私は確かにそれについて議論したくありません。乾杯。
マクロ

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.