簡體   English   中英

變量輸入函數 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM