如何加快Poisson pmf功能?

时间:2014-02-28 00:15:03

标签: python scipy distribution

我的用例是在所有小于10的点上评估Poisson pmf,并且我会多次调用这样的函数,并且使用不同的lambda。 lambda不是提前知道的,所以我无法对lambdas进行矢量化。

我从某个地方听说过使用_pmf的秘密技巧。这样做的缺点是什么?但是,它有点慢,有没有办法改进它而不从头开始重写C中的pmf?

%timeit scipy.stats.poisson.pmf(np.arange(0,10),3.3)
%timeit scipy.stats.poisson._pmf(np.arange(0,10),3.3)
a = np.arange(0,10)
%timeit scipy.stats.poisson._pmf(a,3.3)

10000 loops, best of 3: 94.5 µs per loop
100000 loops, best of 3: 15.2 µs per loop
100000 loops, best of 3: 13.7 µs per loop

更新

好吧,我只是懒得用cython写的。我原本预计所有离散分布都有一个更快的解决方案,可以按顺序(迭代)对连续x进行评估。例如。 P(X=3) = P(X=2) * lambda / 3 if X ~ Pois(lambda)

相关:Is the build-in probability density functions of `scipy.stat.distributions` slower than a user provided one?

我现在对Scipy和Python的信心不足。库函数不像我预期的那样先进。

3 个答案:

答案 0 :(得分:3)

大多数scipy.stats发行版都支持矢量化评估:

>>> poisson.pmf(1, [5, 6, 7, 8])
array([ 0.03368973,  0.01487251,  0.00638317,  0.0026837 ])

这可能或者可能不够快,但您可以尝试将pmf次呼叫带出循环。

pmf_pmf之间的区别:真正的工作是在强调的函数(_pmf_cdf等)中完成的,而公共函数({{1} },pmf)确保只有有效的参数才能使它成为cdf(如果参数无效,则_pmf的输出不能保证有意义,因此请自行承担风险)。

_pmf

更多详情:https://github.com/scipy/scipy/blob/master/scipy/stats/_distn_infrastructure.py#L2721

答案 1 :(得分:2)

  1. 尝试在cython中实现pmf。如果你的scipy是像Anaconda或Enthought这样的软件包的一部分你可能已经安装了cython。 http://cython.org/

  2. 尝试用pypy运行它。 http://pypy.org/

  3. 在大型AWS服务器(或类似服务器)上租用时间。

答案 2 :(得分:0)

我发现scipy.stats.poisson类与简单的python实现相比非常慢。

没有cython,载体或其他任何东西。

import math


def poisson_pmf(x, mu):
    return mu**x / math.factorial(x) * math.exp(-mu)


def poisson_cdf(k, mu):
    p_total = 0.0
    for x in range(k + 1):
        p_total += poisson_pmf(x, mu)
    return p_total

如果您检查scipy.stats.poisson中的source code(甚至是带下划线的前缀版本),则很清楚为什么!

上述实现现在比在C语言中(与gcc -O3 v9.3编译时)完全相同,仅慢 10倍。 scipy版本至少要慢10倍

#include <math.h>

unsigned long factorial(unsigned n) {
  unsigned long fact = 1;
  for (unsigned k = 2; k <= n; ++k)
    fact *= k;
  return fact;
}

double poisson_pmf(unsigned x, double mu) {
  return pow(mu, x) / factorial(x) * exp(-mu);
}

double poisson_cdf(unsigned k, double mu) {
  double p_total = 0.0;
  for (unsigned x = 0; x <= k; ++x)
    p_total += poisson_pmf(x, mu);
  return p_total;
}