如何提升Scipy伽马CDF统计计算的运行速度?
我有一个形状为(2500, 200)的二维NumPy数组,需要使用Scipy对数组的每一行计算Gamma分布的CDF统计量(具体是计算1.25对应的CDF值并转化为百分比)。为方便本地测试,我生成了随机浮点数数组,但当前代码运行速度不理想:在服务器上5次测试后,平均每秒仅能完成7-10行的计算,无法应对更大的数据集。
我已经尝试了一些优化手段,比如用列表推导替代循环、使用NumPy的ufunc(如max、divide、round),但仍有提升空间。请问还有哪些可行的优化方法?
原代码如下:
import numpy as np import scipy.stats as sci def gcdfstats(inputarr): if all(x==inputarr[0] for x in inputarr): value = 0 else: param = sci.gamma.fit(inputarr) x = np.linspace(0, int(np.round(np.max(inputarr), 0)), int(np.round(np.divide(np.max(inputarr), 0.01), 0))) cdf = sci.gamma.cdf(x, *param) value = np.round((sci.gamma.cdf(1.25, *param) * 100), 2) return value def getrow_stats(): # 生成(2500, 200)的随机浮点数组 inputarr = np.random.uniform(0.00, 8.00, (2500,200)) # 逐行计算Gamma CDF统计量 outstats = [gcdfstats(entry) for entry in inputarr] return outstats
优化方法
移除完全冗余的计算
原代码中生成x和计算cdf的代码段完全没有被使用,直接删除这两行可以立刻减少大量不必要的数组操作和CDF计算开销。修改后的gcdfstats函数:def gcdfstats(inputarr): if np.all(inputarr == inputarr[0]): value = 0 else: param = sci.gamma.fit(inputarr) value = np.round((sci.gamma.cdf(1.25, *param) * 100), 2) return value优化全相等判断逻辑
原代码用Python循环all(x==inputarr[0] for x in inputarr)检查数组所有元素是否相等,效率极低。换成NumPy的向量化判断np.all(inputarr == inputarr[0]),利用NumPy的C底层实现,速度可以提升数十倍。并行化处理每行计算
因为每行的Gamma拟合和CDF计算完全独立,适合用并行计算提升速度。可以使用joblib实现并行:from joblib import Parallel, delayed def getrow_stats(): inputarr = np.random.uniform(0.00, 8.00, (2500,200)) # 开启并行,n_jobs设为CPU核心数(-1表示用全部核心) outstats = Parallel(n_jobs=-1)(delayed(gcdfstats)(entry) for entry in inputarr) return outstats多核心CPU环境下,并行化能带来接近核心数倍数的速度提升。
调整Gamma拟合的参数估计方式
scipy.stats.gamma.fit默认使用最大似然估计(MLE),迭代次数较多。可以尝试两种替代方案:- 矩估计替代MLE:手动计算均值和方差来估计Gamma参数(形状参数
a = (mean/scale)**2,尺度参数scale = var/mean),速度远快于MLE,适合对精度要求不是极端苛刻的场景。 - 减少迭代次数:设置
maxiter参数限制拟合迭代次数,比如param = sci.gamma.fit(inputarr, maxiter=100),如果数据收敛快,能显著减少计算时间。
- 矩估计替代MLE:手动计算均值和方差来估计Gamma参数(形状参数
简化冗余的数值转换
原代码中int(np.round(np.max(inputarr), 0))这类操作可以简化为np.ceil(np.max(inputarr)).astype(int),减少函数调用层级,提升小幅度效率(此优化在第一步冗余代码删除后已非必需,但可用于其他类似场景)。
内容的提问来源于stack exchange,提问作者TornadoEric

