[英]Trying to fit data to cotangens function using scipy.curve_fit
[英]Variable input function scipy.curve_fit
我正在研究我的實驗數據的峰值解卷積,我想生成一個 Python 腳本,我可以在其中輕松改變非線性曲線擬合/峰值解卷積的方程。 使用高斯曲線和線性偏移,scipy.optimize.curve_fit 可以很好地與以下代碼配合使用:
def Combined(x,*params):
off = Linear(x,params[0],params[1])
peak1 = Gaussian(x,params[2],params[3],params[4])
peak2 = Gaussian(x,params[5],params[6],params[7])
peak3 = Gaussian(x,params[8],params[9],params[10])
return off + peak1 + peak2 + peak3
popt, pcov = opt.curve_fit(Combined, data[10][0], data[10][1], method='lm', check_finite=True, p0=[0.1, 0.1, 115, 508.33, 7.1,130, 508.33, 7.1, 165.84, 508.33, 7.1])
所有方程之前都定義在一個函數中:
def ZeroOrder(x,a):
return a
def Linear(x,a,b):
return a+b*x
def SecondOrder(x,a,b,c):
return a+b*x+c*x**2
我想創建一個函數為Combine(x,baseline='ZeroOrder',peak1='Gaussian',peak2='Gaussian',peak3='Gaussian')
在那里我可以輕松地分配不同的峰值函數,而不必創建一個每個組合的特定功能。 然而,在我的理解中, curve_fit 函數非常嚴格,需要一個輸入函數為Combined(x,*params)
。 如何更改我的代碼,使其在所需的功能中工作?
對於這種情況,我會使用更通用的東西,比如 scipy 中的 f_min。 以下是如何為實驗數據制作通用回歸量的示例。
我創建了一個抽象類只是為了定義一個形狀函數的接口,這有助於創建一個更通用的方法
在下面的例子中,求解器得到的系數與原始函數不同,但對於確定域,結果函數滿足收斂條件,這一定是因為我創建的函數有很多自由度.
from abc import ABC, abstractmethod
from typing import List
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import fmin
from pprint import pprint
class ShapeFunction(ABC):
def __init__(self, params_count: int):
self.params_count = params_count
self.params = [0] * params_count
@property
def params(self):
return self.__params
@params.setter
def params(self, params: List[float]):
if len(params) != self.params_count:
raise ValueError(f"params count must be {self.params_count}")
self.__params = params
@abstractmethod
def evaluate(self, t: List[float]) -> List[float]:
pass
class Gaussian(ShapeFunction):
def __init__(self):
params_count = 3
super().__init__(params_count)
def evaluate(self, t: List[float]) -> List[float]:
return self.Gaussian(t, *self.params)
@staticmethod
def Gaussian(x, mu, sigma, scale):
return scale/(sigma*np.sqrt(2*np.pi))*np.exp(-0.5*((x-mu)/sigma)**2)
class ZeroOrder(ShapeFunction):
def __init__(self):
params_count = 1
super().__init__(params_count)
def evaluate(self, t: List[float]) -> List[float]:
return self.ZeroOrder(t, *self.params)
@staticmethod
def ZeroOrder(x, a):
return a
class Linear(ShapeFunction):
def __init__(self):
params_count = 2
super().__init__(params_count)
def evaluate(self, t: List[float]) -> List[float]:
return self.Linear(t, *self.params)
@staticmethod
def Linear(x, a, b):
return a+b*x
class Quadratic(ShapeFunction):
def __init__(self):
params_count = 3
super().__init__(params_count)
def evaluate(self, t: List[float]) -> List[float]:
return self.Quadratic(t, *self.params)
@staticmethod
def Quadratic(x, a, b, c):
return a+b*x+c*x**2
f1 = Gaussian()
f1.params = [0, 1, 10]
f2 = ZeroOrder()
f2.params = [1]
f3 = Linear()
f3.params = [1, 1]
f4 = Quadratic()
f4.params = [1, 1, 1]
shape_functions = [f1, f2, f3, f4]
coefs_count = sum([func.params_count for func in shape_functions])
original_coefs = []
for func in shape_functions:
original_coefs.extend(func.params)
x = np.linspace(-5, 5, 100)
sample = f1.evaluate(x) + f2.evaluate(x) + f3.evaluate(x) + f4.evaluate(x)
def cost_function(coefs: list[float], *params):
funcs: List[ShapeFunction] = params[0]
parametric_values: List[float] = params[1]
obj_y: List[float] = params[2]
y = [0] * len(parametric_values)
for func in funcs:
func.params = coefs[:func.params_count]
y += func.evaluate(parametric_values)
coefs = coefs[func.params_count:]
return np.sum((y - obj_y)**2)
x0 = [1] * coefs_count
solution = fmin(cost_function, x0, args=(shape_functions, x, sample))
for sol, coef in zip(solution, original_coefs):
print(f"Original: {coef:.2f} -> solution: {sol:.2f}")
y_sol = np.sum([func.evaluate(x) for func in shape_functions], axis=0)
plt.plot(x, sample, label="Original")
plt.plot(x, y_sol, label="Solution", alpha=0.5, marker=".")
plt.legend()
plt.show()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.