Làm cách nào tôi có thể rút ra một giá trị ngẫu nhiên từ ước tính mật độ hạt nhân?


10

Tôi có một số quan sát và tôi muốn bắt chước lấy mẫu dựa trên những quan sát này. Ở đây tôi xem xét một mô hình không tham số, cụ thể, tôi sử dụng làm mịn kernel để ước tính CDF từ các quan sát hạn chế. Sau đó, tôi rút ra các giá trị ngẫu nhiên từ CDF thu được. Sau đây là mã của tôi, (ý tưởng là lấy ngẫu nhiên một tích lũy xác suất sử dụng phân phối đồng đều và lấy nghịch đảo của CDF đối với giá trị xác suất)

x = [randn(100, 1); rand(100, 1)+4; rand(100, 1)+8];
[f, xi] = ksdensity(x, 'Function', 'cdf', 'NUmPoints', 300);
cdf = [xi', f'];
nbsamp = 100;
rndval = zeros(nbsamp, 1);
for i = 1:nbsamp
    p = rand;
   [~, idx] = sort(abs(cdf(:, 2) - p));
   rndval(i, 1) = cdf(idx(1), 1);
end
figure(1);
hist(x, 40)
figure(2);
hist(rndval, 40)

Như được hiển thị trong mã, tôi đã sử dụng một ví dụ tổng hợp để kiểm tra quy trình của mình, nhưng kết quả không đạt yêu cầu, như được minh họa bởi hai hình dưới đây (hình đầu tiên là cho các quan sát mô phỏng và hình thứ hai cho thấy biểu đồ được vẽ từ CDF ước tính) :

Hình 1 Hình 2

Có ai biết vấn đề ở đâu không? Cảm ơn bạn trước.


Bản lề lấy mẫu biến đổi nghịch đảo sử dụng CDF nghịch đảo . vi.wikipedia.org/wiki/Inverse_transform_sampling
Sycorax nói Phục hồi

1
Công cụ ước tính mật độ hạt nhân của bạn tạo ra một phân phối là một hỗn hợp vị trí của phân phối hạt nhân, vì vậy tất cả những gì bạn cần để rút ra một giá trị từ ước tính mật độ hạt nhân là (1) rút ra một giá trị từ mật độ hạt nhân và sau đó (2) chọn độc lập một trong hai các điểm dữ liệu ngẫu nhiên và thêm giá trị của nó vào kết quả của (1). Cố gắng đảo ngược KDE trực tiếp sẽ kém hiệu quả hơn nhiều.
whuber

@Sycorax Nhưng tôi thực sự tuân theo quy trình lấy mẫu biến đổi nghịch đảo như được mô tả trong Wiki. Vui lòng xem mã: p = rand; [~, idx] = sort (abs (cdf (:, 2) - p)); rndval (i, 1) = cdf (idx (1), 1);
emberbirl

@whuber Tôi không chắc liệu sự hiểu biết của tôi về ý tưởng của bạn có chính xác hay không. Vui lòng giúp kiểm tra: đầu tiên lấy mẫu lại một giá trị từ các quan sát; và sau đó rút ra một giá trị từ kernel, nói phân phối chuẩn thông thường; cuối cùng, thêm chúng lại với nhau?
emberbirl

Câu trả lời:


12

Công cụ ước tính mật độ hạt nhân (KDE) tạo ra phân phối là hỗn hợp vị trí của phân phối hạt nhân, do đó, để rút ra một giá trị từ ước tính mật độ hạt nhân, tất cả những gì bạn cần làm là (1) rút ra một giá trị từ mật độ hạt nhân và sau đó (2) độc lập chọn một trong các điểm dữ liệu một cách ngẫu nhiên và thêm giá trị của nó vào kết quả của (1).

Đây là kết quả của thủ tục này được áp dụng cho một tập dữ liệu giống như trong câu hỏi.

Nhân vật

Biểu đồ bên trái mô tả mẫu. Để tham khảo, đường cong màu đen biểu thị mật độ mà mẫu được vẽ. Đường cong màu đỏ vẽ đồ thị KDE của mẫu (sử dụng băng thông hẹp). (Không có vấn đề gì, hoặc thậm chí là bất ngờ, rằng các đỉnh đỏ ngắn hơn các đỉnh đen: KDE lan truyền mọi thứ ra ngoài, do đó các đỉnh sẽ giảm xuống để bù lại.)

Biểu đồ bên phải mô tả một mẫu (có cùng kích thước) từ KDE. Các đường cong màu đen và đỏ giống như trước đây.

Rõ ràng, quy trình được sử dụng để lấy mẫu từ mật độ hoạt động. Nó cũng cực kỳ nhanh: việc Rtriển khai bên dưới tạo ra hàng triệu giá trị mỗi giây từ bất kỳ KDE nào. Tôi đã nhận xét rất nhiều về việc hỗ trợ chuyển sang Python hoặc các ngôn ngữ khác. Thuật toán lấy mẫu được thực hiện trong hàm rdensvới các dòng

rkernel <- function(n) rnorm(n, sd=width) 
sample(x, n, replace=TRUE) + rkernel(n)  

