当前位置: 首页 > 知识库问答 >
问题:

在Python 3中使用Scipy将多个洛伦兹函数拟合到布里渊光谱

韶兴德
2023-03-14

我正在尝试使用scipy拟合布里渊光谱(具有多个峰)。优化曲线拟合。我有多个光谱和几个峰,我试图用洛伦兹函数拟合它们(每个峰一个洛伦兹函数)。我正在尝试自动化批量分析过程(即,使用scipy的峰值查找算法获得峰值位置、峰值宽度和峰值高度,并将其用作拟合的初始猜测)。我现在正在研究一个光谱,看看总体思路是否可行,然后我会将其扩展为自动光谱,并处理我所有的光谱。到目前为止,我已经做到了:

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
from scipy.optimize import curve_fit

#import y data from linked google sheet 
y_data = np.loadtxt( 'y_peo.tsv' )
#define x data 
x_data = np.arange( len( y_data ) )

#find peak properties (peak position, amplitude, full width half maximum ) to use as 
#initial guesses for the curve_fit function 
pk, properties = find_peaks(y_data, height = 3, width = 3, prominence=0.1 ) #pk returns peaks position, properties returns 
#other properties associated with the peaks
I = properties ['peak_heights'] #amplitude
fwhm = (properties['widths']) #full width half maximum 

#define sum of lorentzians
def func(x, *params): 
    y = np.zeros_like(x)
    for i in range(0, len(params), 3):
        x_0 = params[i]
        I = params[i+1]
        y_0 = params[i+2]
        y = y + (I*y_0**2)/(y_0**2 +(x-x_0)**2) 
    return y

#initial guess list, to be populated with parameters found above (pk, I, fwhm)
guess = [] 

for i in range(len(pk)): 
    guess.append(pk[i])
    guess.append(I[i])
    guess.append(fwhm[i]) 

#convert list to array
guess=np.array(guess)

#fit
popt, pcov = curve_fit(func, x_data, y_data, p0=guess, method = 'lm',  maxfev=1000000)
print(popt)
fit = func(x_data, *popt)

#plotting
fig= plt.figure(figsize=(10,5))
ax= fig.add_subplot(1,1,1)
ax.plot(pk, y_data[pk], 'o', ms=5)
ax.plot(x_data, y_data, 'ok', ms=1)
ax.plot(x_data, fit , 'r--', lw=0.5)

