首页 > 解决方案 > 如何在 Python Curve_fit 上设置两个参数之间的不等式?

问题描述

所以,我正在编写一个程序来将 5 参数函数 (s,t,k,h,b) 拟合到一组实验数据。好吧,该程序有效,但我需要满足一个条件:0<k<h<1。我只需要将这个不等式添加到拟合过程中。任何人都可以帮忙吗?

算法是:

import xlrd
import numpy
import math
import scipy
from scipy.optimize import curve_fit
workbook = xlrd.open_workbook('Pasta3.xls')
sheet = workbook.sheet_by_name('teste')
x = []
for i in range (18):
    cell = sheet.cell_value(i+1,1)
    x.append(cell)
y = []
for i in range (18):
    cell_=sheet.cell_value(i+1,0)
    y.append(cell_)

def Re(x,s,t,k,h,b):
    return 130 + ((38000-130)*(1+s*((2*math.pi*x*t)**(-1*k))*math.cos(k*math.pi/2)+   ((2*math.pi*x*t)**(-1*h))*math.cos(h*math.pi/2)))/((((38000-130)*(1+s*((2*math.pi*x*t)**(-1*k))*math.cos(k*math.pi/2)+((2*math.pi*x*t)**(-1*h))*math.cos(h*math.pi/2)))/(38000-130))**2+(((38000-130)*(s*((2*math.pi*x*t)**(-1*k))*math.sin(k*math.pi/2)+((2*math.pi*x*t)**(-1*h))*math.sin(h*math.pi/2)+((2*math.pi*x*t*b)**(-1))))/(38000-130))**2)
popt, _ = curve_fit(Re,x,y)
print(popt)

标签: pythonconstraintscurve-fitting

解决方案


添加不等式约束的一种方法是使用该lmfit库。它的特点之一是能够以数学表达式的形式施加约束。结合简单的上限和下限,允许不等式约束。

对于您的示例,转换为使用 lmfit (可能会进行一些清理以确保所有内容都是 numpy ndarray)将给出如下结果:

import xlrd
from numpy import array, pi, sin, cos
from lmfit import Model

workbook = xlrd.open_workbook('Pasta3.xls')
sheet = workbook.sheet_by_name('teste')
x = []
for i in range (18):
    cell = sheet.cell_value(i+1,1)
    x.append(cell)
y = []
for i in range (18):
    cell_=sheet.cell_value(i+1,0)
    y.append(cell_)

x = array(x)
y = array(y)

def Re(x, s, t, k, h, b):
    xt = 2*pi*xt
    sxtk = s*xt**(-k)
    xth = xt**(-h)
    scale= 38000-130

    term1  = 1 + sxtk*cos(k*pi/2) + xth*cos(h*pi/2)
    term2 = sxtk*sin(k*pi/2) + xth*sin(h*pi/2)+(1/(xt*b))

    return 130 + scale**2 * term1/ (term1 + term2)

model = lmfit.Model(Re)
params = model.make_params(s=0.5, t=0.5, k=0.2, h=0.5, b=0.5)

result = model.fit(y, params, x=x)

print(result.fit_report())

一些重要的注意事项:

  1. 可读性很重要。真的,它比什么都重要,这就是你选择 Python 的原因。没有人 - 包括你 - 会理解你的拟合功能的混乱。这使得它完全没用:即使它很合身,你也不会知道它做了什么。我做了一些重构和简化,但你当然想检查一下。

  2. 您的示例没有给出拟合参数的初始值。 scipy.optimize.curve_fit()静默允许这一点,将所有参数的值设为 1。这是一个可怕的错误功能,会给许多用户带来麻烦。是的,这似乎是额外的工作,但拟合方法需要初始值。您始终需要提供对您的数据有意义的初始值。没有例外。

在示例lmfit中,每个参数都具有初始值。在这里,它们都设置为 0 到 1 之间的值,但您需要将它们设置为数据的合理初始值。

要设置参数的界限,您可以这样做

params['k'].min = 0
params['k'].max = 1

等等。要这么说h > k,您将定义一个新参数——我将其命名为h_minus_k,然后使用数学表达式设置h为从该值计算得出k

params.add('h_minus_k', value=0.1, min=0, max=1)
params['h'].expr = 'k + h_minus_k'

现在,既然h_minus_k不能为负数,h>k就会保持。拟合中仍有 5 个变量,now stkbh_minus_kh将在拟合中变化,但不是自由变化 - 它的值受k和的值约束h_minus_k。有了这个约束,您可以再次尝试拟合并比较结果:

params = model.make_params(s=0.5, t=0.5, k=0.2, h=0.5, b=0.5)

params['k'].min = 0
params['k'].max = 1
params['h'].min = 0
params['h'].max = 1

params.add('h_minus_k', value=0.1, min=0, max=1)
params['h'].expr = 'k + h_minus_k'

result2 = model.fit(y, params, x=x)

print(result2.fit_report())

推荐阅读