मैं एक कर्नेल घनत्व अनुमान से यादृच्छिक रूप से एक मान कैसे आकर्षित कर सकता हूं?


10

मेरे पास कुछ अवलोकन हैं, और मैं इन टिप्पणियों के आधार पर नमूने की नकल करना चाहता हूं। यहां मैं एक गैर-पैरामीट्रिक मॉडल पर विचार करता हूं, विशेष रूप से, मैं सीमित टिप्पणियों से सीडीएफ का अनुमान लगाने के लिए कर्नेल स्मूथिंग का उपयोग करता हूं। फिर मैं प्राप्त सीडीएफ से यादृच्छिक पर मूल्यों को आकर्षित करता हूं। निम्नलिखित मेरा कोड है, और विचार यादृच्छिक रूप से संचयी प्राप्त करना है समान वितरण का उपयोग करके संभाव्यता, और प्रायिकता मान के संबंध में 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)

जैसा कि कोड में दिखाया गया है, मैंने अपनी प्रक्रिया का परीक्षण करने के लिए एक सिंथेटिक उदाहरण का उपयोग किया, लेकिन परिणाम असंतोषजनक है, जैसा कि नीचे दिए गए दो आंकड़ों द्वारा सचित्र है (पहला नकली टिप्पणियों के लिए है, और दूसरा आंकड़ा सीडीएफ से खींचे गए हिस्टोग्राम को दर्शाता है) :

आकृति 1 चित्र 2

क्या कोई है जो जानता है कि समस्या कहां है? पहले ही, आपका बहुत धन्यवाद।


उलटा प्रयोग पर नमूने टिका बदलने उलटा CDF। en.wikipedia.org/wiki/Inverse_transform_sampling
Sycorax का कहना है कि

1
आपका कर्नेल घनत्व अनुमानक एक वितरण का उत्पादन करता है जो कर्नेल वितरण का एक स्थान मिश्रण है, इसलिए आपको कर्नेल घनत्व के अनुमान से एक मान खींचने की आवश्यकता है (1) कर्नेल घनत्व से एक मान आकर्षित करें और फिर (2) स्वतंत्र रूप से एक का चयन करें डेटा यादृच्छिक पर इंगित करता है और (1) के परिणाम में इसका मूल्य जोड़ता है। केडीई को सीधे उलटने का प्रयास बहुत कम कुशल होगा।
whuber

@ साइकोरेक्स लेकिन मैं वास्तव में विकी में वर्णित उलटा रूपांतरण नमूना प्रक्रिया का पालन करता हूं। कृपया कोड देखें: p = rand; [~, आईडीएक्स] = सॉर्ट (एब्स (सीएफडी: (२, २) - पी)); rndval (i, 1) = cdf (idx (1), 1);
एम्बरबिलो

@ जब भी मुझे यकीन नहीं होता कि आपके विचार की मेरी समझ सही है या नहीं। कृपया जाँच में मदद करें: पहले अवलोकनों से मान को फिर से भरें; और फिर कर्नेल से एक मान बनाएं, मानक सामान्य वितरण कहें; अंत में, उन्हें एक साथ जोड़ें?
emberbillow

जवाबों:


12

कर्नेल घनत्व अनुमानक (KDE) एक वितरण का निर्माण करता है जो कर्नेल वितरण का एक स्थान मिश्रण होता है, इसलिए कर्नेल घनत्व के मान का अनुमान लगाने के लिए आपको सभी की आवश्यकता होती है (1) कर्नेल घनत्व से मान निकालें और फिर (2) स्वतंत्र रूप से यादृच्छिक पर डेटा बिंदुओं में से एक का चयन करें और (1) के परिणाम में इसका मूल्य जोड़ें।

यहाँ इस प्रक्रिया का परिणाम है प्रश्न में एक जैसे डेटासेट के लिए लागू किया जाता है।

आकृति

बाईं ओर हिस्टोग्राम नमूने को दर्शाता है। संदर्भ के लिए, काला वक्र घनत्व को प्लॉट करता है जिसमें से नमूना खींचा गया था। लाल वक्र नमूना के केडीई (एक संकीर्ण बैंडविड्थ का उपयोग करके) को प्लॉट करता है। (यह कोई समस्या नहीं है, या अप्रत्याशित भी है, कि लाल चोटियाँ काली चोटियों से छोटी हैं: KDE चीजों को फैलाता है, इसलिए चोटियों को क्षतिपूर्ति करने के लिए कम मिलेगा।)

सही पर हिस्टोग्राम KDE से एक नमूना (समान आकार का) दर्शाता है काले और लाल वक्र पहले जैसे ही हैं।

जाहिर है, घनत्व कार्यों से नमूना करने के लिए इस्तेमाल की जाने वाली प्रक्रिया। यह बहुत तेज़ है: Rनीचे दिया गया कार्यान्वयन किसी भी केडीई से प्रति सेकंड लाखों मान उत्पन्न करता है। मैंने इसे पायथन या अन्य भाषाओं में पोर्टिंग में सहायता करने के लिए भारी टिप्पणी की है। नमूना एल्गोरिथ्म स्वयं rdensलाइनों के साथ फ़ंक्शन में कार्यान्वित किया जाता है

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

rkernelड्रॉ nगिरी समारोह से आईआईडी नमूने जबकि sampleड्रॉ nडेटा से प्रतिस्थापन के साथ नमूने x। "+" ऑपरेटर घटक द्वारा नमूने घटक के दो सरणियों को जोड़ता है।


