커널 밀도 추정값에서 임의의 값을 임의로 그리는 방법은 무엇입니까?


10

몇 가지 관측치가 있으며이 관측치를 기반으로 샘플링을 모방하고 싶습니다. 여기서는 비모수 적 모델, 특히 커널 평활화를 사용하여 제한된 관측치에서 CDF를 추정합니다. 그런 다음 얻은 CDF에서 무작위로 값을 그립니다. 다음은 내 코드입니다. (아이디어는 무작위로 누적됩니다. 균일 분포를 사용한 확률, 확률 값과 관련하여 CDF의 역수를 취함)

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)

코드에서 볼 수 있듯이 합성 예제를 사용하여 절차를 테스트했지만 아래 두 그림에 표시된 것처럼 결과가 만족스럽지 않습니다 (첫 번째는 시뮬레이션 된 관찰에 대한 것이고 두 번째 그림은 추정 된 CDF에서 얻은 막대 그래프를 보여줍니다) :

그림 1 그림 2

문제의 위치를 ​​아는 사람이 있습니까? 미리 감사드립니다.


변환 샘플링은 CDF 사용에 달려 있습니다. en.wikipedia.org/wiki/Inverse_transform_sampling
Sycorax는 Monica Reinstate Monica

1
커널 밀도 추정기는 커널 분포의 위치 혼합 인 분포를 생성하므로 커널 밀도 추정값에서 값을 가져 오려면 (1) 커널 밀도에서 값을 가져온 다음 (2) 독립적으로 데이터는 무작위로 지정되며 그 값을 (1)의 결과에 더합니다. KDE를 직접 반전시키는 것은 훨씬 덜 효율적입니다.
whuber

@Sycorax 그러나 실제로 Wiki에 설명 된 것처럼 역 변환 샘플링 절차를 따릅니다. 코드를 참조하십시오 : p = rand; [~, idx] = 정렬 (abs (cdf (:, 2)-p)); rndval (i, 1) = cdf (idx (1), 1);
emberbillow

@ whuber 나는 당신의 아이디어에 대한 나의 이해가 올바른지 확실하지 않습니다. 다음 사항을 확인하십시오. 먼저 관측치에서 값을 다시 샘플링하십시오. 그런 다음 표준 정규 분포와 같은 커널에서 값을 가져옵니다. 마지막으로 함께 추가 하시겠습니까?
emberbillow

답변:


12

커널 밀도 추정기 (KDE)는 커널 분포의 위치 혼합 인 분포를 생성하므로 커널 밀도 추정값에서 값을 얻으려면 (1) 커널 밀도에서 값을 얻은 다음 (2) 독립적으로 데이터 포인트 중 하나를 임의로 선택하고 그 값을 (1)의 결과에 추가하십시오.

이 절차의 결과는 문제의 데이터 세트와 같은 데이터 세트에 적용됩니다.

그림

왼쪽의 히스토그램은 샘플을 나타냅니다. 참고로 검은 색 곡선은 샘플이 추출 된 밀도를 나타냅니다. 빨간색 곡선은 좁은 대역폭을 사용하여 샘플의 KDE를 표시합니다. (빨간 피크가 검은 피크보다 짧다는 것은 문제가 아니거나 예기치 않은 일이 아닙니다. KDE가 사물을 퍼뜨 리므로 피크가 보상하기 위해 낮아집니다.)

오른쪽의 히스토그램 은 KDE 의 샘플 (동일한 크기) 을 나타냅니다. 검은 색과 빨간색 곡선은 이전과 동일합니다.

분명히, 밀도에서 샘플링하는 데 사용되는 절차가 작동합니다. 또한 매우 빠릅니다. R아래 구현은 모든 KDE에서 초당 수백만 개의 값을 생성합니다. 나는 파이썬이나 다른 언어로의 포팅을 돕기 위해 크게 언급했다. 샘플링 알고리즘 자체는 다음과 같은 기능 rdens으로 구현됩니다.

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

rkernel그리는 n동안 커널 함수에서 IID 샘플을 sample그립니다 n데이터 관련으로 샘플을 x. "+"연산자는 두 개의 샘플 배열을 구성 요소별로 추가합니다.