其中y_peo是因变量(以下是作为google工作表文件的值:https://docs.google.com/spreadsheets/d/1UB2lqs0Jmhthed7B0U9rznqcbpEMRmRX99c-iOBHLeE/edit?usp=sharing)像素是自变量(代码中创建的任意值数组)。这就是我得到的:光谱拟合的结果。知道为什么最后一个三元组没有按预期安装吗?我检查过了,所有的峰都被scipy正确地找到了。信号find_peaks函数-因此我认为问题在于scipy。优化曲线拟合,因为我必须增加maxfev的数量以适应“工作”。你知道如何以更聪明的方式解决这个问题吗?

提前感谢,,

朱塞佩。

共有1个答案

羊毅庵
2023-03-14

通过一些小的修改,OP的代码运行得很好。现在看来:

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
from scipy.signal import find_peaks

def lorentzian( x, x0, a, gam ):
    return a * gam**2 / ( gam**2 + ( x - x0 )**2 )

def multi_lorentz( x, params ):
    off = params[0]
    paramsRest = params[1:]
    assert not ( len( paramsRest ) % 3 )
    return off + sum( [ lorentzian( x, *paramsRest[ i : i+3 ] ) for i in range( 0, len( paramsRest ), 3 ) ] )

def res_multi_lorentz( params, xData, yData ):
    diff = [ multi_lorentz( x, params ) - y for x, y in zip( xData, yData ) ]
    return diff

y0 = np.loadtxt( 'y_peo.tsv' )
yData = np.loadtxt( 'y_peo.tsv' )
xData = np.arange( len( yData ) )

yGround = min( yData )
yData = yData - yGround
yAmp = max( yData )
yData = yData / yAmp 

#initial properties of peaks 
pk, properties = find_peaks( yData, height = .05, width = 3 )#, prominence=0.1 )
#extract peak heights and fwhm 
I = properties [ 'peak_heights' ]
fwhm = properties[ 'widths' ]

guess = [0]
for i in range( len( pk ) ): 
    guess.append( pk[i] )
    guess.append( I[i] )
    guess.append( fwhm[i] ) 

guess=np.array( guess )

popt, pcov = leastsq( res_multi_lorentz, x0=guess, args=( xData, yData ) )
print( popt )


testData = [ multi_lorentz( x, popt ) for x in xData ]
fitData = [ yGround + yAmp * multi_lorentz( x, popt ) for x in xData ]

fig= plt.figure( figsize=( 10, 5 ) )
ax= fig.add_subplot( 2, 1, 1 )
bx= fig.add_subplot( 2, 1, 2 )
ax.plot( pk, yData[pk], 'o', ms=5 )
ax.plot( xData, yData, 'ok', ms=1 )
ax.plot( xData, testData , 'r--', lw=0.5 )
bx.plot( xData, y0, ls='', marker='o', markersize=1 )
bx.plot( xData, fitData )
plt.show()

[9.39456104e-03 6.55864388e+01 5.73522507e-02 5.46727721e+00
 1.21329586e+02 2.97187567e-01 2.12738107e+00 1.76823266e+02
 1.57244131e-01 4.55424037e+00 3.51556692e+02 3.08959955e-01
 4.39114581e+00 4.02954496e+02 9.02677035e-01 2.27647259e+00
 4.53994668e+02 3.74379310e-01 4.02432791e+00 6.15694190e+02
 4.51943494e-01 4.06522919e+00 6.63307635e+02 1.05793098e+00
 2.32168786e+00 7.10644233e+02 4.33194434e-01 4.17050014e+00
 8.61276198e+02 1.79240633e-01 4.26617114e+00 9.06211127e+02
 5.03070470e-01 2.10563379e+00 9.50973864e+02 1.78487912e-01
 4.01254815e+00]

 类似资料:
  • 我试图用一个以上的吸收峰(穆斯堡尔谱)拟合洛伦兹函数,但曲线拟合函数不能正常工作,只能拟合几个峰。我怎样才能适应它? 图:尝试调整多重洛伦兹 下面我展示我的代码。请帮帮我。 我的数据--

  • 问题内容: 在python中,我有一个具有许多参数的函数。我想将此函数适合数据集,但只使用一个参数,其余的参数我要自己提供。这是一个例子: 在这种情况下,我希望只对它进行拟合,并且参数采用循环变量的值。如何才能做到这一点? 问题答案: 您可以包装一个lambda,如下所示: Lambda是一个匿名函数,在Python中只能用于简单的单行函数。基本上,通常在不需要为函数分配名称时通常用于减少代码量。

  • 问题内容: 我有一组近似于2D曲线的点。我想将Python与numpy和scipy配合使用,以找到近似适合这些点的三次贝塞尔曲线路径,在该路径中,我指定两个端点的确切坐标,并返回其他两个控制点的坐标。 我最初以为可以做我想做的事,但是似乎迫使曲线穿过每个数据点(因为我想您希望进行插值)。我以为我走错了路。 我的问题与此相似:如何将贝塞尔曲线拟合到一组数据?,除了他们说他们不想使用numpy。我的偏

  • 问题内容: 有没有更有效的方法来对预先指定的bin中的数组取平均值?例如,我有一个数字数组以及一个与该数组中bin的开始和结束位置相对应的数组,我只想取这些bin中的均值?我下面有执行此操作的代码,但我想知道如何减少和改进它。谢谢。 问题答案: 它可能更快更容易使用: 替代方法是使用: 自己尝试哪个更快… :)

  • 问题内容: 我有一个包含多个包含整数(a1,a2,a3等)的字段的postgresql表。 我想一次跨多个列运行汇总函数(均值,标准差等)。(其中一些可能具有合理数量的空值,因此我不想只生成列平均值然后再对它们求平均值)。 我可以得到一组整数 但是我然后无法获取聚合函数以将此作为输入。 谁能给我任何有关如何使它工作的提示? 问题答案: 使用子查询,您可以使用所有行: 您还可以对行进行分组,例如:

  • 我想将光栅数据聚合到自定义形状文件中的每个多边形。 在这种情况下,我想获得撒哈拉以南非洲次国家区域城市化的平均程度。 我的sf如下所示: 或绘制: 另一方面,光栅数据采用以下形式: 这些比整个星球所需的要细得多。为了加速计算,我首先聚合光栅,然后将其转换为shapefile,剩余的每个光栅像素都转换为shapefile中的点几何形状。然后,这个shapefile可以聚合到我的区域边界。诚然,这不是