एफएक्स=(एक्स1,एक्स2,...,एक्सn)

एफएक्स^;(एक्स)=1nΣमैं=1nएफ(एक्स-एक्समैं)

एक्सएक्समैं1/nमैंYएक्स+Yएक्सएक्स

एफएक्स+Y(एक्स)=पीआर(एक्स+Yएक्स)=Σमैं=1nपीआर(एक्स+Yएक्स|एक्स=एक्समैं)पीआर(एक्स=एक्समैं)=Σमैं=1nपीआर(एक्समैं+Yएक्स)1n=1nΣमैं=1nपीआर(Yएक्स-एक्समैं)=1nΣमैं=1nएफ(एक्स-एक्समैं)=एफएक्स^;(एक्स),

जैसा दावा किया गया है।


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

आप पहले सीडीएफ से इनवर्ट करके नमूना लें। उलटा सीडीएफ को क्वांटाइल फ़ंक्शन कहा जाता है; यह आरवी के डोमेन के लिए [0,1] से मैपिंग है। फिर आप प्रतिशत के रूप में यादृच्छिक वर्दी आरवी का नमूना लेते हैं और उन्हें उस वितरण से यादृच्छिक नमूना प्राप्त करने के लिए क्वांटाइल फ़ंक्शन में पास करते हैं।


2
यह कठिन तरीका है: प्रश्न के लिए मेरी टिप्पणी देखें।
whuber

2
@ अच्छी बात। प्रोग्रामेटिक पहलुओं में बहुत अधिक जानकारी के बिना, मैं मान रहा था कि हमें इस उदाहरण में सीडीएफ के साथ काम करना चाहिए। इस तरह के कार्य के लिए कोई संदेह नहीं है कि कर्नेल स्मूद डेंसिटी लेता है और फिर इसे CDF प्राप्त करने के लिए एकीकृत करता है। उस बिंदु पर व्युत्क्रम नमूनाकरण का उपयोग करना शायद बेहतर और तेज़ है। हालांकि, मिश्रण से सीधे घनत्व और नमूने का उपयोग करने का आपका सुझाव बेहतर है।
एडमों

@ अदमो आपके जवाब के लिए धन्यवाद। लेकिन मेरा कोड वास्तव में उसी विचार का अनुसरण करता है जैसा आपने यहां कहा था। मुझे नहीं पता कि त्रि-मोडल पैटर्न को फिर से क्यों नहीं बनाया जा सकता है।
एम्बरबिलो

@ अदमो यहाँ आपकी टिप्पणी में "इंटर्नल" शब्द "अंतराल" होना चाहिए या नहीं? धन्यवाद।
emberbillow

एम्बर, "इंटर्नल" मेरे लिए एकदम सही समझ में आता है। इस तरह के एक फ़ंक्शन को मिश्रण घनत्व को एकीकृत करना पड़ता है और एक व्युत्क्रम का निर्माण होता है: यह एक गड़बड़, संख्यात्मक रूप से जटिल प्रक्रिया है जैसा कि एडमो संकेत देता है, और इसलिए इसे फ़ंक्शन के भीतर दफन किया जाएगा - इसके "इंटर्ल्स।"
whuber

1

यहाँ, मैं व्हीलर द्वारा बताए गए विचार के बाद मतलब कोड भी पोस्ट करना चाहता हूं, उन लोगों की मदद करने के लिए जो आर की तुलना में मतलाब से अधिक परिचित हैं।

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
अतिरिक्त रूप से, मैंने पाया कि प्रश्न में मेरा कोड सही है। अवलोकन कि पैटर्न को पुन: पेश नहीं किया जा सकता है, मोटे तौर पर बैंडविड्थ की पसंद के कारण है।
एम्बरबिलो

0

आपके कार्यान्वयन में बहुत करीब देखे बिना, ICDF से आकर्षित करने के लिए मुझे आपकी अनुक्रमण प्रक्रिया पूरी तरह से नहीं मिलती है। मुझे लगता है कि आप सीडीएफ से आकर्षित होते हैं, उलटा नहीं। यहाँ मेरा कार्यान्वयन है:

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
यदि आपके पास एक C F है तो यह माना जाता है कि F (X) एक समान है। तो आप एक समान वितरण से एक यादृच्छिक संख्या के व्युत्क्रम cdf को ले कर X प्राप्त करते हैं। मुझे लगता है कि समस्या यह है कि जब आप कर्नेल घनत्व का उत्पादन कर रहे हैं तो व्युत्क्रम का निर्धारण कैसे करें।
बजे माइकल आर। चेरिक

आपके उत्तर के लिए धन्यवाद। मैंने सीधे सीडीएफ से नमूना नहीं लिया। कोड से पता चलता है कि मैंने वास्तव में उलटा रूपांतर नमूने के रूप में एक ही काम किया। पी = रैंड; इस पंक्ति को संचयी संभाव्यता के रूप में एक समान यादृच्छिक संख्या मिलती है। [~, आईडीएक्स] = सॉर्ट (एब्स (सीएफडी: (२, २) - पी)); rndval (i, 1) = cdf (idx (1), 1);% इन दो पंक्तियों को संचयी संभाव्यता के अनुसार परिमाण का निर्धारण करना है
emberbillow
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.