首页 > 解决方案 > 使用 scipy 集成模块规范化函数的问题

问题描述

我想在 A 到 B 的范围内对函数(例如 chi2.pdf 的 scipy)进行归一化。例如,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。

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 秒。慢了一百倍。

标签: pythonscipyscipy.stats

解决方案


quad可能在引擎盖下运行一个循环,每次调用你的函数时,它都会调用另一个函数quad来计算 z,所有这些都会变得相当繁琐。为了测试这一点,我在原始函数中添加了一个带有计数器的简单打印语句。

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 更好。


推荐阅读