尝试用任意曲线拟合Python中的多个Moyal函数

尝试用任意曲线拟合Python中的多个Moyal函数,python,scipy,curve-fitting,Python,Scipy,Curve Fitting,我一直在尝试拟合具有多个Landau峰值的分布(因此我使用scipy.stats moyal)。我尝试过使用scipy曲线拟合,但总是出错。该函数将有多条曲线,由“nMIPs”指定。函数如下所示: x = np.linspace(0, len(ADC[0])-1, len(ADC[0])) def multMoyal(x, nMIPs, *args): moy = 0 for i in range(int(nMIPs)): r = 3*i moy

我一直在尝试拟合具有多个Landau峰值的分布(因此我使用scipy.stats moyal)。我尝试过使用scipy曲线拟合,但总是出错。该函数将有多条曲线,由“nMIPs”指定。函数如下所示:

x = np.linspace(0, len(ADC[0])-1, len(ADC[0]))

def multMoyal(x, nMIPs, *args):
    moy = 0
    for i in range(int(nMIPs)):
        r = 3*i
        moy = moy + args[r]*moyal.pdf(x, args[r+1], args[r+2])
    return moy
    param, param_cov = curve_fit(multMoyal(x=x, nMIPs=nMIPs), x, y[0], maxfev=5000)
fitStart = []
startVal = []
weightGuess = []
guess = []
boundsInit = []
boundsEnd = []
for q in range(int(len(ADC))):
    MIPguess = exMax[q][np.where(exMax[q] > exMin[q][0])[0][0]]-10
    starter = int(exMin[q][0]+abs(exMin[q][0]-MIPguess)/2)
    fitStart.append(starter)
    startVal.append(MIPguess)
    weightGuess.append(y[q][MIPguess])
    guess.append([nMIPs, weightGuess[q], startVal[q], 0.2*startVal[q]])
    boundsInit.append([nMIPs, 0, fitStart[q], 0])
    boundsEnd.append([nMIPs+1, np.inf, np.inf, 0.3*startVal[q]])
    for i in range(1, nMIPs):
        l = i+1
        guess[q].extend([weightGuess[q]/l, l*startVal[q], 0.2*startVal[q]])
        boundsInit[q].extend([0, fitStart[q]*l*0.75, 0])
        boundsEnd[q].extend([np.inf, np.inf, np.inf])
其中ADC是光谱。这是粒子碰撞数据;它本质上是带电粒子通过探测器中的一块瓷砖,每个moyal曲线都表示带电粒子的数量(通常在2-5之间,取决于实验参数和瓷砖的位置)

当我这样调用该函数时,该函数运行良好(nMIPs=2):

但当我尝试在曲线拟合中输入它时,我不断地得到错误(“IndexError:元组索引超出范围”)。曲线拟合线如下所示:

x = np.linspace(0, len(ADC[0])-1, len(ADC[0]))

def multMoyal(x, nMIPs, *args):
    moy = 0
    for i in range(int(nMIPs)):
        r = 3*i
        moy = moy + args[r]*moyal.pdf(x, args[r+1], args[r+2])
    return moy
    param, param_cov = curve_fit(multMoyal(x=x, nMIPs=nMIPs), x, y[0], maxfev=5000)
fitStart = []
startVal = []
weightGuess = []
guess = []
boundsInit = []
boundsEnd = []
for q in range(int(len(ADC))):
    MIPguess = exMax[q][np.where(exMax[q] > exMin[q][0])[0][0]]-10
    starter = int(exMin[q][0]+abs(exMin[q][0]-MIPguess)/2)
    fitStart.append(starter)
    startVal.append(MIPguess)
    weightGuess.append(y[q][MIPguess])
    guess.append([nMIPs, weightGuess[q], startVal[q], 0.2*startVal[q]])
    boundsInit.append([nMIPs, 0, fitStart[q], 0])
    boundsEnd.append([nMIPs+1, np.inf, np.inf, 0.3*startVal[q]])
    for i in range(1, nMIPs):
        l = i+1
        guess[q].extend([weightGuess[q]/l, l*startVal[q], 0.2*startVal[q]])
        boundsInit[q].extend([0, fitStart[q]*l*0.75, 0])
        boundsEnd[q].extend([np.inf, np.inf, np.inf])
