잘린 다항 분포를 샘플링하는 방법은 무엇입니까?


9

잘린 다항 분포를 샘플링하는 알고리즘이 필요합니다. 그건,

x1Zp1x1pkxkx1!xk!

여기서 는 정규화 상수이고 에는 양의 성분이 있으며 입니다. 범위 의 값만 고려합니다 .Zxkxi=nxaxb

이 잘린 다항 분포를 어떻게 샘플링 할 수 있습니까?

참고 : 잘리지 않은 다항 분포를 샘플링하는 알고리즘 은 Wikipedia 를 참조하십시오 . 이 알고리즘을 잘린 분포에 적용 할 수있는 방법이 있습니까?

균일 한 버전 : 문제의 더 간단한 버전은 모든 를 . 이 경우 잘린 분포를 샘플링하는 알고리즘을 설계 할 수있는 경우 게시하십시오. 일반적인 대답은 아니지만 현재 다른 실제 문제를 해결하는 데 도움이됩니다.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 알고리즘 을 사용하는 것 입니다. 알고리즘은 직접 시뮬레이션을 사용하여 초기화되지만 첫 번째 샘플 을 그리기 위해 다르게 초기화 될 수 있습니다 . 다음 단계를 반복적으로 수행합니다. 제안 값 은 확률 가진 로 수락되고 , 그렇지 않으면 값이 이곳은. 제안으로 나는 함수 를 사용하여 값 을 취하고 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이런 경우 매우 드문 일이 발생할 것으로 예상됩니다 (이러한 분포의 실제 사례는 가설과 일치하는 표본을 찾을 때까지 표본 추출을 반복하는 연구원 일 것이므로 무작위 표본 추출보다 부정 행위와 더 관련이 있습니다) .

분포를 Rukhin (2007, 2008)으로 정의하면 각 범주에 대해 사례를 샘플링합니다 (예 : 비례하여 샘플링) .npipi


루킨, AL (2007). 처리 할당 문제에서 일반적인 순서 통계와 기하 랜덤 변수의 합. 통계 및 확률 문자, 77 (12), 1312-1321.

루킨, AL (2008). 균형 할당 문제에서 규칙 중지 : 정확한 분포 및 점근 분포. 순차적 분석, 27 (3), 277-292.


유효하지 않은 샘플을 거부하는 것은 너무 느릴 수 있습니다. 번역, , 보다 간단합니다 . 이런 식으로 상한, 만 걱정할 수 있습니다. 샘플을 거부 어디에 경우에 당신은 라인을 제거 할 수 있습니다 위반 (하나의 값 임신을 할 수 이 거부 매우 비효율적)yi=xiaim=niaiyibiaixaa
becko

@becko 당신이 나와 같은 방법을 비교하면 다른 솔루션 을 제공한다는 것을 알 수 있습니다.
Tim

나는 그들이 어떻게 다를 수 있는지 이해하지 못합니까? 내가 한 것은 변수의 변경이었습니다.
becko

@becko 당신의 출발점은 전부 x[i] >= a입니다. 머리 = 0.9의 확률로 바이어스 동전을 던졌다 고 상상해보십시오. 머리가 10 개 이상, 꼬리가 10 개가 될 때까지 동전을 던집니다. 정지 지점에서는 평균적으로 꼬리보다 머리가 훨씬 더 많습니다. 에서 시작 x[1] = ... = x[k] = a한다는 것은의 시작점 x[i]이 서로 다르기 때문에 무시한다는 것을 의미합니다 p[i].
Tim

너의 의도를 알 겠어. 솔루션에 대해 내가 싫어하는 유일한 점은 특정 매개 변수 선택에 매우 비효율적이라고 생각합니다.
becko

1

Tim의 R 코드를 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. 메트로폴리스 알고리즘
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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.