Як відібрати вибірку усіченого багаточленного розподілу?


9

Мені потрібен алгоритм для вибірки усіченого багаточленного розподілу. Це є,

x1Zp1x1pkxkx1!xk!

де - константа нормалізації, має позитивних компонентів, а . Я вважаю лише значення у діапазоні .Zxkxi=nxaxb

Як я можу взяти вибірку на цей усічений багаточленний розподіл?

Примітка. Дивіться у Вікіпедії алгоритм для вибірки неврізаного багаточленного розподілу. Чи є спосіб адаптувати цей алгоритм до усіченого розподілу?

Уніфікована версія: Простішою версією задачі є прийняття всіх рівних, . Якщо ви можете розробити алгоритм для вибірки усіченого розподілу в цьому випадку хоча б, опублікуйте його. Хоча це не загальна відповідь, це могло б допомогти мені вирішити інші практичні проблеми на даний момент.pipi=1/k

Відповіді:


9

Якщо я вас правильно зрозумів, ви бажаєте відібрати значення з багаточленного розподілу з ймовірностями таким чином, що , однак ви хочете, щоб розподіл був усічений таким чином для всіх .x1,,xkp1,,pkixi=naixibixi

Я бачу три рішення (не такі елегантні, як у невкороченому корпусі):

  1. Прийняти-відхилити. Зразок із нестриженого мультиноміалу приймайте зразок, якщо він відповідає меж усічення, інакше відхиліть і повторіть процес. Це швидко, але може бути дуже неефективно.
rtrmnomReject <- function(R, n, p, a, b) {
  x <- t(rmultinom(R, n, p))
  x[apply(a <= x & x <= b, 1, all) & rowSums(x) == n, ]
}
  1. Пряме моделювання. Зразок в моді, що нагадує процес генерації даних, тобто зразок одинарного мармуру з випадкової урни і повторюйте цей процес, поки ви не відібрали загальних мармурів, але, коли ви розгорнете загальну кількість мармурів з даної урни ( вже дорівнює ) тоді перестаньте малювати з такої урни. Я реалізував це в сценарії нижче.nxibi
# 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)
}
  1. Алгоритм Metropolis. Нарешті, третім та найефективнішим підходом було б використання алгоритму Metropolis . Алгоритм ініціалізується за допомогою прямого моделювання (але може бути ініціалізований по-різному), щоб намалювати перший зразок . На наступних етапах ітеративно: значення пропозиції приймається як з ймовірністю , інакше значення приймається у це місце, де. В якості пропозиції я використав функцію яка приймає значення і випадковим чином перевертає з 0 на кількість випадків і переміщує її в іншу категорію.X1y=q(Xi1)Xif(y)/f(Xi1)Xi1f(x)ipixi/xi!qXi1step
# 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)
}

Алгоритм починається з а потім бродить по різних регіонах розповсюдження. Це, очевидно, швидше, ніж попередні, але вам потрібно пам’ятати, що якби ви використовували його для вибірки невеликої кількості справ, то ви могли б закінчитися з малюнками, близькими один до одного. Інша проблема полягає в тому, що вам потрібно визначитися з розміром, тобто якими великими стрибками повинен робити алгоритм - занадто малі можуть призвести до повільного переміщення, занадто великі можуть призвести до створення занадто багато недійсних пропозицій та їх відхилення. Ви можете побачити приклад його використання нижче. На графіках ви можете бачити: граничні густини в першому ряду, трасплоти в другому ряду та графіки, що показують наступні стрибки для пар змінних.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)

введіть тут опис зображення

Проблема вибірки з цього розподілу полягає в тому, що описується дуже неефективна стратегія вибірки загалом. Уявіть, що і , та близькі до , у такому випадку ви хочете зробити вибірку до категорій з різними ймовірностями, але очікуйте подібних частоти в підсумку. У крайньому випадку, уявіть розподіл, де , а ,p1pka1==akb1=bkaibip1p2a1a2b1b2, у такому випадку ви очікуєте, що трапиться якась дуже рідкісна подія (в реальному прикладі такого розподілу буде дослідник, який повторює вибірку, поки не знайде зразок, що відповідає його гіпотезі, тому це має більше стосунку до обману, ніж до випадкового відбору) .

Розподіл набагато менш проблематичний, якщо ви визначаєте його як Рухін (2007, 2008), де ви випадків до кожної категорії, тобто вибірки пропорційно до .npipi


Рухін, А. Л. (2007). Статистика звичайного порядку та суми геометричних випадкових змінних в задачах розподілу лікування. Статистика та ймовірнісні листи, 77 (12), 1312-1321.

Рухін, А. Л. (2008). Правила зупинки в задачах збалансованого розподілу: точні та асимптотичні розподіли. Послідовний аналіз, 27 (3), 277-292.


