使用 scipy 集成模块规范化函数时出现问题

A problem with normalizing a function using scipy integrate module

我想在 A 到 B 的范围内标准化一个函数(例如,scipy 的 chi2.pdf)。例如,chi2.pdf 被标准化为它的范围 0到无穷大,它在该区域上的积分是 1。为了做到这一点,我可以计算函数在 A 到 B 上的积分,然后将函数除以该积分。我可以用下面的代码实现它:

import numpy as np
from scipy.stats import chi2
from scipy.integrate import quad

A = 2
B = 4
df = 3

z = quad(chi2.pdf,A,B,args=(df,A)[0]

Quad 传递参数 df 作为自由度,A 作为 loc - 由于各种原因,我希望我的卡方函数移动 A。现在我有了 z,我可以定义一个新函数:

def normalized_chi_2(x,df,A,z):
    y = chi2.pdf(x,df,A)/z
    return(y)

再次快速检查集成:

integral_chi2 = quad(normalized_chi_2,A,B,args=(df,A,z)[0]
print(integral_chi2)
>0.9999999999999999

说明我的目的达到了。但是有两个函数,并且主要计算 Z 相对笨重,所以我想我可以定义一个新函数并计算 Z inside 那个函数。

def normalized_chi_1(x,df,A):
    z = quad(chi2.pdf,A,B,args=(df,A))[0]
    y = chi2.pdf(x,df,A) / z
    return(y)

现在,当我再次进行快速集成时:

integral_chi1 = quad(normalized_chi_1,A,B,args=(df,A))[0]
print(integral_chi1)
>0.42759329552910064

我没有得到 1,我得到的值等于原始的、未规范化的 chi2.pdf(上面的 z)的值。另一个问题是 normalized_chi_1(接受 df 和 A​​,并计算它自己的 z)非常非常慢。例如,方法 2,我在函数外部计算 z 并将其传递给下一个函数需要大约 0.07 秒,而方法 1,我在函数内部计算 z 需要大约 7.30 秒。慢一百倍。

quad 可能在幕后运行一个循环,每次调用您的函数时,它都会调用另一个 quad 来计算 z,所有这些都非常费力。为了对此进行测试,我添加了一个简单的 print 语句,其中包含原始函数的计数器。

count = 0
def normalized_chi_1(x,df,A):
    global count
    z = quad(chi2.pdf,A,B,args=(df,A))[0]
    print(f"calculating z {count}th time")
    count += 1
    y = chi2.pdf(x,df,A) / z
    return(y)

我得到的输出是

calculating z 0th time
...
calculating z 227th time
calculating z 228th time
calculating z 229th time
calculating z 230th time

因此,您正在计算 z 的积分约 230 次,这或多或少地解释了 100x 执行时间的增加。

如果你想要一个函数来计算 z 你可以简单地做

from functools import lru_cache

@lru_cache
def get_z(*ars,args):
    return quad(*ars,args)[0]

A = 2
B = 4
df = 3

def normalized_chi_1(x,df,A):  
    z = get_z(chi2.pdf,A,B,args=(df,A))
    y = chi2.pdf(x,df,A) / z
    return(y)

integral_chi1 = quad(normalized_chi_1,A,B,args=(df,A))[0]

这给了我正确的结果和 0.07 秒的 运行 时间,但我认为简单地在 main 中定义 z 更好。