【问题标题】:scipy.integrate Pseudo-Voigt function, integral becomes 0scipy.integrate Pseudo-Voigt 函数,积分变为 0
【发布时间】:2016-10-06 00:49:20
【问题描述】:

我正在编写一个脚本,使用 Scipy、Numpy 和 Matplotlib 在 Python 中将峰形拟合到光谱数据。它可以一次拟合多个峰。峰值轮廓(目前)是 Pseudo-Voigt,它是高斯(又名正态)和洛伦兹(又名 Cauchy)分布的线性组合。

我有一个选项开关,可以让软件优化高斯和洛伦兹的贡献,也可以将其设置为固定值(其中 0 = 纯高斯,1 = 纯洛伦兹)。正常工作,绘制拟合的峰值看起来符合预期。当我尝试使用scipy.integrate 计算峰值的积分时,问题就开始了。

到目前为止,我尝试了 scipy.integrate.quad、scipy.integrate.quadrature、scipy.integrate.fixed_quad 和 scipy.integrate.romberg。当峰是纯高斯峰时,积分变为类似于1.73476E-34(并不总是相同的数字),即使峰面积明显大于相邻峰的面积,这些峰不是纯高斯峰,但返回大约 10 的有限积分到 1000。以下是相关部分的样子:

# Function defining the peak functions for plotting and integration
# WavNr: Wave number, the x-axis over which shall be integrated
# Pos: Peak center position
# Amp: Amplitude of the peak
# GammaL: Gamma parameter of the Lorentzian distribution
# FracL: Fraction of Lorentzian distribution
def PseudoVoigtFunction(WavNr, Pos, Amp, GammaL, FracL):
    SigmaG = GammaL / np.sqrt(2*np.log(2)) # Calculate the sigma parameter  for the Gaussian distribution from GammaL (coupled in Pseudo-Voigt)
    LorentzPart = Amp * (GammaL**2 / ((WavNr - Pos)**2 + GammaL**2)) # Lorentzian distribution
    GaussPart = Amp * np.exp( -((WavNr - Pos)/SigmaG)**2) # Gaussian distribution
    Fit = FracL * LorentzPart + (1 - FracL) * GaussPart # Linear combination of the two parts (or distributions)
    return Fit

这是绘图函数通过以下方式调用的:

Fit = PseudoVoigtFunction(WavNr, Pos, Amp, GammaL, FracL)

效果很好。积分器也通过以下方式调用它:

PeakArea, PeakAreaError = integrate.quad(PseudoVoigtFunction, -np.inf, np.inf, args=(Pos, Amp, GammaL, FracL))

或 scipy.integrate 提供的任何其他变体,都具有相同的结果,如果 FracL = 0,则 PeakArea =(几乎)0。

我确定问题是我太愚蠢了,无法弄清楚 scipy.integrate 如何使用比我找不到示例的稍微复杂的函数工作。希望有人看到我没有看到的明显错误。两天的搜索 stackoverflow 和 Scipy Docs 以及重新排列和完全重写我的代码让我一无所获。我怀疑 scipy.integrate 中的参数在某种程度上与问题有关,但据我所知,它们似乎排列正确。

提前致谢, 操作系统

【问题讨论】:

    标签: python numpy scipy data-fitting integrate


    【解决方案1】:

    我相信您知道,间隔 (-inf, inf) 相当大。 :) 高斯衰减非常快,因此除了峰值附近的区间外,高斯在数值上与 0 无法区分。我怀疑 quad 根本看不到您的峰值。

    一个简单的解决方法是将积分分成两个区间,(-inf, pos) 和 (pos, inf)。 (你的函数是关于Pos 对称的,所以你真的只需要积分的两倍(-inf,pos)。)

    这是一个例子。我不知道这些参数值是否接近您使用的典型值,但它们说明了这一点。

    In [259]: pos = 1500.0
    
    In [260]: amp = 4.0
    
    In [261]: gammal = 0.5
    
    In [262]: fracl = 0  # Pure Gaussian
    

    quad认为积分为0:

    In [263]: quad(PseudoVoigtFunction, -np.inf, np.inf, args=(pos, amp, gammal, fracl))
    Out[263]: (0.0, 0.0)
    

    相反,对 (-inf, pos) 和 (pos, inf) 进行积分:

    In [264]: quad(PseudoVoigtFunction, -np.inf, pos, args=(pos, amp, gammal, fracl))
    Out[264]: (1.5053836955785238, 3.616268258191726e-11)
    
    In [265]: quad(PseudoVoigtFunction, pos, np.inf, args=(pos, amp, gammal, fracl))
    Out[265]: (1.5053836955785238, 3.616268258191726e-11)
    

    所以 (-inf, inf) 上的积分约为 3.010767。

    【讨论】:

    • 感谢沃伦,这解决了问题!但我仍然不明白为什么。我确实注意到,当我使用一个非常窄的范围时,比如 -GammaL 到 GammaL,我得到的结果对该范围来说似乎是合理的,但是在该范围的 10 倍时,积分已经下降到零。从理论上讲,情况不应该如此;随着函数接近 0,积分不应该再变得更大,但它绝对不应该变小甚至消失。好吧,我想这与四边形算法的工作方式有关,到目前为止,我一直懒得去挖掘源代码。 (待续)
    • (续)正如您所说,它可能只是在浩瀚的无限空间中看不到高斯峰。奇怪的是,找到和整合洛伦兹峰似乎没有问题。将两半积分往返峰值中心的技巧可能应该添加到 scipy.integrate 参考指南中。无论如何,非常感谢,这很可能使我的项目免于死亡和我发疯!
    猜你喜欢
    • 1970-01-01
    • 2016-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 1970-01-01
    • 2018-08-27
    相关资源
    最近更新 更多