Làm thế nào để lấy mẫu phân phối đa cực bị cắt ngắn?


9

Tôi cần một thuật toán để lấy mẫu phân phối đa cực bị cắt ngắn. Đó là,

x1Zp1x1pkxkx1!xk!

Trong đó là hằng số chuẩn hóa, có thành phần dương và . Tôi chỉ xem xét các giá trị của trong phạm vi .x k x i = n x axbZxkxi=nxaxb

Làm thế nào tôi có thể lấy mẫu phân phối đa cực bị cắt này?

Lưu ý: Xem Wikipedia để biết thuật toán để lấy mẫu phân phối đa cực không cắt ngắn. Có cách nào để thích ứng thuật toán này với phân phối bị cắt không?

Phiên bản thống nhất: Một phiên bản đơn giản hơn của vấn đề là lấy tất cả bằng nhau, . Nếu bạn có thể thiết kế một thuật toán để lấy mẫu phân phối bị cắt ít nhất trong trường hợp này, vui lòng gửi nó. Mặc dù không phải là câu trả lời chung, nhưng điều đó sẽ giúp tôi giải quyết các vấn đề thực tế khác vào lúc này.p i = 1 / kpipi=1/k

Câu trả lời:


9

Nếu tôi hiểu chính xác về bạn, bạn muốn lấy mẫu các giá trị từ phân phối đa thức với xác suất sao cho , tuy nhiên bạn muốn phân phối bị cắt bớt nên cho tất cả .x1,,xkp1,,pkixi=naixibixi

Tôi thấy ba giải pháp (không thanh lịch như trong trường hợp không cắt ngắn):

  1. Chấp nhận từ chối. Mẫu từ đa cực không cắt, chấp nhận mẫu nếu nó phù hợp với ranh giới cắt, nếu không thì từ chối và lặp lại quy trình. Nó nhanh, nhưng có thể rất không hiệu quả.
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. Mô phỏng trực tiếp. Mẫu trong thời trang tương tự như việc xử lý dữ-tạo, tức là đá cẩm thạch đơn mẫu từ một urn ngẫu nhiên và lặp lại quá trình này cho đến khi bạn lấy mẫu bi trong tổng số, nhưng khi bạn triển khai tổng số viên bi từ trao urn ( là đã tương đương với ) sau đó dừng vẽ từ urn như vậy. Tôi đã thực hiện điều này trong một kịch bản dưới đây.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. Thuật toán đô thị. Cuối cùng, cách tiếp cận thứ ba và hiệu quả nhất sẽ là sử dụng thuật toán Metropolis . Thuật toán được khởi tạo bằng cách sử dụng mô phỏng trực tiếp (nhưng có thể được khởi tạo khác nhau) để vẽ mẫu đầu tiên . Trong các bước sau lặp lại: giá trị đề xuất được chấp nhận là với xác suất , nếu không, giá trị được lấy đó là nơi, nơi. Như một đề xuất, tôi đã sử dụng hàm lấy giá trị và chuyển ngẫu nhiên từ 0 sang số trường hợp và chuyển nó sang danh mục khác.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)
}

Thuật toán bắt đầu ở và sau đó đi lang thang xung quanh các khu vực phân phối khác nhau. Nó rõ ràng là nhanh hơn những cái trước, nhưng bạn cần nhớ rằng nếu bạn sử dụng nó để lấy mẫu số lượng nhỏ các trường hợp, thì bạn có thể kết thúc với các lần rút gần nhau. Một vấn đề khác là bạn cần quyết định về kích thước, tức là thuật toán nên nhảy lớn như thế nào - quá nhỏ có thể dẫn đến di chuyển chậm, quá lớn có thể dẫn đến đưa ra quá nhiều đề xuất không hợp lệ và từ chối chúng. Bạn có thể xem ví dụ về việc sử dụng nó dưới đây. Trên các ô bạn có thể thấy: mật độ biên ở hàng đầu tiên, traceplots ở hàng thứ hai và các ô hiển thị các bước nhảy tiếp theo cho các cặp biến.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)