Відхилення невірних зразків може бути занадто повільним. Можливо, простіше зробити переклад, , . Таким чином, у вас є лише верхня межа, турбуватися. Тоді ви можете видалити рядок, де ви відхиляєте зразок, якщо порушено (можна уявити значення де це відхилення було б дуже неефективним)yi=xiaim=niaiyibiaixaa
becko

@becko, якщо порівнювати такий підхід із описаним мною, ви побачите, що вони дають різні рішення.
Тім

Я не розумію, як вони можуть бути різними? Все, що я робив - це зміна змінних.
бекко

@becko Ваша відправна точка - це все x[i] >= a. Уявіть, що ви кинули упереджену монету з ймовірністю головок = 0,9. Ви кидаєте монету, поки не отримаєте принаймні 10 голів та 10 хвостів. На зупинці у вас буде в середньому набагато більше голів, ніж у хвостів. Починаючи з x[1] = ... = x[k] = aозначає, що ви ігноруєте той факт, що вихідні точки кожного з x[i]них різні через різні p[i].
Тім

Я бачу вашу думку. Єдине, що мені не подобається у вашому рішенні, це те, що я думаю, що це може бути дуже неефективним для певного вибору параметрів.
бекко

1

Ось мої зусилля в спробі перевести код Тіма на Python. Оскільки я витратив деякий час на розуміння цієї проблеми і зашифрував алгоритми в Python, я подумав поділитися ними тут на випадок, коли люди зацікавлені.

  1. Алгоритм Прийняти-відхилити :
def sample_truncated_multinomial_accept_reject(k, pVec, a, b):
    x = list(np.random.multinomial(k, pVec, size=1)[0])
    h = [x[i] >= a[i] and x[i] <= b[i] for i in range(len(x))]
    while sum(h) < len(h):
        x = list(np.random.multinomial(k, pVec, size=1)[0])
        h = [x[i] >= a[i] and x[i] <= b[i] for i in range(len(x))]
    return x
  1. Пряме моделювання
def truncated_multinomial_direct_sampling_from_urn(k, pVec, a, b):
    n = len(pVec)
    while True:
        pp = pVec 
        x = [0 for _ in range(n)] 
        while True:
            if sum([x[h] < b[h] for h in range(n)])==1:
                indx = [h for h in range(n) if x[h] < b[h]][0]
                x[indx] = k - sum(x)
                break
            i = np.random.choice(n, 1, p=pp)[0]
            x[i] += 1
            if x[i] == b[i]:
                pp = [pp[j]/(1-pp[i]) for j in range(n)]
                pp[i] = 0 
            if sum(x) == k:
                break  
        if sum([x[h] < a[h] for h in range(n)]) == 0:
            break 
    return x 
  1. Алгоритм Metropolis
def compute_log_function(x, pVec, a, b):
    x_less_a = sum([x[i] < a[i] for i in range(len(pVec))])
    x_more_a = sum([x[i] > b[i] for i in range(len(pVec))])
    if x_less_a or x_more_a or sum(x) != k:
        return float("-inf")
    return np.sum(np.log(pVec)*x - np.array([math.lgamma(h+1) for h in x]))
def sampling_distribution(original, pVec, a, b, step):
    x = copy.deepcopy(original) 
    idx = np.random.choice(len(x), 2, replace=False)
    u = np.random.choice(step, 1)[0]
    x[idx[0]] -= u
    x[idx[1]] += u
    x_less_a = sum([x[i] < a[i] for i in range(len(pVec))])
    x_more_a = sum([x[i] > b[i] for i in range(len(pVec))])
    while x_less_a or x_more_a or sum(x) != k:
        x = copy.deepcopy(original)  
        idx = np.random.choice(len(x), 2, replace=False)
        u = np.random.choice(step, 1)[0]
        x[idx[0]] -= u
        x[idx[1]] += u
        x_less_a = sum([x[i] < a[i] for i in range(len(pVec))])
        x_more_a = sum([x[i] > b[i] for i in range(len(pVec))])
    return x 
def sample_truncated_multinomial_metropolis_hasting(k, pVec, a, b, iters, step=1):
    tmp=sample_truncated_multinomial_accept_reject(k, pVec, a, b)[0]
    step = max(2, step)
    for i in range(iters):
        proposal = sampling_distribution(tmp, pVec, a, b, step)
        if compute_log_function(proposal, pVec, a, b) == float("-inf"):
            continue             
        prob = np.exp(np.array(compute_log_function(proposal, pVec, a, b)) -\
                      np.array(compute_log_function(tmp, pVec, a, b)))
        if np.random.uniform() < prob:
            tmp = proposal 
        step -= 1 
    return tmp

Для повної реалізації цього коду, будь ласка, перегляньте моє сховище Github за адресою

https://github.com/mohsenkarimzadeh/sampling

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.