在 scipy 中创建新的发行版

Posted

技术标签:

【中文标题】在 scipy 中创建新的发行版【英文标题】:Creating new distributions in scipy 【发布时间】:2012-05-27 13:23:12 【问题描述】:

我正在尝试根据我拥有的一些数据创建一个分布,然后从该分布中随机抽取。这是我所拥有的:

from scipy import stats
import numpy

def getDistribution(data):
    kernel = stats.gaussian_kde(data)
    class rv(stats.rv_continuous):
        def _cdf(self, x):
            return kernel.integrate_box_1d(-numpy.Inf, x)
    return rv()

if __name__ == "__main__":
    # pretend this is real data
    data = numpy.concatenate((numpy.random.normal(2,5,100), numpy.random.normal(25,5,100)))
    d = getDistribution(data)

    print d.rvs(size=100) # this usually fails

我认为这是在做我想做的事,但是当我尝试执行 d.rvs() 时,我经常会遇到错误(见下文),而 d.rvs(100) 永远不会工作。难道我做错了什么?有没有更简单或更好的方法来做到这一点?如果它是 scipy 中的一个错误,有什么办法可以解决它吗?

最后,是否有更多关于在某处创建自定义发行版的文档?我发现的最好的是 scipy.stats.rv_continuous 文档,它非常简陋,没有包含有用的示例。

回溯:

Traceback(最近一次调用最后一次):文件“testDistributions.py”,行 19,在 打印 d.rvs(size=100) 文件“/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/stats/distributions.py” , 第 696 行,在房车中 vals = self._rvs(*args) 文件“/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/stats/distributions.py” , 第 1193 行,在 _rvs Y = self._ppf(U,*args) 文件“/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/stats/distributions. py", 第 1212 行,在 _ppf 返回 self.vecfunc(q,*args) 文件“/usr/local/lib/python2.6/dist-packages/numpy-1.6.1-py2.6-linux-x86_64.egg/numpy/lib/function_base.py ", 第 1862 行,在 调用 theout = self.thefunc(*newargs) 文件“/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/stats/distributions.py” , 第 1158 行,在 _ppf_single_call 返回 optimize.brentq(self._ppf_to_solve, self.xa, self.xb, args=(q,)+args, xtol=self.xtol) 文件 “/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/optimize/zeros.py”, 第 366 行,在布伦特 r = _zeros._brentq(f,a,b,xtol,maxiter,args,full_output,disp) ValueError: f(a) and f(b) must have different sign

编辑

对于那些好奇的人,请按照以下答案中的建议,以下是有效的代码:

from scipy import stats
import numpy

def getDistribution(data):
    kernel = stats.gaussian_kde(data)
    class rv(stats.rv_continuous):
        def _rvs(self, *x, **y):
            # don't ask me why it's using self._size 
            # nor why I have to cast to int
            return kernel.resample(int(self._size)) 
        def _cdf(self, x):
            return kernel.integrate_box_1d(-numpy.Inf, x)
        def _pdf(self, x):
            return kernel.evaluate(x)
    return rv(name='kdedist', xa=-200, xb=200)

【问题讨论】:

那么当我们执行上述操作并调用randoms = getDistribution(Mydata) 然后调用randoms = randoms.rvs(size=1000) 时,它会在类中执行三个def 吗?即计算pdf,整合它等? 我确实让我的随机数遵循数据分布,但我想对其进行平滑处理,使其不完全遵循数据分布。我一直在手动调整kernel 的带宽来做到这一点。例如,我们如何指定 PDF 函数,然后使用 PDF 函数使用 Metropolis Hastings 创建随机数。 【参考方案1】:

特别是您的回溯:

rvs 使用 cdf 的倒数 ppf 创建随机数。由于您没有指定 ppf,它是通过寻根算法brentq 计算的。 brentq 使用下限和上限来搜索函数为零的值 at (查找 x 使得 cdf(x)=q, q 为分位数)。

限制的默认值xaxb 在您的示例中太小了。以下适用于我的 scipy 0.9.0, xa, xb 在创建函数实例时可以设置

def getDistribution(data):
    kernel = stats.gaussian_kde(data)
    class rv(stats.rv_continuous):
        def _cdf(self, x):
            return kernel.integrate_box_1d(-numpy.Inf, x)
    return rv(name='kdedist', xa=-200, xb=200)

目前有一个 scipy 的拉取请求来改进这一点,因此在下一个版本中 xaxb 将自动扩展以避免 f(a) and f(b) must have different signs 异常。

这方面的文档不多,最简单的方法是遵循一些示例(并在邮件列表中询问)。

编辑:添加

pdf:既然你也有 gaussian_kde 给出的密度函数,我会添加_pdf 方法,这将使一些计算更高效。

edit2:添加

rvs:如果你对生成随机数感兴趣,那么 gaussian_kde 有一个 resample 方法。可以通过从数据中采样并添加高斯噪声来生成随机样本。因此,这将比使用 ppf 方法的通用 rvs 更快。我会写一个 ._rvs 方法,它只调用 gaussian_kde 的 resample 方法。

预计算 ppf:我不知道预计算 ppf 的任何一般方法。但是,我想到的方法(但到目前为止从未尝试过)是在许多点上预先计算 ppf,然后使用线性插值来近似 ppf 函数。

edit3:关于_rvs在评论中回答Srivatsan的问题

_rvs 是由公共方法rvs 调用的特定于分发的方法。 rvs 是一个泛型方法,它进行一些参数检查,添加位置和比例,并设置属性self._size 这是所请求的随机变量数组的大小,然后调用分布特定方法._rvs 或者它是泛型的对方。 ._rvs 中的额外参数是形状参数,但由于在这种情况下没有,*x**y 是多余且未使用的。

我不知道size.rvs 方法的形状在多变量情况下的效果如何。这些分布是为单变量分布而设计的,可能无法完全适用于多变量情况,或者可能需要一些重塑。

【讨论】:

太棒了,谢谢,这很有帮助。是否有某种方法可以使用与 scipy 相同的方法从 cdf 预先计算 ppf,从而提高效率?我注意到每次 rv() 调用都会调用 _cdf() 很多次。 我在 rvs 和 ppf 上添加了一些 cmets。还有一条评论:如果您有带有肥尾的数据,那么 gaussian_kde 的尾巴就不会很好。当我考虑编写一个类似的分布子类时,我会尝试使用帕累托尾巴。我在一个论坛上读到了关于这个的评论,matlab 有一个帕累托尾分布。 @user333700 我想知道问题中的编辑发布的答案中的 def _rvs(self, *x, **y) 有什么作用??

以上是关于在 scipy 中创建新的发行版的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中集成 beta 发行版

scikit-learn 的 Pip 安装:未找到匹配的发行版。 [复制]

在 SciPy 中拟合分布时如何检查收敛性

国内的用户一般用啥Linux发行版?

屏幕支持比较好的linux发行版么

如何在Linux发行版下禁用IPv6