从密度分布中抽样随机值

Sample Random values from a density distribution

大家好, 我正在尝试从 0 到 1 之间的随机值进行采样,权重由上面的数据提供。我使用 scipy.stats.gaussian_kde 及其 .resample(n) 方法找到了该问题的部分解决方案。我的主要问题是,因为我的大部分数据非常接近于 0,所以重新采样 returns 一堆负数会搞乱我以后的计算。

有没有办法限制我的重采样全部大于零,而不改变样本space?我考虑过只取所有东西的绝对值去除底片,但我不知道这是否反映了分布权重。

澄清一下,我重新采样的每个值 (n) 都对应于我代码中的一个特定变量,所以我不能只删除小于零的数字。

# Here is a little sample dataset if you need something to work this out!
import numpy as np
data = np.array([0.147, 0.066, 0.017, 0.011, 0.040, 0.087, 0.024, 0.127, 0.071, 0.127,
                 0.027, 0.008, 0.067, 0.032, 0.247, 0.028, 0.122, 0.304, 0.074, 0.119])
# Thank you!

您可以使用支持度不包含负数的分布。例如,从指数分布中抽样可能适用于您提供的示例数组:

import numpy as np
from scipy.stats import expon
import matplotlib.pyplot as plt

data = np.array([0.147, 0.066, 0.017, 0.011, 0.040, 0.087, 0.024, 0.127, 0.071, 0.127, 0.027, 0.008, 0.067, 0.032, 0.247, 0.028, 0.122, 0.304, 0.074, 0.119])

# fit exponential model using data
loc, scale = expon.fit(data)

# plot histogram and model
fig, ax = plt.subplots()
ax.hist(data, density = True)
x = np.linspace(0.01, 1, 200)
ax.plot(x, expon.pdf(x, loc, scale), 'k-')
plt.show()

# sample from your modelled distribution using your fitted loc and scale parameters
sample = expon.rvs(loc, scale)

您可以使用拒绝抽样或逆变换抽样直接对数据进行抽样。 该算法有 4 个步骤:

  1. 计算数据的累积分布函数 CDF。
  2. 从区间 [0,1] 的均匀分布中抽取一个随机数 u
  3. 求 CDF 的倒数 InverseCDF
  4. 计算 X = InverseCDF(u) 其中 X 将分配为 CDF(X)

下面是一个 Python 示例代码,使用 NumPy 来说明算法:

import numpy as np
import numpy.random as ra
from matplotlib import pyplot as plt


'''
Step 1: Compute cumulative distribution function
'''
data = [[1, 2, 3, 4, 5, 6],[100, 200, 400, 310, 130, 50]]
event_value = np.array(data[0])
event_frequency = np.array(data[1])

prob = event_frequency/float(sum(event_frequency))
cum_prob = np.cumsum(prob)
print(prob)
print(cum_prob)

'''
Step 2: Sample uniform random numbers
'''
N = 10000
U = ra.uniform(0, 1, N)


'''
Step 3: Generate Samples from Inverse CDF:
'''
sample_x = [int(event_value[np.argwhere(cum_prob == min(cum_prob[(cum_prob - u) > 0]))]) for u in U]


'''
Step 4: Comparison
'''
sample_x = (np.array(sample_x)-1).astype(int)
times = np.arange(0,6,1)
lc = np.bincount(sample_x, minlength=len(times))

plot1, = plt.plot(lc/float(sum(lc)), 'r--', label='Sampled events')
plot2, = plt.plot(prob,'g',label='Original data')
plt.xlabel('Event Value')
plt.ylabel('Probability')
plt.legend(handles=[plot1,plot2])
plt.show()

输出:

要完成 Ben Devries 的回答,您有多种选择来处理这种情况。您面临的是一个 cut-off 为零的分布(让我们指出这可能不是这种情况,但了解您的数据,您似乎确定这是不可能的,这没关系!)。 Gaussian KDE 处理得不是很好,因为它们通常是在真实 space.

上定义的

明智的选择是从 KDE 切换到参数估计。这意味着您假设概率密度的一种形式(基于数据的形式和您对它们来源的了解)并尝试调整概率密度参数(例如许多分布的位置和比例)以使密度适合数据.在您的情况下,分布看起来很像指数分布。

如果你没有头绪,你可以尝试坚持使用 KDE(顺便说一句 non-parametric 估计的一种形式),并使用随机变量转换来管理 cut-off:你尝试对你的数据应用一个函数,这样它就可以很容易地被 KDE 拟合,并在之后很容易地返回到原始分布。

在那里,对数转换似乎是应用的完美函数。 A link 用于详细信息:https://thirdorderscientist.org/homoclinic-orbit/2013/10/24/kernel-density-estimation-for-random-variables-with-bounded-support-mdash-the-transformation-trick

在下一个脚本中,我尝试绘制提到的各种方法(直接 KDE,如 Ben DeVries 所说,用指数拟合),以及转换变量上的 KDE(我也为此重新采样变量,并绘制直方图):

import numpy as np
from scipy.stats import expon, gaussian_kde
import matplotlib.pyplot as plt

data = np.array([0.147, 0.066, 0.017, 0.011, 0.040, 0.087, 0.024, 0.127, 0.071, 0.127,
                 0.027, 0.008, 0.067, 0.032, 0.247, 0.028, 0.122, 0.304, 0.074, 0.119])


# gaussian KDE
kde = gaussian_kde(data)
x_kde = np.linspace(-0.2, 0.6, 10000)
y_kde = kde.evaluate(x_k)

# fit an exponential distribution
loc, scale = expon.fit(data)
x_exp = np.linspace(0.01, 0.6, 10000)
y_exp = expon.pdf(x_exp, loc, scale)

# use random variable transformation
log_data = np.log(data)
kde_log = gaussian_kde(log_data)
x_kl = np.linspace(-5, -0.5, 10000) # don't hesitate to plot kde_log over x_kl !
x_klt = np.exp(x_kl) # turn back to x (y = log(x) <-> x = exp(y))
y_klt = [kde_log.evaluate(x) / np.exp(x) for x in x_kl] # turn back to fX(x) = fY(log(x)) / x

fig, ax = plt.subplots(1, 1, dpi=120)
ax.hist(data, label="data", density=True)
ax.plot(x_kde, y_kde, label="kde")
ax.plot(x_exp, y_exp, label="exp")
ax.plot(x_klt, y_klt, label="kde_transformed")

#let's try to resample using KDE.resample(n)
x_resample = kde_log.resample(1000)[0]
y = [np.exp(x) for x in x_resample]
ax.hist(y, density=True, bins=100, histtype='step', #plot using 'step' to see something
        label="resampled_from_transformed") 

ax.legend()

剧情:

如您所见: