
本文介绍如何在线性组合拟合中引入x轴(如能量)平移参数,通过插值实现各参考谱形的连续位移,从而显著提升对目标谱的拟合精度。核心方法是将位移量设为可调参数,并利用scipy.interpolate.interp1d进行动态插值计算。
本文介绍如何在线性组合拟合中引入x轴(如能量)平移参数,通过插值实现各参考谱形的连续位移,从而显著提升对目标谱的拟合精度。核心方法是将位移量设为可调参数,并利用scipy.interpolate.interp1d进行动态插值计算。
在光谱分析、X射线吸收精细结构(XAFS)拟合或材料表征中,常需用若干已知标准谱形(lineshapes)的线性组合去拟合实测谱。但若各标准谱与实测谱存在微小的能量偏移(例如因仪器校准偏差、化学环境差异导致的峰位漂移),固定网格上的简单线性叠加往往难以获得高质量拟合。此时,允许各组分在x轴(如Energy)上独立或协同平移,是提升模型灵活性与物理合理性的关键。
实现x轴平移的核心思路是:不直接使用原始离散点上的强度值,而是构建连续插值函数,再在待优化的位移位置处实时采样。以下为完整、稳健的实现方案:
✅ 步骤一:构建插值函数(支持外推与边界处理)
from scipy.interpolate import interp1d
import numpy as np
# 假设 component_1/2/3 和 df1 均含 'Energy' 与 'Intensity' 列
# 注意:确保 Energy 单调递增,否则 interp1d 会报错
for comp in [component_1, component_2, component_3, df1]:
comp = comp.sort_values('Energy').reset_index(drop=True)
# 构建三次样条插值(更平滑),并设置 fill_value 处理边界外推
f_c1 = interp1d(component_1['Energy'], component_1['Intensity'],
kind='cubic', bounds_error=False, fill_value='extrapolate')
f_c2 = interp1d(component_2['Energy'], component_2['Intensity'],
kind='cubic', bounds_error=False, fill_value='extrapolate')
f_c3 = interp1d(component_3['Energy'], component_3['Intensity'],
kind='cubic', bounds_error=False, fill_value='extrapolate')
# 实测数据插值(含强度与不确定度)
f_obs = interp1d(df1['Energy'], df1['Intensity'],
kind='cubic', bounds_error=False, fill_value='extrapolate')
f_unc = interp1d(df1['Energy'], df1['unc'],
kind='linear', bounds_error=False, fill_value=np.nan)⚠️ 注意事项:
bounds_error=False+fill_value='extrapolate'是必须的,因为优化过程中位移参数可能导致查询点超出原始Energy范围;- 对不确定度建议用
'linear'插值(避免高阶振荡),且fill_value=np.nan可配合后续nan_policy='omit'安全过滤。
✅ 步骤二:扩展参数空间,加入位移变量
from lmfit import Parameters, minimize
params = Parameters()
params.add('a', value=0.33, min=0, max=1)
params.add('b', value=0.33, min=0, max=1)
params.add('c', value=0.33, min=0, max=1)
# 添加能量平移参数:e 表示整体位移量(单位同 Energy,如 eV)
# 若需独立位移,可定义 e1, e2, e3 并分别约束(如 ±0.5 eV)
params.add('e', value=0.0, min=-0.8, max=0.8) # 示例:±0.8 eV 范围✅ 步骤三:重写目标函数,支持动态位移采样
def residual(params):
a, b, c, e = params['a'].value, params['b'].value, params['c'].value, params['e'].value
# 在位移后的位置 e 处采样所有谱形(注意:此处 e 是标量位移,即 x → x + e)
# 更严谨做法:对整个Energy网格应用平移,再插值 → 见下方增强版
energy_grid = df1['Energy'].values
shifted_energy = energy_grid - e # 减e表示将标准谱向右平移e(等价于实测谱向左平移e)
try:
c1_vals = f_c1(shifted_energy)
c2_vals = f_c2(shifted_energy)
c3_vals = f_c3(shifted_energy)
obs_vals = f_obs(energy_grid)
unc_vals = f_unc(energy_grid)
except ValueError as exc:
# 插值失败时返回大残差,引导优化器避开无效区域
return np.full(len(energy_grid), 1e6)
calc = a * c1_vals + b * c2_vals + c * c3_vals
resid = (obs_vals - calc) / unc_vals
# 自动忽略 NaN 和 inf(如插值外推失效区)
resid = np.nan_to_num(resid, nan=0.0, posinf=1e6, neginf=-1e6)
return resid
# 执行拟合
result = minimize(residual, params, nan_policy='omit')
print(result.fit_report())? 增强技巧:支持独立位移与网格对齐
若三个组分需各自独立平移(如 e1, e2, e3),只需在 params 中添加对应变量,并在 residual 中分别计算 shifted_energy1 = energy_grid - e1 等。此外,为保证数值稳定性,建议:
- 使用相同长度与范围的
energy_grid(如取np.linspace(min_E, max_E, 1000)); - 对插值结果做
np.clip(..., a_min=0)防止负强度(若物理意义要求); - 拟合后用
result.eval()可视化各组分贡献与总拟合曲线。
通过引入x轴平移自由度,线性组合模型从刚性叠加升级为具备几何对齐能力的物理模型,大幅拓展了其在真实谱分析中的适用性与鲁棒性。

















