在 python 中拟合自定义函数

Jon*_*eco 1 python function curve-fitting

我正在尝试使用以下函数来拟合我的数据:

在此输入图像描述

我正在使用的数据如下:

X1: 
0        1.0
1      101.0
2      201.0
3      301.0
4      401.0
5      501.0
6      601.0
7      701.0
8      801.0
9      901.0
10    1001.0
11    1101.0
12    1201.0
13    1301.0
14    1401.0
15    1501.0
16    1601.0
17    1701.0
18    1801.0
19    1901.0

Y1: 
0     0.121159
1     0.195525
2     0.167305
3     0.125499
4     0.094138
5     0.071610
6     0.053848
7     0.039890
8     0.031099
9     0.023976
10    0.018271
11    0.013807
12    0.010596
13    0.008033
14    0.006710
15    0.005222
16    0.004299
17    0.003376
18    0.002976
19    0.002659
Run Code Online (Sandbox Code Playgroud)

我调用该函数的代码如下:

def logN(X1, mu, SD1):

return  A/X1 * np.exp(-0.5 * (np.log(X1/mu)**2/np.log(SD1)**2))
        params, pcov = curve_fit(logN, X1,Y1) print (params)


plt.plot(X1, Y1, "o") 
plt.plot(X1, logN(X1 ,params[0], params[1]))  
plt.show()
Run Code Online (Sandbox Code Playgroud)

该函数的结果显示参数等于 1,并且我收到以下警告:

minpack.py:829: OptimizeWarning: Covariance of the parameters could not be estimated
Run Code Online (Sandbox Code Playgroud)

类别=优化警告)

在此输入图像描述

我想知道我是否正确调用了函数的语法是否错误。一些想法?

jla*_*rcy 7

观察结果

您面临着多重挑战:

  • 正如您所说,您的问题是非线性回归(就系数而言),可以使用非线性算法来解决,例如Levenberg Marquardt(在 中实现scipy.optimize.curve_fit
  • 您没有考虑A优化过程中的系数,但它在您的函数中明确说明(因此它采用您的帖子中未详细说明的全局值),并且该系数与前者包含后者A有关。sigma
  • 您的一些数据不适合对数正态分布(指向x=1似乎可疑)并且没有不y确定性的估计。这可能会妨碍执行参数优化时的正确收敛,然后算法无法计算协方差矩阵。

改进建议:

  • 可以将您的问题重写为涉及二阶多项式的经典 OLS。那么我们就不必依赖NLLS算法了。只需将双对数变换应用于您的关系即可确认它是可以承受的并获得参数转换公式。如果可用,始终优先选择 OLS,而不是 NLLS。

  • 删除或惩罚(加权)可疑点,最好使用客观标准。

  • 调整您的模型函数(此处不考虑)。

MCVE

根据您提供的数据:

import io
import numpy as np
from scipy import optimize
import pandas as pd
import matplotlib.pyplot as plt

data = io.StringIO("""id;x;y;sy
0;1.0;0.121159;1
1;101.0;0.195525;1
2;201.0;0.167305;1
3;301.0;0.125499;1
4;401.0;0.094138;1
5;501.0;0.071610;1
6;601.0;0.053848;1
7;701.0;0.039890;1
8;801.0;0.031099;1
9;901.0;0.023976;1
10;1001.0;0.018271;1
11;1101.0;0.013807;1
12;1201.0;0.010596;1
13;1301.0;0.008033;1
14;1401.0;0.006710;1
15;1501.0;0.005222;1
16;1601.0;0.004299;1
17;1701.0;0.003376;1
18;1801.0;0.002976;1
19;1901.0;0.002659;1
""")
df = pd.read_csv(data, sep=";", index_col="id")
Run Code Online (Sandbox Code Playgroud)

将模型函数重写为:

def func(x, A, mu, sigma):
    return (A/x)*np.exp(-((np.log(x/mu)/np.log(sigma))**2)/2)
Run Code Online (Sandbox Code Playgroud)

修改签名

然后我们可以通过向优化算法提供数据和足够智能的初始条件来简单地拟合该函数:

popt, pcov = optimize.curve_fit(func, df.x, df.y, sigma=df.sy,
                                p0=(50, 100, 0.1), method="lm")
Run Code Online (Sandbox Code Playgroud)

但结果不是很理想(未加权):

在此输入图像描述

并且由于可疑点而容易发生变化(x=1用惩罚w=100):

在此输入图像描述

因此,测量结果的不确定性y有助于调整拟合度。

无论如何,由于问题可以线性化,我们应该依赖这个属性,权重也可以在 OLS 中使用。

线性化

scipy.optimize.least_squares如果您愿意,您可以执行 OLS 。我将使用sklearn非常方便的框架:

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
Run Code Online (Sandbox Code Playgroud)

让我们删除第一个可疑点:

df = df.loc[1:,:]
Run Code Online (Sandbox Code Playgroud)

然后,我们调整输入并执行对数转换:

X = np.log(df.x.values).reshape(-1, 1)
y = np.log(df.y)
Run Code Online (Sandbox Code Playgroud)

我们为二阶多项式创建 OLS 管道:

poly = PolynomialFeatures(2)
linreg = LinearRegression()
model = make_pipeline(poly, linreg)
Run Code Online (Sandbox Code Playgroud)

最后我们根据数据调整模型:

model.fit(X, y)
model.score(X, y) # 0.9982242621455882
Run Code Online (Sandbox Code Playgroud)

它导致:

在此输入图像描述

这对于二次方程来说似乎是一个合理的调整。然后只需将系数转换回您想要的数量即可。