【问题标题】:Why norm.cdf is faster than norm.pdf in scipy?为什么 norm.cdf 在 scipy 中比 norm.pdf 快?
【发布时间】:2020-09-11 16:08:04
【问题描述】:

我现在使用scipy 进行一些norm.pdfnorm.cdf 计算。我想知道为什么cdfpdf 快?

我知道norm.cdf 有一些渐近方法,而在scipy 中似乎使用了norm.pdf 的集成。这就是为什么我无法想象cdfpdf 快。如果是集成的话,cdf 应该比pdf 慢很多(也许并行计算能帮上大忙?);如果应用渐近方法,我仍然认为cdf 可能比pdf 慢一点。

下面是一些简单的示例:

import scipy.stats as st
from datetime import datetime
import numpy as np
num_iter = 100000
x_lower = 0.25
x_upper = 0.75

time_start = datetime.now()
for x in np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)):
    y = st.norm.pdf(x)
time_end = datetime.now()
print(time_end - time_start)

time_start = datetime.now()
for x in np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)):
    y = st.norm.cdf(x)
time_end = datetime.now()
print(time_end - time_start)

以下是运行结果:

0:00:05.736985
0:00:04.896390

【问题讨论】:

    标签: python scipy statistics normal-distribution


    【解决方案1】:

    快速查看源代码表明scipy.stats.norm.pdf 只是使用 NumPy 返回 pdf 的 x 的值:

    def _norm_pdf(x):
    return np.exp(-x**2/2.0) / _norm_pdf_C
    

    _norm_pdf_C = np.sqrt(2*np.pi).

    对于 cdf,由于我们谈论的是正态分布,因此使用了特殊函数(对于它们与正态分布之间的关系,see here)。

    SciPy 实现了特殊功能directly in C。特别是,累积分布函数是根据ndtr.c 计算的。因此,即使 NumPy 真的很快,我认为在这种情况下 C 仍然更快。

    编辑

    抱歉,我刚刚意识到我的回答并不能完全回答您的问题。

    首先,NumPy 还在 C 中实现数学运算。 因此,要了解为什么会出现时代差异,就应该了解 C 中发生了什么。

    • 如果你看这个question,似乎数值和硬件架构会影响时间。

    所以我再次检查了 cdf 的 C 实现,我发现评估特殊函数的多项式的常数和系数不是计算的,而是存储在数组和变量中的!例如,1/sqrt(2) 包含在 NPY_SQRT1_2 中。这可能是 cdf 比 pdf 快的原因!

    因此我尝试计算已初始化常量的 pdf:

    import scipy.stats as st
    from datetime import datetime
    import numpy as np
    num_iter = 100000
    x_lower = 0.25
    x_upper = 0.75
    
    const = np.sqrt(2*np.pi)
    time_start = datetime.now()
    for x in np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)):
        # y = st.norm.pdf(x)
        y = np.exp((x**2 / 2)) / const
    time_end = datetime.now()
    print(time_end - time_start)
    
    time_start = datetime.now()
    for x in np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)):
        y = st.norm.cdf(x)
    time_end = datetime.now()
    

    这段代码给了我:

    0:00:00.202531
    0:00:07.703083
    

    请注意,norm.pdf 也预先初始化了 pdf 的分母,但在 for 循环中您每次都调用该方法,从而减慢速度。

    P.S.:如果您尝试摆脱原始代码中的循环并仅使用 x = np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)),则 cdf 再次更快。原因可能是 cdf 是用多项式近似计算的。但我没有找到有关 C 如何准确处理指数以进行比较的信息。

    【讨论】:

    • 非常感谢您的详细回答,这很有道理。
    猜你喜欢
    • 2019-07-17
    • 1970-01-01
    • 2021-06-09
    • 2011-01-24
    • 2019-11-02
    • 2017-01-11
    • 2019-10-19
    • 2015-07-24
    相关资源
    最近更新 更多