【问题标题】:Creating a mixture of probability distributions for sampling为抽样创建混合概率分布
【发布时间】:2018-05-25 08:50:51
【问题描述】:

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

我有这样一个分布用于显示,使用类似的东西:

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

如果然后绘制如下:

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

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

SciPy/NumPy 是否提供了一些轻松完成此任务的方法?

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

【问题讨论】:

  • This 可能会帮助您入门。

标签: python numpy scipy probability-density


【解决方案1】:

从混合分布中采样(其中 PDF 添加了一些系数 c_1、c_2、...概率 c_k。

使用numpy.random.choice 可以有效地完成后一个混合步骤。这是一个混合了三个分布的示例。分布在distributions 中列出,它们的系数在coefficients 中列出。有脂肪正态分布、均匀分布和窄正态分布,系数分别为 0.5、0.2、0.3。在根据给定系数生成random_idx 之后,混合发生在data[np.arange(sample_size), 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()

【讨论】:

    【解决方案2】:

    按照@PaulPanzer 在 cmets 中的指针,我创建了以下子类,以便从 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)
    

    【讨论】:

    • 我以为你想“对不同的“子”分布进行不同的加权”;在这里,它们的重量都相同。
    • @CrazyIvan,没错。我最终希望它们能够以不同的方式加权。目前我写的内容已经足够了,但最终我将不得不将我的随机选择(以及pdf 的缩放比例)更改为更接近您在答案中所做的内容。
    • @Jenny Shoars,我遇到了和你类似的问题。但是,我有兴趣对输入子分布进行不同的加权。你能给我一个提示如何实现(尤其是for_pdf)吗?顺便说一句,为了避免出现错误消息,我不得不将代码 super().__init__(*args, **kwargs) 中的以下行替换为 super(MixtureModel, self).__init__(*args, **kwargs)
    【解决方案3】:

    下面的代码将来自 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
    

    可以使用除法线以外的混合组件(例如,stats.poisson),并且可以有任意数量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多