为抽样创建概率分布的混合

时间:2017-12-11 18:57:31

标签: python numpy scipy probability-density

是否有一种通用方法可以加入SciPy(或NumPy)概率分布来创建混合概率分布,然后可以从中进行采样?

我有这样的分发用于显示:

mixture_gaussian = (norm.pdf(x_axis, -3, 1) + norm.pdf(x_axis, 3, 1)) / 2

如果然后绘制如下:

double gaussian

但是,我不能从这个生成的模型中采样,因为它只是一个点的列表,将绘制为曲线。

注意,这个特定的分布只是一个简单的例子。我希望能够生成几种分布(包括“子”分布,这不仅仅是正态分布)。理想情况下,我希望函数可以自动标准化(即不必像上面的代码那样明确地执行/ 2

SciPy / NumPy能否提供一些轻松实现此目的的方法?

This answer提供了一种方法,可以从多个分布中进行这样的采样,但它确实需要对给定的混合分布进行一些手工制作,特别是当想要不同地加权不同的“子”分布时。这是可用的,但如果可能的话,我希望方法更清洁,更直接。谢谢!

3 个答案:

答案 0 :(得分:5)

从分布混合中采样(其中PDF添加了一些系数c_1,c_2,... c_n)相当于每个独立采样,然后,对于每个索引,从第k个样本中选取值,概率c_k。

后者,混合,步骤可以用numpy.random.choice有效地完成。这是一个混合了三种分布的例子。分布在distributions中列出,其系数在coefficients中列出。存在脂肪正态分布,均匀分布和窄的正态分布,系数为0.5,0.2,0.3。根据给定系数生成data[np.arange(sample_size), random_idx]后,混合发生在random_idx

import numpy as np
import matplotlib.pyplot as plt

distributions = [
    {"type": np.random.normal, "kwargs": {"loc": -3, "scale": 2}},
    {"type": np.random.uniform, "kwargs": {"low": 4, "high": 6}},
    {"type": np.random.normal, "kwargs": {"loc": 2, "scale": 1}},
]
coefficients = np.array([0.5, 0.2, 0.3])
coefficients /= coefficients.sum()      # in case these did not add up to 1
sample_size = 100000

num_distr = len(distributions)
data = np.zeros((sample_size, num_distr))
for idx, distr in enumerate(distributions):
    data[:, idx] = distr["type"](size=(sample_size,), **distr["kwargs"])
random_idx = np.random.choice(np.arange(num_distr), size=(sample_size,), p=coefficients)
sample = data[np.arange(sample_size), random_idx]
plt.hist(sample, bins=100, density=True)
plt.show()

histogram

答案 1 :(得分:2)

在评论中关注@PaperPanzer的指针后,我创建了以下子类,以便从SciPy发行版中轻松创建混合模型。请注意,我的问题不需要pdf,但我很高兴。

class MixtureModel(rv_continuous):
    def __init__(self, submodels, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.submodels = submodels

    def _pdf(self, x):
        pdf = self.submodels[0].pdf(x)
        for submodel in self.submodels[1:]:
            pdf += submodel.pdf(x)
        pdf /= len(self.submodels)
        return pdf

    def rvs(self, size):
        submodel_choices = np.random.randint(len(self.submodels), size=size)
        submodel_samples = [submodel.rvs(size=size) for submodel in self.submodels]
        rvs = np.choose(submodel_choices, submodel_samples)
        return rvs

mixture_gaussian_model = MixtureModel([norm(-3, 1), norm(3, 1)])
x_axis = np.arange(-6, 6, 0.001)
mixture_pdf = mixture_gaussian_model.pdf(x_axis)
mixture_rvs = mixture_gaussian_model.rvs(10)

答案 2 :(得分:0)

下面的代码将 N(0,1)中的1000个样本和 N(7,2)中的500个样本存储在一个数组中,然后可以从中进行采样

import numpy as np
from scipy import stats

d = np.concatenate((stats.norm.rvs(0.0, 1.0, 1000), stats.norm.rvs(7.0, 2.0, 500)))
np.random.choice(d, 3)  # sample 3 observations

可以使用除Normal以外的混合物成分(例如stats.poisson),并且可以有任意数量的