케이에프케이엑스=(엑스1,엑스2,,엑스)

에프엑스^;케이(엑스)=1나는=1에프케이(엑스엑스나는).

엑스엑스나는1/나는와이엑스+와이엑스엑스

에프엑스+와이(엑스)=홍보(엑스+와이엑스)=나는=1홍보(엑스+와이엑스엑스=엑스나는)홍보(엑스=엑스나는)=나는=1홍보(엑스나는+와이엑스)1=1나는=1홍보(와이엑스엑스나는)=1나는=1에프케이(엑스엑스나는)=에프엑스^;케이(엑스),

주장대로.


#
# 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))

안녕하세요 @ whuber, 나는이 아이디어를 내 논문에 인용하고 싶습니다. 이를 위해 출판 된 논문이 있습니까? 감사합니다.
emberbillow

2

먼저 CDF를 뒤집어서 샘플링합니다. 역 CDF를 Quantile 함수라고합니다. [0,1]에서 RV의 도메인으로의 매핑입니다. 그런 다음 임의의 균일 한 RV를 백분위 수로 샘플링하고 Quantile 함수에 전달하여 해당 분포에서 임의의 샘플을 얻습니다.


2
이것은 어려운 방법입니다. 질문에 대한 내 의견을 참조하십시오.
whuber

2
@ whuber 좋은 지적. 프로그래밍 방식에 너무 몰입하지 않고,이 경우 CDF로 작업해야한다고 가정했습니다. 의심 할 여지없이 그러한 기능의 내부는 커널 스무스 밀도를 취한 다음이를 통합하여 CDF를 얻습니다. 이 시점에서 역변환 샘플링을 사용하는 것이 더 좋고 빠를 것입니다. 그러나 혼합물에서 직접 밀도와 샘플을 사용하는 것이 좋습니다.
AdamO

@AdamO 답변 주셔서 감사합니다. 그러나 내 코드는 실제로 여기에서 말한 것과 동일한 아이디어를 따릅니다. 트라이 모달 패턴을 재현 할 수없는 이유를 모르겠습니다.
emberbillow

@AdamO 여기서 주석에 "internals"라는 단어가 "intervals"인지 여부 감사합니다.
emberbillow

Ember, "내부"는 나에게 완벽하게 이해됩니다. 이러한 함수는 혼합 밀도를 통합하고 역수를 구성해야합니다. 이는 AdamO가 암시하는 것처럼 복잡하고 수치 적으로 복잡한 프로세스이므로 함수 내에 "내부"로 묻 힙니다.
whuber

1

여기에 whuber가 설명한 아이디어에 따라 Matlab 코드를 게시하여 R보다 Matlab에 더 익숙한 사람들을 돕기를 원합니다.

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;

결과는 다음과 같습니다. 결과

내 이해와 코드에 문제가 있으면 알려주십시오. 감사합니다.


1
또한, 질문의 코드가 옳다는 것을 알았습니다. 패턴을 재현 할 수 없다는 관찰은 주로 대역폭 선택으로 인한 것입니다.
emberbillow

0

구현을 너무 자세히 보지 않으면 ICDF에서 인덱싱 절차를 완전히 이해할 수 없습니다. CDF를 사용하는 것이 아니라, CDF를 사용한다고 생각합니다. 내 구현은 다음과 같습니다.

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
cdf F가 있으면 F (X)가 균일하다는 것은 사실이 아닙니다. 따라서 균일 분포에서 난수의 역 cdf를 취하여 X를 얻습니다. 내가 생각하는 문제는 커널 밀도를 생성 할 때 역을 결정하는 방법입니다.
Michael R. Chernick

답변 주셔서 감사합니다. CDF에서 직접 샘플링하지 않았습니다. 이 코드는 실제로 역 변환 샘플링과 동일한 작업을 수행했음을 보여줍니다. p = 랜드; %이 줄은 누적 확률로 균일 한 난수를 얻습니다. [~, idx] = 정렬 (abs (cdf (:, 2)-p)); rndval (i, 1) = cdf (idx (1), 1); %이 두 줄은 누적 확률
emberbillow
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.