11

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

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 文档,它非常简陋,不包含任何有用的示例。

追溯:

回溯(最后一次调用):文件“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 行,在 rvs 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 行,在 brentq 中 r = _zeros._brentq(f,a,b,xtol,maxiter ,args,full_output,disp) ValueError: f(a) 和 f(b) 必须有不同的符号

编辑

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

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)
4

1 回答 1

7

特别是您的回溯:

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

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

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方法,这将使一些计算更有效。

编辑2:添加

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

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

edit3:即将_rvs在评论中回答 Srivatsan 的问题

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

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

于 2012-05-21T04:10:40.723 回答