Dans R, j'ai une matrice où la ème ligne de correspond à une distribution sur . Essentiellement, je dois échantillonner efficacement chaque ligne. Une implémentation naïve est:
X = rep(0, N);
for(i in 1:N){
X[i] = sample(1:K, 1, prob = P[i, ]);
}
C'est beaucoup trop lent. En principe, je pourrais déplacer ceci en C mais je suis sûr qu'il doit y avoir une manière existante de le faire. Je voudrais quelque chose dans l'esprit du code suivant (qui ne fonctionne pas):
X = sample(1:K, N, replace = TRUE, prob = P)
EDIT: Pour la motivation, prenez et . J'ai matrices toutes les et je dois échantillonner un vecteur de chacune d'elles.
Réponses:
Nous pouvons le faire de deux manières simples . Le premier est facile à coder, facile à comprendre et raisonnablement rapide. La seconde est un peu plus délicate, mais beaucoup plus efficace pour cette taille de problème que la première méthode ou les autres approches mentionnées ici.
Méthode 1 : rapide et sale.
Pour obtenir une seule observation de la distribution de probabilité de chaque ligne, nous pouvons simplement faire ce qui suit.
Cela produit la distribution cumulative de chaque ligne de , puis échantillonne une observation de chaque distribution. Notez que si nous pouvons réutiliser nous pouvons calculer une fois et le stocker pour une utilisation ultérieure. Cependant, la question a besoin de quelque chose qui fonctionne pour un différent à chaque itération.P P Q P
Si vous avez besoin de plusieurs ( ) observations pour chaque ligne, remplacez la dernière ligne par la suivante.n
Ceci est vraiment pas une façon extrêmement efficace en général de le faire, mais il ne bien tirer profit des
R
capacités de vectorisation, ce qui est généralement le principal déterminant de la vitesse d'exécution. Il est également simple à comprendre.Méthode 2 : concaténation des cdfs.
Supposons que nous ayons une fonction qui prend deux vecteurs, dont le second est trié dans l'ordre monotone non décroissant et trouve l'indice dans le deuxième vecteur de la plus grande borne inférieure de chaque élément dans le premier. Ensuite, nous pourrions utiliser cette fonction et une astuce: il suffit de créer la somme cumulée des cdfs de toutes les lignes. Cela donne un vecteur croissant de façon monotone avec des éléments dans la plage .[0,N]
Voici le code.
Remarquez ce que fait la dernière ligne, elle crée des variables aléatoires réparties dans puis appelle pour trouver l'index de la plus grande borne inférieure de chaque entrée . Donc, ceci nous indique que le premier élément de se trouver entre l' indice 1 et l' indice , le second se trouve entre l' indice et , etc, chacun en fonction de la répartition de la rangée correspondante de . Ensuite, nous devons sauvegarder la transformation pour récupérer chacun des indices dans la plage .(0,1),(1,2),…,(N−1,N) K K+1 2K P {1,…,K}
findInterval
runif(N)+i
Parce qu'elle
findInterval
est rapide sur le plan algorithmique et sur le plan de l'implémentation, cette méthode s'avère extrêmement efficace.Une référence
Sur mon ancien ordinateur portable (MacBook Pro, 2,66 GHz, 8 Go de RAM), j'ai essayé cela avec et et générer 5000 échantillons de taille , exactement comme suggéré dans la question mise à jour, pour un total de 50 millions de variantes aléatoires .N=10000 K=100 N
Le code de la méthode 1 a pris presque exactement 15 minutes à exécuter, soit environ 55 000 variables aléatoires par seconde. Le code de la méthode 2 a pris environ quatre minutes et demie à exécuter, soit environ 183 Ko de variations aléatoires par seconde.
Voici le code pour des raisons de reproductibilité. (Notez que, comme indiqué dans un commentaire, est recalculé pour chacune des 5000 itérations pour simuler la situation du PO.)Q
Voici la sortie.
Postscript : En regardant le code de
findInterval
, nous pouvons voir qu'il effectue quelques vérifications sur l'entrée pour voir s'il y a desNA
entrées ou si le deuxième argument n'est pas trié. Par conséquent, si nous voulions en extraire plus de performances, nous pourrions créer notre propre version modifiéefindInterval
qui supprime ces contrôles inutiles dans notre cas.la source
Une
for
boucle peut être terriblement lenteR
. Que diriez-vous de cette simple vectorisation avecsapply
?Bien sûr, cet uniforme p est juste pour les tests.
la source