Si lo entiendo correctamente, desea muestrear los valores de la distribución multinomial con probabilidades modo que , sin embargo, desea que la distribución se trunca de modo que para todo .p 1 , ... , p k ∑ i x i = n a i ≤ x i ≤ b i x ix1,…,xkp1,…,pk∑ixi=nai≤xi≤bixi
Veo tres soluciones (ni tan elegantes como en el caso no truncado):
- Aceptar rechazar. Muestra de multinomio no truncado, acepte la muestra si se ajusta a los límites de truncamiento; de lo contrario, rechace y repita el proceso. Es rápido, pero puede ser muy ineficiente.
rtrmnomReject <- function(R, n, p, a, b) {
x <- t(rmultinom(R, n, p))
x[apply(a <= x & x <= b, 1, all) & rowSums(x) == n, ]
}
- Simulación directa Muestra de manera que se asemeja al proceso de generación de datos, es decir, muestree canicas individuales de una urna aleatoria y repita este proceso hasta que muestree canicas en total, pero a medida que despliega el número total de canicas de una urna dada ( ya es igual a ) luego deje de dibujar de dicha urna. Implementé esto en un script a continuación.x i b inxibi
# single draw from truncated multinomial with a,b truncation points
rtrmnomDirect <- function(n, p, a, b) {
k <- length(p)
repeat {
pp <- p # reset pp
x <- numeric(k) # reset x
repeat {
if (sum(x<b) == 1) { # if only a single category is left
x[x<b] <- x[x<b] + n-sum(x) # fill this category with reminder
break
}
i <- sample.int(k, 1, prob = pp) # sample x[i]
x[i] <- x[i] + 1
if (x[i] == b[i]) pp[i] <- 0 # if x[i] is filled do
# not sample from it
if (sum(x) == n) break # if we picked n, stop
}
if (all(x >= a)) break # if all x>=a sample is valid
# otherwise reject
}
return(x)
}
- Algoritmo de metrópolis. Finalmente, el tercer enfoque más eficiente sería utilizar el algoritmo Metropolis . El algoritmo se inicializa mediante la simulación directa (pero se puede inicializar de manera diferente) para extraer la primera muestra . En los siguientes pasos de forma iterativa: el valor de propuesta
se acepta como con probabilidad , de lo contrario se toma el valor es el lugar, donde. Como propuesta, utilicé la función que toma el valor y cambia aleatoriamente de 0 al número de casos y lo mueve a otra categoría.X1y=q(Xi−1)Xif(y)/f(Xi−1)Xi−1f(x)∝∏ipxii/xi!qXi−1
step
# draw R values
# 'step' parameter defines magnitude of jumps
# for Meteropolis algorithm
# 'init' is a vector of values to start with
rtrmnomMetrop <- function(R, n, p, a, b,
step = 1,
init = rtrmnomDirect(n, p, a, b)) {
k <- length(p)
if (length(a)==1) a <- rep(a, k)
if (length(b)==1) b <- rep(b, k)
# approximate target log-density
lp <- log(p)
lf <- function(x) {
if(any(x < a) || any(x > b) || sum(x) != n)
return(-Inf)
sum(lp*x - lfactorial(x))
}
step <- max(2, step+1)
# proposal function
q <- function(x) {
idx <- sample.int(k, 2)
u <- sample.int(step, 1)-1
x[idx] <- x[idx] + c(-u, u)
x
}
tmp <- init
x <- matrix(nrow = R, ncol = k)
ar <- 0
for (i in 1:R) {
proposal <- q(tmp)
prob <- exp(lf(proposal) - lf(tmp))
if (runif(1) < prob) {
tmp <- proposal
ar <- ar + 1
}
x[i,] <- tmp
}
structure(x, acceptance.rate = ar/R, step = step-1)
}
El algoritmo comienza en y luego deambula por las diferentes regiones de distribución. Obviamente, es más rápido que los anteriores, pero debe recordar que si lo usa para muestrear un pequeño número de casos, podría terminar con sorteos que están cerca uno del otro. Otro problema es que necesita decidir sobre el tamaño, es decir, qué tan grandes debe hacer el algoritmo: demasiado pequeño puede llevar a moverse lentamente, demasiado grande puede llevar a hacer demasiadas propuestas inválidas y rechazarlas. Puede ver un ejemplo de su uso a continuación. En los gráficos se pueden ver: densidades marginales en la primera fila, traceplots en la segunda fila y gráficos que muestran saltos posteriores para pares de variables.X1step
n <- 500
a <- 50
b <- 125
p <- c(1,5,2,4,3)/15
k <- length(p)
x <- rtrmnomMetrop(1e4, n, p, a, b, step = 15)
cmb <- combn(1:k, 2)
par.def <- par(mfrow=c(4,5), mar = c(2,2,2,2))
for (i in 1:k)
hist(x[,i], main = paste0("X",i))
for (i in 1:k)
plot(x[,i], main = paste0("X",i), type = "l", col = "lightblue")
for (i in 1:ncol(cmb))
plot(jitter(x[,cmb[1,i]]), jitter(x[,cmb[2,i]]),
type = "l", main = paste(paste0("X", cmb[,i]), collapse = ":"),
col = "gray")
par(par.def)
El problema con el muestreo de esta distribución es que describe una estrategia de muestreo muy ineficiente en general. Imagine que y , y 's están cerca de ' s, en tal caso desea muestrear categorías con diferentes probabilidades, pero espera que sean similares frecuencias al final. En casos extremos, imagine una distribución de dos categorías donde , y ,a 1 = ⋯ = a k b 1 = … b k a i b i p 1 ≫ p 2 a 1 ≪ a 2 b 1 ≪ b 2p1≠⋯≠pka1=⋯=akb1=…bkaibip1≫p2a1≪a2b1≪b2, en tal caso, espera que suceda algo muy raro (un ejemplo real de dicha distribución sería un investigador que repita el muestreo hasta que encuentre la muestra que sea consistente con su hipótesis, por lo que tiene más que ver con el engaño que con el muestreo aleatorio) .
La distribución es mucho menos problemática si la define como Rukhin (2007, 2008) donde muestrea casos para cada categoría, es decir, muestra proporcionalmente a 's.p inpipi
Rukhin, AL (2007). Estadísticas de orden normal y sumas de variables geométricas aleatorias en problemas de asignación de tratamientos. Estadísticas y cartas de probabilidad, 77 (12), 1312-1321.
Rukhin, AL (2008). Reglas de detención en problemas de asignación equilibrada: distribuciones exactas y asintóticas. Análisis secuencial, 27 (3), 277-292.
x[i] >= a
. Imagine que arrojó una moneda sesgada con una probabilidad de cara = 0.9. Lanzas la moneda hasta que obtengas al menos 10 caras y 10 colas. En el punto de parada, tendrías en promedio muchas más caras que colas. Comenzar enx[1] = ... = x[k] = a
significa que ignora el hecho de que los puntos de partida de cada uno de ellosx[i]
son diferentes debido a los diferentesp[i]
.Aquí está mi esfuerzo al tratar de traducir el código R de Tim a Python. Como pasé un tiempo entendiendo este problema y codifiqué los algoritmos en Python, pensé compartirlos aquí en caso de que la gente estuviera interesada.
Para una implementación completa de este código, consulte mi repositorio de Github en
https://github.com/mohsenkarimzadeh/sampling
fuente