rkernelvẽ ncác mẫu iid từ hàm kernel trong khi samplevẽ ncác mẫu có thay thế từ dữ liệu x. Toán tử "+" thêm hai mảng thành phần mẫu theo thành phần.


Đối với những người muốn một minh chứng chính thức về tính đúng đắn, tôi cung cấp nó ở đây. Đặt đại diện cho phân phối kernel bằng CDF và để dữ liệu là . Theo định nghĩa của ước tính kernel, CDF của KDE làKx = ( x 1 , x 2 , Mạnh , x n )FKx=(x1,x2,,xn)

Fx^;K(x)=1ni=1nFK(xxi).

Công thức trước nói rằng rút từ phân phối dữ liệu theo kinh nghiệm (nghĩa là nó đạt giá trị với xác suất cho mỗi ), rút ​​ra một biến ngẫu nhiên từ phân phối hạt nhân và tính tổng chúng. Tôi nợ bạn một bằng chứng rằng hàm phân phối của là của KDE. Hãy bắt đầu với định nghĩa và xem nó dẫn đến đâu. Đặt là số thực bất kỳ. Điều hòa trên chox i 1 / n i Y X + Y x XXxi1/niYX+YxX

FX+Y(x)= =Pr(X+Yx)= =ΣTôi= =1nPr(X+Yx|X= =xTôi)Pr(X= =xTôi)= =ΣTôi= =1nPr(xTôi+Yx)1n= =1nΣTôi= =1nPr(Yx-xTôi)= =1nΣTôi= =1nFK(x-xTôi)= =Fx^;K(x),

như đã tuyên bố.


#
# Define a function to sample from the density.
# This one implements only a Gaussian kernel.
#
rdens <- function(n, density=z, data=x, kernel="gaussian") {
  width <- z$bw                              # Kernel width
  rkernel <- function(n) rnorm(n, sd=width)  # Kernel sampler
  sample(x, n, replace=TRUE) + rkernel(n)    # Here's the entire algorithm
}
#
# Create data.
# `dx` is the density function, used later for plotting.
#
n <- 100
set.seed(17)
x <- c(rnorm(n), rnorm(n, 4, 1/4), rnorm(n, 8, 1/4))
dx <- function(x) (dnorm(x) + dnorm(x, 4, 1/4) + dnorm(x, 8, 1/4))/3
#
# Compute a kernel density estimate.
# It returns a kernel width in $bw as well as $x and $y vectors for plotting.
#
z <- density(x, bw=0.15, kernel="gaussian")
#
# Sample from the KDE.
#
system.time(y <- rdens(3*n, z, x)) # Millions per second
#
# Plot the sample.
#
h.density <- hist(y, breaks=60, plot=FALSE)
#
# Plot the KDE for comparison.
#
h.sample <- hist(x, breaks=h.density$breaks, plot=FALSE)
#
# Display the plots side by side.
#
histograms <- list(Sample=h.sample, Density=h.density)
y.max <- max(h.density$density) * 1.25
par(mfrow=c(1,2))
for (s in names(histograms)) {
  h <- histograms[[s]]
  plot(h, freq=FALSE, ylim=c(0, y.max), col="#f0f0f0", border="Gray",
       main=paste("Histogram of", s))
  curve(dx(x), add=TRUE, col="Black", lwd=2, n=501) # Underlying distribution
  lines(z$x, z$y, col="Red", lwd=2)                 # KDE of data

}
par(mfrow=c(1,1))

Xin chào @whuber, tôi muốn trích dẫn ý tưởng này trong bài viết của mình. Bạn có một số giấy tờ đã được xuất bản cho điều này? Cảm ơn bạn.
emberbfl ngày

2

Bạn lấy mẫu từ CDF trước bằng cách đảo ngược nó. CDF nghịch đảo được gọi là hàm lượng tử; nó là ánh xạ từ [0,1] đến miền của RV. Sau đó, bạn lấy mẫu RV thống nhất ngẫu nhiên dưới dạng phần trăm và chuyển chúng đến hàm lượng tử để lấy mẫu ngẫu nhiên từ phân phối đó.


2
Đây là cách khó: xem bình luận của tôi cho câu hỏi.
whuber

2
@whuber điểm tốt. Không quá say mê trong các khía cạnh lập trình, tôi đã cho rằng chúng ta phải làm việc với CDF trong trường hợp này. Không còn nghi ngờ gì nữa, các hàm bên trong của hàm như vậy lấy mật độ làm mịn của hạt nhân và sau đó tích hợp nó để có được CDF. Tại thời điểm đó, có thể tốt hơn và nhanh hơn để sử dụng lấy mẫu biến đổi nghịch đảo. Tuy nhiên, đề nghị của bạn chỉ cần sử dụng mật độ và mẫu thẳng từ hỗn hợp là tốt hơn.
AdamO

@AdamO Cảm ơn câu trả lời của bạn. Nhưng mã của tôi thực sự theo cùng một ý tưởng như bạn đã nói ở đây. Tôi không biết tại sao các mô hình ba phương thức không thể được sao chép.
emberbirl