其中y[0]是ADC谱之一,但在x上平滑以去除噪声

那么,我如何让它拥有任意数量的曲线,并且仍然在scipy的曲线拟合中工作呢?或者,除了曲线拟合之外,还有什么我应该使用的吗

相关:如果我让它工作,我如何设置某些参数,但让其他参数不确定?(我想为第一条moyal曲线的峰值和权重设置猜测值,但其他内容留空。)


谢谢

我找到了一个解决方案,但我不知道这是否是正确的方法(但确实有效)。我将函数更新为:

def multMoyal(x, nMIPs, *args):
    moy = 0
    for i in range(int(nMIPs)):
        l = 3*i
        moy = moy + args[l]*moyal.pdf(x, args[l+1], args[l+2])
    return moy
然后我添加了边界和猜测。对于scipy.optimize.curve_fit的初始猜测值都是1,所以您输入的几乎任何内容都会比这更好。我选择了一些似乎适合这个问题的范围,并从那里放手。为了设置单个参数(或任意多个参数),我使用了边界。将边界设置为+-np.inf将使该参数保持自由

例如,我使用了一个从nMIPs到nMIPs+1的nMIPs界限,这实际上是将它设置为nMIPs(当然是猜测nMIPs)。情况如下:

x = np.linspace(0, len(ADC[0])-1, len(ADC[0]))

def multMoyal(x, nMIPs, *args):
    moy = 0
    for i in range(int(nMIPs)):
        r = 3*i
        moy = moy + args[r]*moyal.pdf(x, args[r+1], args[r+2])
    return moy
    param, param_cov = curve_fit(multMoyal(x=x, nMIPs=nMIPs), x, y[0], maxfev=5000)
fitStart = []
startVal = []
weightGuess = []
guess = []
boundsInit = []
boundsEnd = []
for q in range(int(len(ADC))):
    MIPguess = exMax[q][np.where(exMax[q] > exMin[q][0])[0][0]]-10
    starter = int(exMin[q][0]+abs(exMin[q][0]-MIPguess)/2)
    fitStart.append(starter)
    startVal.append(MIPguess)
    weightGuess.append(y[q][MIPguess])
    guess.append([nMIPs, weightGuess[q], startVal[q], 0.2*startVal[q]])
    boundsInit.append([nMIPs, 0, fitStart[q], 0])
    boundsEnd.append([nMIPs+1, np.inf, np.inf, 0.3*startVal[q]])
    for i in range(1, nMIPs):
        l = i+1
        guess[q].extend([weightGuess[q]/l, l*startVal[q], 0.2*startVal[q]])
        boundsInit[q].extend([0, fitStart[q]*l*0.75, 0])
        boundsEnd[q].extend([np.inf, np.inf, np.inf])
如果我只想设置nMIPs,我可以把所有的起始猜测都放在1和边界上(除了nMIPs)到+-np.inf。然后按照以下步骤进行装配:

param, param_cov = curve_fit(
            multMoyal, x[fitStart[q]:], ADC[q][fitStart[q]:], maxfev=5000, p0=guess[q],
            bounds=(boundsInit[q], boundsEnd[q]))

它是有效的,但我非常怀疑它是否是最严格的代码。我仍然觉得我错过了一些更简单的方法,但现在就可以了。我很想知道专家们可能会对此给出什么答案,但我把这个作为一个答案,因为还没有答案,这至少解决了问题。

因为没有人对此发表评论,而且解决方案可行,所以我将接受这个答案来结束这个问题。虽然这有点像在和我自己说话。