Python SciPy曲线拟合不符合幂律

Python SciPy曲线拟合不符合幂律,python,numpy,optimization,scipy,curve-fitting,Python,Numpy,Optimization,Scipy,Curve Fitting,因此,我试图用以下类型的幂律拟合一组数据: def f(x,N,a): # Power law fit if a >0: return N*x**(-a) else: return 10.**300 par,cov = scipy.optimize.curve_fit(f,data,time,array([10**(-7),1.2])) 其中else条件只是强制a为正。使用scipy.optimize.curve_拟合,N和a的返回值分别

因此,我试图用以下类型的幂律拟合一组数据:

def f(x,N,a): # Power law fit
    if a >0:
        return N*x**(-a)
    else:
        return 10.**300

par,cov = scipy.optimize.curve_fit(f,data,time,array([10**(-7),1.2]))
其中else条件只是强制a为正。使用scipy.optimize.curve_拟合,N和a的返回值分别为1.2e+04和1.9e0-7,与数据绝对不相交。根据我手动输入的拟合,N和a的值应分别达到1e-07和1.2左右,尽管将这些值作为初始参数输入曲线拟合不会改变结果。删除a为正值的条件会导致拟合更差,因为它选择了负值,这会导致拟合具有错误的符号斜率


我不知道如何从这个例程中获得可信的拟合,更不用说可靠的拟合了,但是我找不到任何其他好的Python曲线拟合例程。我是否需要编写自己的最小二乘算法,或者我在这里做错了什么?

更新

在最初的帖子中,我展示了一个使用
lmfit
的解决方案,它允许为参数指定边界。从版本0.17开始,scipy还允许直接为参数指定边界(请参阅)。请在编辑之后找到下面的解决方案,这有望成为如何使用scipy的
曲线拟合
和参数边界的一个最小示例

原创帖子

正如@Warren Weckesser所建议的,您可以使用来完成此任务,这允许您为参数分配边界,并避免使用这个“丑陋的”if子句

由于您没有提供任何数据,我创建了一些,如下所示:

它们遵循的规律是f(x)=10.5*x**(-0.08)

正如@roadrunner66所建议的那样,我通过将幂律转化为线性函数来拟合它们:

y = N * x ** a
ln(y) = ln(N * x ** a)
ln(y) = a * ln(x) + ln(N)
因此,我首先对原始数据使用
np.log
,然后进行拟合。现在使用lmfit时,我得到以下输出:

[[Variables]]
    lN:   2.35450302 +/- 0.019531 (0.83%) (init= 1.704748)
    a:   -0.08035342 +/- 0.005158 (6.42%) (init=-0.5)
因此,
a
非常接近原始值,
np.exp(2.35450302)
给出了10.53,也非常接近原始值

情节如下:;正如您所见,拟合非常好地描述了数据:

以下是包含两条内联注释的完整代码:

import numpy as np
import matplotlib.pyplot as plt
from lmfit import minimize, Parameters, Parameter, report_fit

# generate some data with noise
xData = np.linspace(0.01, 100., 50.)
aOrg = 0.08
Norg = 10.5
yData = Norg * xData ** (-aOrg) + np.random.normal(0, 0.5, len(xData))
plt.plot(xData, yData, 'bo')
plt.show()

# transform data so that we can use a linear fit
lx = np.log(xData)
ly = np.log(yData)
plt.plot(lx, ly, 'bo')
plt.show()

def decay(params, x, data):

    lN = params['lN'].value
    a = params['a'].value

    # our linear model
    model = a * x + lN
    return model - data # that's what you want to minimize

# create a set of Parameters
params = Parameters()
params.add('lN', value=np.log(5.5), min=0.01, max=100)  # value is the initial value
params.add('a', value=-0.5, min=-1, max=-0.001)  # min, max define parameter bounds

# do fit, here with leastsq model
result = minimize(decay, params, args=(lx, ly))

# write error report
report_fit(params)

# plot data
xnew = np.linspace(0., 100., 5000.)
# plot the data
plt.plot(xData, yData, 'bo')
plt.plot(xnew, np.exp(result.values['lN']) * xnew ** (result.values['a']), 'r')
plt.show()
编辑

假设已安装scipy 0.17,还可以使用
curve\u fit
执行以下操作。我展示它是为了你对幂律的原始定义(下图中的红线)以及对数数据(下图中的黑线)。数据的生成方式与上述相同。绘图如下所示:

如您所见,数据描述得非常好。如果打印
popt
popt_log
,则分别获得
数组([10.47463426,0.07914812])
数组([2.35158653,-0.08045776])
(注意:对于字母1,必须取第一个参数的指数-
np.exp(popt_log[0])=10.502,这与原始数据相近)

以下是完整的代码:

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# generate some data with noise
xData = np.linspace(0.01, 100., 50)
aOrg = 0.08
Norg = 10.5
yData = Norg * xData ** (-aOrg) + np.random.normal(0, 0.5, len(xData))

# get logarithmic data
lx = np.log(xData)
ly = np.log(yData)

def f(x, N, a):
    return N * x ** (-a)

def f_log(x, lN, a):
    return a * x + lN

# optimize using the appropriate bounds
popt, pcov = curve_fit(f, xData, yData, bounds=(0, [30., 20.]))
popt_log, pcov_log = curve_fit(f_log, lx, ly, bounds=([0, -10], [30., 20.]))

xnew = np.linspace(0.01, 100., 5000)

# plot the data
plt.plot(xData, yData, 'bo')
plt.plot(xnew, f(xnew, *popt), 'r')
plt.plot(xnew, f(xnew, np.exp(popt_log[0]), -popt_log[1]), 'k')
plt.show()

欢迎来到这么好的第一个问题。不知道为什么会有一个沉默的否决票。“我找不到任何其他好的Python曲线拟合例程…”看看
lmfit
();特别要注意的是,在scipy 0.17中,你可以对边界进行拟合,这应该比你强加的非平滑截止要好。幂律中的数据在不同的区域有很大的不同。我会尝试拟合数据的
日志
。什么是
数据
,什么是
时间
?你不是换了吗?@CF84我刚摘了一些;你可以选择任何你喜欢的边界。