@AdamO Ở đây liệu từ "nội bộ" trong bình luận của bạn có nên là "khoảng"? Cảm ơn bạn.
emberbirl

Ember, "nội bộ" có ý nghĩa hoàn hảo với tôi. Một hàm như vậy phải tích hợp mật độ hỗn hợp và xây dựng một nghịch đảo: đó là một quá trình lộn xộn, phức tạp về số lượng như gợi ý của AdamO, và do đó sẽ bị chôn vùi trong hàm - "phần bên trong" của nó.
whuber

1

Ở đây, tôi cũng muốn đăng mã Matlab theo ý tưởng được mô tả bởi whuber, để giúp những người quen thuộc với Matlab hơn R.

x = exprnd(3, [300, 1]);
[~, ~, bw] = ksdensity(x, 'kernel', 'normal', 'NUmPoints', 800);

k = 0.25; % control the uncertainty of generated values, the larger the k the greater the uncertainty
mstd = bw*k;
rkernel = mstd*randn(300, 1);
sampleobs = randsample(x, 300, true);
simobs = sampleobs(:) + rkernel(:);

figure(1);
subplot(1,2,1);
hist(x, 50);title('Original sample');
subplot(1,2,2);
hist(simobs, 50);title('Simulated sample');
axis tight;

Sau đây là kết quả: các kết quả

Xin vui lòng cho tôi biết nếu bất cứ ai tìm thấy bất kỳ vấn đề với sự hiểu biết của tôi và mã. Cảm ơn bạn.


1
Ngoài ra, tôi thấy rằng mã của tôi trong câu hỏi là đúng. Quan sát rằng mô hình không thể được sao chép phần lớn là do sự lựa chọn của băng thông.
emberbirl

0

Không nhìn quá gần vào việc triển khai của bạn, tôi không hoàn toàn nhận được quy trình lập chỉ mục của bạn để rút ra từ ICDF. Tôi nghĩ rằng bạn rút ra từ CDF, không phải là nghịch đảo. Đây là cách thực hiện của tôi:

import sys
sys.path.insert(0, './../../../Python/helpers')
import numpy as np
import scipy.stats as stats
from sklearn.neighbors import KernelDensity

def rugplot(axis,x,color='b',label='draws',shape='+',alpha=1):
    axis.plot(x,np.ones(x.shape)*0,'b'+shape,ms=20,label=label,c=color,alpha=alpha);
    #axis.set_ylim([0,max(axis.get_ylim())])

def PDF(x):
    return 0.5*(stats.norm.pdf(x,loc=6,scale=1)+ stats.norm.pdf(x,loc=18,scale=1));

def CDF(x,PDF):
    temp = np.linspace(-10,x,100)
    pdf = PDF(temp);
    return np.trapz(pdf,temp);

def iCDF(p,x,cdf):
    return np.interp(p,cdf,x);

res = 1000;
X = np.linspace(0,24,res);
P = np.linspace(0,1,res)
pdf  = np.array([PDF(x) for x in X]);#attention dont do [ for x in x] because it overrides original x value
cdf  = np.array([CDF(x,PDF) for x in X]);
icdf = [iCDF(p,X,cdf) for p in P];

#draw pdf and cdf
f,(ax1,ax2) = plt.subplots(1,2,figsize=(18,4.5));
ax1.plot(X,pdf, '.-',label = 'pdf');
ax1.plot(X,cdf, '.-',label = 'cdf');
ax1.legend();
ax1.set_title('PDF & CDF')

#draw inverse cdf
ax2.plot(cdf,X,'.-',label  = 'inverse by swapping axis');
ax2.plot(P,icdf,'.-',label = 'inverse computed');
ax2.legend();
ax2.set_title('inverse CDF');

#draw from custom distribution
N = 100;
p_uniform = np.random.uniform(size=N)
x_data  = np.array([iCDF(p,X,cdf) for p in p_uniform]);

#visualize draws
a = plt.figure(figsize=(20,8)).gca();
rugplot(a,x_data);

#histogram
h = np.histogram(x_data,bins=24);
a.hist(x_data,bins=h[1],alpha=0.5,normed=True);

2
Nếu bạn có một cdf F thì điều đó là F (X) là đồng nhất. Vì vậy, bạn nhận được X bằng cách lấy cdf nghịch đảo của một số ngẫu nhiên từ một phân phối thống nhất. Vấn đề tôi nghĩ là làm thế nào để xác định nghịch đảo khi bạn tạo mật độ hạt nhân.
Michael R. Chernick

Cảm ơn về câu trả lời của bạn. Tôi đã không lấy mẫu trực tiếp từ CDF. Mã cho thấy rằng tôi thực sự đã làm điều tương tự như lấy mẫu biến đổi nghịch đảo. p = rand; % dòng này nhận được một số ngẫu nhiên thống nhất là xác suất tích lũy. [~, idx] = sort (abs (cdf (:, 2) - p)); rndval (i, 1) = cdf (idx (1), 1);% hai dòng này là để xác định lượng tử tương ứng với xác suất tích lũy
emberbfl
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.