Generazione di variabili casuali da una miscela di distribuzioni normali


20

Come posso campionare da una distribuzione della miscela, e in particolare una miscela di distribuzioni normali in R? Ad esempio, se volessi provare da:

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

come potrei farlo?


3
Non mi piace davvero questo modo di indicare una miscela. So che è fatto in modo convenzionale in questo modo, ma lo trovo fuorviante. La notazione suggerisce che per campionare, è necessario campionare tutte e tre le normali e pesare i risultati con quei coefficienti che ovviamente non sarebbero corretti. Qualcuno sa una notazione migliore?
StijnDeVuyst,

Non ho mai avuto questa impressione. Penso alle distribuzioni (in questo caso le tre distribuzioni normali) come funzioni e quindi il risultato è un'altra funzione.
roundsquare

@StijnDeVuyst potresti voler visitare questa domanda originata dal tuo commento: stats.stackexchange.com/questions/431171/…
ankii

@ankii: grazie per averlo sottolineato!
StijnDeVuyst,

Risposte:


32

È buona prassi evitare forloop in Rper motivi di prestazioni. Una soluzione alternativa che sfrutta il fatto rnormè vettorializzata:

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
In alternativa, è possibile utilizzare le proprietà della distribuzione normale per sostituire l'ultima riga con samples <- rnorm(N)*sds[components]+mus[components]. Trovo più facile da leggere :)
Elvis

Molto elegante (cc @Elvis)!
Itamar,

18

In generale, uno dei modi più semplici per campionare da una distribuzione della miscela è il seguente:

Passaggi dell'algoritmo

1) Genera una variabile casuale U~Uniforme(0,1)

2) Se intervallo, dove p k corrisponde alla probabilità del componente k t h del modello di miscela, quindi genera da la distribuzione del componente k t hU[Σio=1KpK,Σio=1K+1pK+1)pKKthKth

3) Ripetere i passaggi 1) e 2) fino ad ottenere la quantità desiderata di campioni dalla distribuzione della miscela

Ora usando l'algoritmo generale indicato sopra, è possibile campionare dalla combinazione di normali di esempio usando il Rcodice seguente :

#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)

Che genera:

inserisci qui la descrizione dell'immagine

e come controllo di sanità mentale:

inserisci qui la descrizione dell'immagine


Ciao! Grazie mille! Questa risposta mi ha aiutato molto. Sto usando questo in un progetto di ricerca. Vorrei citare un riferimento per quanto sopra. Potete per favore suggerire una citazione di un articolo di ricerca.
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))

inserisci qui la descrizione dell'immagine


Bella risposta, mi hai battuto per la pubblicazione: P

1
Grazie per la punta, @BabakP. Non sono sicuro di cosa fosse. Era qualcosa ifelse()nell'affermazione, ma dovrò scoprirlo più tardi. Ho sostituito quel codice con un ciclo.
gung - Ripristina Monica

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, codice molto vero e molto bello! Non ho mai visto il findInterval()comando prima, tuttavia, mi piace scrivere qui il codice nel modo più semplice possibile perché voglio che sia uno strumento per comprendere piuttosto che per l'efficienza.

1
Ho detto che queste erano buone risposte. Il mio scopo non era di criticare te, ma di offrire un approccio che si generalizza facilmente a più di tre dimensioni modificando solo un singolo argomento, non alcun codice. Non mi è chiaro perché ciò che hai scritto sia più trasparente di quello che ho scritto, ma sicuramente non voglio discuterne. Saluti.
Macro

0

Ho già dato risposte perfette, quindi per coloro che vogliono ottenere questo risultato in Python, ecco la mia soluzione:

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)

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.