Scipy.stats gaussian_kde 从条件分布中重新采样

问题描述 投票:0回答:2

我正在使用 scipy.stats 中的 gaussian_kde 来拟合 X 和 Y 上的多元数据的联合 PDF。

现在我想根据 X 值有条件地从该 PDF 中重新采样。例如,一旦我的 X=x,就从其条件分布生成 Y。

让我们使用文档中的示例这里

kernel.resample(1)
将在所有分布上生成一对 (X,Y)。例如,一旦 X 为 0,我如何生成 Y?

python scipy statistics scipy.stats
2个回答
1
投票

一种方法可能是从 pdf 创建自定义连续分布。 可以通过

kernel
函数创建 pdf。由于 pdf 需要面积为 1,因此仅限于给定
x0
的内核应按面积缩放。

自定义分发似乎相当慢。更快的解决方案可能是从

ys = np.linspace(-10, 10, 1000); kernel(np.vstack([np.full_like(ys, x0), ys]))
创建直方图并使用
rv_histogram
。更快(但随机性要低得多)的方法是使用
np.random.choice(..., p=...)
以及根据约束内核计算的 p。

以下代码从采用 2D kde 的链接示例代码开始。

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

def measure(n):
    m1 = np.random.normal(size=n)
    m2 = np.random.normal(scale=0.5, size=n)
    return m1 + m2, m1 - m2 ** 2

m1, m2 = measure(2000)
xmin = m1.min()
xmax = m1.max()
ymin = m2.min()
ymax = m2.max()

X, Y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
positions = np.vstack([X.ravel(), Y.ravel()])
values = np.vstack([m1, m2])
kernel = stats.gaussian_kde(values)
Z = np.reshape(kernel(positions).T, X.shape)

x0 = 0.678

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4))
ax1.imshow(np.rot90(Z), cmap=plt.cm.magma_r, alpha=0.4, extent=[xmin, xmax, ymin, ymax])
ax1.plot(m1, m2, 'k.', markersize=2)
ax1.axvline(x0, color='dodgerblue', ls=':')
ax1.set_xlim([xmin, xmax])
ax1.set_ylim([ymin, ymax])

# create a distribution given the kernel function limited to x=x0
class Special_distrib(stats.rv_continuous):
    def _pdf(self, y, x0, area_x0):
        return kernel(np.vstack([np.full_like(y, x0), y])) / area_x0

ys = np.linspace(-10, 10, 1000)
area_x0 = np.trapz(kernel(np.vstack([np.full_like(ys, x0), ys])), ys)

special_distr = Special_distrib(name="special")

vals = special_distr.rvs(x0, area_x0, size=500)
ax2.hist(vals, bins=20, color='dodgerblue')

plt.show()

example plot


0
投票

我编写了一个小包(PyPI 上的

kdetools
)来使用
scipy.stats.gaussian_kde
的直接替换超类进行条件采样。将其应用到 JohanC 的示例中:

import numpy as np
import matplotlib.pyplot as plt
import kdetools as kt

def measure(n):
    "Measurement model, return two coupled measurements."
    m1 = np.random.normal(size=n)
    m2 = np.random.normal(scale=0.5, size=n)
    return m1 + m2, m1 - m2**2

X, Y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
positions = np.vstack([X.ravel(), Y.ravel()])
values = np.vstack([m1, m2])
kernel = kt.gaussian_kde(values)
kernel.set_bandwidth(bw_method='cv', bw_type='diagonal')
Z = np.reshape(kernel(positions).T, X.shape)

fig, ax = plt.subplots()
ax.imshow(np.rot90(Z), cmap=plt.cm.gist_earth_r,
          extent=[xmin, xmax, ymin, ymax])
ax.plot(m1, m2, 'k.', markersize=1)
ax.set_xlim([xmin, xmax])
ax.set_ylim([ymin, ymax])
plt.show()

KDE plot

# Take samples of y|x=-1, 0, 1
x_cond = np.array([[-1],[0],[1]])
samples = kernel.conditional_resample(10000, x_cond=x_cond, dims_cond=[0])

fig, ax = plt.subplots(1, 1, figsize=(6,4))
xs = np.linspace(-4, 4, 1000)

for i in range(3):
    kde1d = kt.gaussian_kde(samples[i].ravel())
    ax.plot(xs, kde1d(xs), label=f'y|x={x_cond[i]}')
    ax.hist(samples[i].ravel(), density=True, bins=50, color=f'C{i}', alpha=0.5)
ax.legend();

Conditional KDEs

也很快:

%timeit samples = kernel.conditional_resample(10000, x_cond=x_cond, dims_cond=[0])
3.36 ms ± 123 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
© www.soinside.com 2019 - 2024. All rights reserved.