En R, tengo un matriz donde el la fila de corresponde a una distribución en . Esencialmente, necesito tomar muestras de cada fila de manera eficiente. Una implementación ingenua es:
X = rep(0, N);
for(i in 1:N){
X[i] = sample(1:K, 1, prob = P[i, ]);
}
Esto es demasiado lento. En principio, podría mover esto a C, pero estoy seguro de que debe haber una forma existente de hacerlo. Me gustaría algo en el espíritu del siguiente código (que no funciona):
X = sample(1:K, N, replace = TRUE, prob = P)
EDITAR: para motivar, tomar y . yo tengo todas las matrices y necesito muestrear un vector de cada uno de ellos.
Respuestas:
Podemos hacer esto de dos maneras simples . El primero es fácil de codificar, fácil de entender y razonablemente rápido. El segundo es un poco más complicado, pero mucho más eficiente para este tamaño de problema que el primer método u otros enfoques mencionados aquí.
Método 1 : rápido y sucio.
Para obtener una sola observación de la distribución de probabilidad de cada fila, simplemente podemos hacer lo siguiente.
Esto produce la distribución acumulativa de cada fila deP y luego muestrea una observación de cada distribución. Tenga en cuenta que si podemos reutilizar P entonces podemos calcular Q una vez y guárdelo para su uso posterior. Sin embargo, la pregunta necesita algo que funcione para otroP en cada iteración
Si necesitas múltiples (n ) observaciones de cada fila, luego reemplace la última línea con la siguiente.
En general, esta no es una forma extremadamente eficiente de hacerlo, pero sí aprovecha las
R
capacidades de vectorización, que generalmente es el principal determinante de la velocidad de ejecución. También es sencillo de entender.Método 2 : Concatenar los cdfs.
Supongamos que tenemos una función que toma dos vectores, el segundo de los cuales se clasificó en orden monotónicamente no decreciente y encontró el índice en el segundo vector del límite inferior más grande de cada elemento en el primero. Entonces, podríamos usar esta función y un truco ingenioso: simplemente cree la suma acumulativa de los cdf de todas las filas. Esto da un vector monotónicamente creciente con elementos en el rango[0,N] .
Aquí está el código.
Observe lo que hace la última línea, crea variables aleatorias distribuidas en(0,1),(1,2),…,(N−1,N) y luego llama K , el segundo se encontrará entre el índice K+1 y 2K , etc., cada uno según la distribución de la fila correspondiente de P . Luego, necesitamos volver a transformar para volver a colocar cada uno de los índices en el rango{1,…,K} .
findInterval
para encontrar el índice del límite inferior más grande de cada entrada. Entonces, esto nos dice que el primer elemento derunif(N)+i
se encontrará entre el índice 1 y el índiceDebido a que
findInterval
es rápido tanto desde el punto de vista algorítmico como de implementación, este método resulta ser extremadamente eficiente.Un punto de referencia
En mi vieja computadora portátil (MacBook Pro, 2.66 GHz, 8GB RAM), probé esto conN=10000 y K=100 y generando 5000 muestras de tamaño N , exactamente como se sugiere en la pregunta actualizada, para un total de 50 millones de variantes aleatorias.
El código para el Método 1 tardó casi exactamente 15 minutos en ejecutarse, o alrededor de 55,000 variantes aleatorias por segundo. El código para el Método 2 tardó aproximadamente cuatro minutos y medio en ejecutarse, o alrededor de 183 mil variantes aleatorias por segundo.
Aquí está el código por el bien de la reproducibilidad. (Tenga en cuenta que, como se indica en un comentario,Q se recalcula para cada una de las 5000 iteraciones para simular la situación del OP).
Aquí está la salida.
Postdata : Al observar el código
findInterval
, podemos ver que realiza algunas verificaciones en la entrada para ver si hayNA
entradas o si el segundo argumento no está ordenado. Por lo tanto, si quisiéramos exprimir más el rendimiento de esto, podríamos crear nuestra propia versión modificadafindInterval
que elimine estas comprobaciones que son innecesarias en nuestro caso.fuente
Un
for
bucle puede ser terriblemente lentoR
. ¿Qué tal esta simple vectorización consapply
?Por supuesto, este uniforme p es solo para pruebas.
fuente