曲线是:
import numpy as np
import scipy.stats as sp
from scipy.optimize import curve_fit
from lmfit import minimize, Parameters, Parameter, report_fit#
import xlwings as xw
import os
import pandas as pd
我尝试从scipy运行简单的曲线拟合: 返回
Out[156]:
(array([ 1., 1.]), array([[ inf, inf],
[ inf, inf]]))
好的,所以我认为我需要开始绑定我的参数,这就是求解器的作用。我使用lmfit来做到这一点:
params = Parameters()
params.add
我试图通过改变起始参数来将python推向正确的方向:
或使用curvefit:
答案 0 :(得分:2)
我认为问题的一部分是你只有5个观察值,2个x
的值相同,而且模型并不能完美地表示你的数据。我还建议尝试将模型的日志放入数据日志中。而且,如果您希望n2
为~10,则应将其用作起始值。
任意将min
和max
应用于计算出的模型,尤其是值非常接近您的数据范围,这一点毫无意义。这样做会阻止拟合方法探索更改参数值对模型函数的影响。
对代码进行一些修改以省略第一个数据点(这似乎是抛弃模型),我发现:
import numpy as np
import scipy.stats as sp
from lmfit import Parameters, minimize, fit_report
import matplotlib.pyplot as plt
x_data = np.array((1e-04,9e-01,9.5835e-01,9.8e-01,9.9e-01,9.9e-01))
y_data = np.array((250e3,1e6,2.5e6,5e6,7.5e6,10e6))
x_data = x_data[1:]
y_data = y_data[1:]
def func(params, x, data, m1=250e3,m2=10e6):
n1 = params['n1'].value
n2 = params['n2'].value
model = n2 + n1*(sp.norm.ppf(x))
return np.log(data) - model
params = Parameters()
params.add('n1', value= 1, min=0.01, max=20)
params.add('n2', value= 10, min=0, max=20)
result = minimize(func, params, args=(x_data[1:-1], y_data[1:-1]))
print(fit_report(result))
n1 = result.params['n1'].value
n2 = result.params['n2'].value
ym = np.exp(n2 + n1*(sp.norm.ppf(x_data)))
plt.plot(x_data, y_data, 'o')
plt.plot(x_data, ym, '-')
plt.legend(['data', 'fit'])
plt.show()
提供
的报告[[Fit Statistics]]
# fitting method = leastsq
# function evals = 15
# data points = 3
# variables = 2
chi-square = 0.006
reduced chi-square = 0.006
Akaike info crit = -14.438
Bayesian info crit = -16.241
[[Variables]]
n1: 1.85709072 +/- 0.190473 (10.26%) (init= 1)
n2: 11.5455736 +/- 0.390805 (3.38%) (init= 10)
[[Correlations]] (unreported correlations are < 0.100)
C(n1, n2) = -0.993
答案 1 :(得分:1)
当我们提供一个良好的起点时,曲线拟合运行平稳。我们可以得到一个
sp.norm.ppf(x_data)
和np.log(y_data)
或者,如果您希望计算机在没有“帮助”的情况下找到解决方案
所有四种方法都产生相同的结果,并且优于excel结果。
import numpy as np
import scipy.stats as sp
from scipy.optimize import curve_fit, basinhopping, brute
def func1(x, n1, n2):
return np.clip(np.exp(n2 + n1*(sp.norm.ppf(x))),25e4,10e6)
def func_free(x, n1, n2):
return np.exp(n2 + n1*(sp.norm.ppf(x)))
def sqerr(n12, x, y):
return ((func1(x, *n12) - y)**2).sum()
x_data = np.array((1e-04,9e-01,9.5835e-01,9.8e-01,9.9e-01,9.9e-01))
y_data = np.array((250e3,1e6,2.5e6,5e6,7.5e6,10e6))
# get a good starting point
# either by linear regression
lin = sp.linregress(sp.norm.ppf(x_data), np.log(y_data))
# or by using the free (non-clipped) version of the formula
(n1f, n2f), infof = curve_fit(func_free, x_data, y_data, (1, 1))
# use those on the original problem
(n1, n2), info = curve_fit(func1, x_data, y_data, (lin.slope, lin.intercept))
(n12, n22), info2 = curve_fit(func1, x_data, y_data, (n1f, n2f))
# OR
# use basin hopping
hop = basinhopping(sqerr, (1, 1), minimizer_kwargs=dict(args=(x_data, y_data)), stepsize=10)
# OR
# brute force it
brt = brute(sqerr, ((-100, 100), (-100, 100)), (x_data, y_data), 201, full_output=True)
# all four solutions are essentially the same:
assert np.allclose((n1, n2), (n12, n22))
assert np.allclose((n1, n2), hop.x)
assert np.allclose((n1, n2), brt[0])
# we are actually a bit better than excel
n1excel, n2excel = 1.7925, 11.6771
print('solution', n1, n2)
print('error', ((func1(x_data, n1, n2) - y_data)**2).sum())
print('excel', ((func1(x_data, n1excel, n2excel) - y_data)**2).sum())
输出:
solution 2.08286042997 11.1397332743
error 3.12796761241e+12
excel 5.80088578059e+12
备注:一个简单的优化 - 我为了简单而省略,因为事情还是足够快 - 本来是将sp.norm.ppf
拉出模型函数。这是可能的,因为它不依赖于拟合参数。因此,当我们的任何求解器调用该函数时,它总是执行完全相同的计算 - sp.norm.ppf(x_data)
- 首先,所以我们不妨预先计算它。
这一观察结果也是我在线性回归中使用sp.norm.ppf(x_data)
的原因。
答案 2 :(得分:0)
以下是使用scipy.optimize.curve_fit执行回归的简单方法:
import matplotlib.pyplot as plt
import scipy.optimize as opt
import scipy.stats as stats
import numpy as np
% matplotlib inline
# Objective
def model(x, n1, n2):
return np.exp(n2 + n1*(stats.norm.ppf(x)))
# Data
x_samp = np.array((1e-04, 9e-01, 9.5835e-01, 9.8e-01, 9.9e-01, 9.9e-01))
y_samp = np.array((250e3, 1e6, 2.5e6, 5e6 ,7.5e6, 10e6))
x_lin = np.linspace(min(x_samp), max(x_samp), 50) # for fitting
# Regression
p0 = [5, 5] # guessed params
w, cov = opt.curve_fit(model, x_samp, y_samp, p0=p0)
print("Estimated Parameters", w)
y_fit = model(x_lin, *w)
# Visualization
plt.plot(x_samp, y_samp, "ko", label="Data")
plt.plot(x_lin, y_fit, "k--", label="Fit")
plt.title("Curve Fitting")
plt.legend(loc="upper left")
输出
Estimated Parameters [ 2.08285048 11.13975585]
<强>详情
您的数据按原样绘制,没有转换。如果模型和初始参数,p0
合理地适合您的数据,则最容易执行此类回归。当这些项目提供给scipy.optimize.curve_fit
时,会返回权重或优化的估计参数,w
的元组,以及{{3} }}。我们可以计算矩阵对角线的一个标准差:
p_stdev = np.sqrt(np.diag(cov))
print("Std. Dev. of Params:", p_stdev)
# Std. Dev. of Params: [ 0.42281111 0.95945127]
我们通过再次将这些估计参数提供给模型并绘制拟合线来直观地评估拟合优度。