You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用lmfit实现掩码数组拟合(原使用curve_fit的sigma参数)

问题描述

我之前用scipy.optimize.curve_fit结合sigma=weight参数实现了掩码数组拟合,现在切换到lmfit后遇到了实现难题。观测数据与模板均为500-550nm范围内的波长(x)-通量(y)数据,观测数据是两种不同参数光谱的混合。lambda_input为拟合所用的波长列表,拟合仅在这些波长±线宽(lw)的范围内执行。请问如何将观测数据和模板的波长掩码到lambda_input指定范围以完成拟合?

原代码

import pandas as pd
import numpy as np
from scipy import interpolate
from scipy.optimize import curve_fit
import lmfit

i_run = pd.read_csv("run.txt",delimiter='\t')
i_run.columns = ["wave","flux"]
run_w = np.array(i_run['wave'])
run_f = np.array(i_run['flux']) # observation data

lambda_input = pd.read_excel("../lambda/weights.xlsx") #fitting wavelengths

v1 = 5 # conversion factors
v2 = 6
lw = 0.01 # linewidths (+/- fitting wavelength)

weight = np.zeros(len(run_w)) 
for w in range (0,187):
    n = lambda_input.iat[w,1]
    weight += np.select([np.logical_and(run_w >= ((n *(1 + (v1)))-lw),run_w <= ((n *(1 + (v1)))+lw)),run_w>0],[1,0])
for w in range (0,187):
    n = lambda_input.iat[w,1]
    weight += np.select([np.logical_and(run_w >= ((n *(1 + (v2)))-lw),run_w <= ((n *(1 + (v2)))+lw)),run_w>0],[1,0])
weight = np.select([weight>0,weight==0],[1,1e6])

p1 = 750
p2 = 800
p3 = 1.0
p4 = 20.0
p5 = 30.0
p6 = 0.5
p7 = 0.5
p8 = 100
p9 = -100
p10 = 4.0
p11 = 4.0
p12 = 3.0
p13 = 3.0

def spec(run_w,t1,t2,r,v1,v2,m1,m2,r1,r2,l1,l2,mi1,mi2):
    # 原代码省略了混合光谱生成逻辑,此处为示意
    return spectrum

popt, pcov = curve_fit(spec, xdata=run_w,sigma=weight,ydata=run_f, p0=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13])

model = lmfit.Model(spec) #fitting: lmfit
p = model.make_params(t1=p1,t2=p2,r=p3,v1=p4,v2=p5,m1=p6,m2=p7,r1=p8,r2=p9,l1=p10,l2=p11,mi1=p12,mi2=p13)
fit = model.fit(data=run_f, params=p, run_w=run_w, method='nelder', nan_policy='omit')
lmfit.report_fit(fit)
fit.plot()
解决方案

在lmfit中实现指定波长范围的拟合,有两种更简洁的方案,无需依赖极端sigma值掩码,直接通过布尔筛选或权重参数实现:

1. 生成布尔掩码筛选目标波长范围

替换原weight生成逻辑,直接创建布尔数组标记需要保留的波长点,再筛选数据:

# 初始化掩码为全False
mask = np.zeros(len(run_w), dtype=bool)
# 获取lambda_input中的目标波长列表
target_waves = lambda_input.iloc[:,1].values

# 遍历所有目标波长,标记符合条件的范围
for n in target_waves:
    # 第一种光谱的波长范围
    wave_range1 = (n*(1+v1) - lw, n*(1+v1) + lw)
    mask |= np.logical_and(run_w >= wave_range1[0], run_w <= wave_range1[1])
    # 第二种光谱的波长范围
    wave_range2 = (n*(1+v2) - lw, n*(1+v2) + lw)
    mask |= np.logical_and(run_w >= wave_range2[0], run_w <= wave_range2[1])

# 筛选出需要拟合的波长和通量数据
filtered_w = run_w[mask]
filtered_f = run_f[mask]

2. 使用筛选后的数据执行lmfit拟合

将筛选后的filtered_w和filtered_f传入拟合函数,lmfit仅在目标范围内计算拟合误差:

model = lmfit.Model(spec)
p = model.make_params(t1=p1,t2=p2,r=p3,v1=p4,v2=p5,m1=p6,m2=p7,r1=p8,r2=p9,l1=p10,l2=p11,mi1=p12,mi2=p13)

# 使用筛选后的数据拟合
fit = model.fit(data=filtered_f, params=p, run_w=filtered_w, method='nelder')
lmfit.report_fit(fit)
fit.plot()

3. 替代方案:沿用权重逻辑与scipy对齐

若想保留原weight控制权重的思路,可直接将weight转换为lmfit所需的weights参数(lmfit中weights = 1/sigma):

# 原weight是sigma,转换为lmfit要求的weights
weights = 1 / weight

fit = model.fit(data=run_f, params=p, run_w=run_w, method='nelder', weights=weights, nan_policy='omit')

关键注意事项

  • 若spec函数涉及模板光谱插值,需对模板数据应用相同掩码,确保模板在筛选后的波长点上有对应值;
  • 布尔掩码筛选比极端sigma值更高效,更符合lmfit设计逻辑,避免无效计算;
  • 原代码中spec函数的v1、v2参数与全局变量重名,建议修改为v1_factor、v2_factor避免混淆。

内容的提问来源于stack exchange,提问作者cosmic noise

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 20:25:30