nhập mô tả hình ảnh ở đây

Vấn đề với lấy mẫu từ phân phối này là mô tả một chiến lược lấy mẫu rất kém hiệu quả nói chung. Hãy tưởng tượng rằng và , và gần với , trong trường hợp như vậy bạn muốn lấy mẫu cho các danh mục có xác suất khác nhau, nhưng mong muốn tương tự tần số cuối cùng. Trong trường hợp cực đoan, hãy tưởng tượng phân phối hai loại trong đó và ,p1pka1==akb1=bkaibip1p2a1a2b1b2, trong trường hợp như vậy, bạn mong đợi một sự kiện rất hiếm xảy ra (ví dụ thực tế về phân phối như vậy sẽ là nhà nghiên cứu lặp lại việc lấy mẫu cho đến khi anh ta tìm thấy mẫu phù hợp với giả thuyết của mình, vì vậy nó có liên quan nhiều đến gian lận hơn là lấy mẫu ngẫu nhiên) .

Phân phối sẽ ít gặp vấn đề hơn nếu bạn xác định nó là Rukhin (2007, 2008) trong đó bạn lấy mẫu các trường hợp cho mỗi danh mục, tức là mẫu theo tỷ lệ với 's.npipi


Rukhin, AL (2007). Thống kê đơn hàng bình thường và tổng các biến ngẫu nhiên hình học trong các vấn đề phân bổ điều trị. Số liệu thống kê & xác suất, 77 (12), 1312-1321.

Rukhin, AL (2008). Các quy tắc dừng trong các vấn đề phân bổ cân bằng: Phân phối chính xác và tiệm cận. Phân tích tuần tự, 27 (3), 277-292.


Từ chối các mẫu không hợp lệ có thể quá chậm. Có lẽ đơn giản hơn để thực hiện một bản dịch, , . Bằng cách này, bạn chỉ có giới hạn trên, phải lo lắng. Sau đó, bạn có thể loại bỏ dòng mà bạn từ chối mẫu nếu bị vi phạm (ai có thể thụ thai giá trị của nơi từ chối này sẽ rất hiệu quả) m = n - i a i y ib i - a i x a ayi=xiaim=niaiyibiaixaa
becko

@becko nếu bạn so sánh cách tiếp cận như vậy với cách tiếp cận được mô tả bởi tôi, bạn sẽ thấy rằng họ đưa ra các giải pháp khác nhau .
Tim

Tôi không hiểu làm thế nào họ có thể khác nhau? Tất cả tôi đã làm là một sự thay đổi của các biến.
vẫy gọi

@becko điểm bắt đầu của bạn là tất cả x[i] >= a. Hãy tưởng tượng bạn ném một đồng xu thiên vị với xác suất đứng đầu = 0,9. Bạn tung đồng xu cho đến khi bạn nhận được ít nhất 10 cái đầu và 10 cái đuôi. Tại điểm dừng, bạn có trung bình nhiều đầu hơn đuôi. Bắt đầu tại x[1] = ... = x[k] = acó nghĩa là bạn bỏ qua thực tế là điểm bắt đầu của mỗi điểm x[i]khác nhau do khác nhau p[i].
Tim

Tôi thấy điểm của bạn. Điều duy nhất tôi không thích về giải pháp của bạn là tôi nghĩ rằng nó có thể rất không hiệu quả đối với các lựa chọn cụ thể của các tham số.
vẫy gọi

1

Đây là nỗ lực của tôi trong việc cố gắng dịch mã R của Tim sang Python. Vì tôi đã dành thời gian để hiểu vấn đề này và mã hóa các thuật toán trong Python, tôi nghĩ sẽ chia sẻ chúng ở đây trong trường hợp mọi người quan tâm.

  1. Thuật toán chấp nhận từ chối :
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. Mô phỏng trực tiếp
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. Thuật toán đô thị
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

Để thực hiện đầy đủ mã này, vui lòng xem kho Github của tôi tại

https://github.com/mohsenkarimzadeh/sampling

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.