【问题标题】:Fitting a curve to a power-law distribution with curve_fit does not work使用 curve_fit 将曲线拟合到幂律分布不起作用
【发布时间】:2017-04-27 19:22:10
【问题描述】:

我正在尝试找到一条拟合我的数据的曲线,该曲线在视觉上似乎具有幂律分布。

我希望使用 scipy.optimize.curve_fit,但无论我尝试什么函数或数据规范化,我都会收到 RuntimeError(未找到参数或溢出)或一条曲线甚至不适合我的数据。请帮我弄清楚我在这里做错了什么。

%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

df = pd.DataFrame({
            'x': [ 1000, 3250, 5500, 10000, 32500, 55000, 77500, 100000, 200000 ],
            'y': [ 1100, 500, 288, 200, 113, 67, 52, 44, 5 ]
        })
df.plot(x='x', y='y', kind='line', style='--ro', figsize=(10, 5))

def func_powerlaw(x, m, c, c0):
    return c0 + x**m * c

target_func = func_powerlaw

X = df['x']
y = df['y']

popt, pcov = curve_fit(target_func, X, y)

plt.figure(figsize=(10, 5))
plt.plot(X, target_func(X, *popt), '--')
plt.plot(X, y, 'ro')
plt.legend()
plt.show()

输出

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-243-17421b6b0c14> in <module>()
     18 y = df['y']
     19 
---> 20 popt, pcov = curve_fit(target_func, X, y)
     21 
     22 plt.figure(figsize=(10, 5))

/Users/evgenyp/.virtualenvs/kindle-dev/lib/python2.7/site-packages/scipy/optimize/minpack.pyc in curve_fit(f, xdata, ydata, p0, sigma, absolute_sigma, check_finite, bounds, method, **kwargs)
    653         cost = np.sum(infodict['fvec'] ** 2)
    654         if ier not in [1, 2, 3, 4]:
--> 655             raise RuntimeError("Optimal parameters not found: " + errmsg)
    656     else:
    657         res = least_squares(func, p0, args=args, bounds=bounds, method=method,

RuntimeError: Optimal parameters not found: Number of calls to function has reached maxfev = 800.

【问题讨论】:

    标签: python numpy scipy runtime-error power-law


    【解决方案1】:

    您的func_powerlaw 不是严格的幂律,因为它有一个加法常数。

    一般来说,如果您想快速直观地评估幂律关系,您会

    plot(log(x),log(y))
    

    loglog(x,y)
    

    它们都应该给出一条直线,尽管它们之间存在细微差别(特别是在曲线拟合方面)。

    所有这一切都没有附加常数,这会破坏幂律关系。


    如果您想根据对数比例(通常需要)拟合加权数据的幂律,您可以使用下面的代码。

    import numpy as np
    from scipy.optimize import curve_fit
    
    def powlaw(x, a, b) :
        return a * np.power(x, b)
    def linlaw(x, a, b) :
        return a + x * b
    
    def curve_fit_log(xdata, ydata) :
        """Fit data to a power law with weights according to a log scale"""
        # Weights according to a log scale
        # Apply fscalex
        xdata_log = np.log10(xdata)
        # Apply fscaley
        ydata_log = np.log10(ydata)
        # Fit linear
        popt_log, pcov_log = curve_fit(linlaw, xdata_log, ydata_log)
        #print(popt_log, pcov_log)
        # Apply fscaley^-1 to fitted data
        ydatafit_log = np.power(10, linlaw(xdata_log, *popt_log))
        # There is no need to apply fscalex^-1 as original data is already available
        return (popt_log, pcov_log, ydatafit_log)
    

    【讨论】:

      【解决方案2】:

      作为回溯状态,在没有找到固定点的情况下达到了函数评估的最大数量(以终止算法)。您可以使用选项maxfev 增加最大数量。对于此示例,设置 maxfev=2000 足够大,可以成功终止算法。

      但是,解决方案并不令人满意。这是由于算法为变量选择了一个(默认)初始估计,对于这个例子来说,这并不好(需要大量的迭代是一个指标)。提供另一个初始化点(通过简单的试验和错误发现)可以很好地拟合,无需增加maxfev

      两个拟合和与数据的视觉比较如下所示。

      x = np.asarray([ 1000, 3250, 5500, 10000, 32500, 55000, 77500, 100000, 200000 ])
      y = np.asarray([ 1100, 500, 288, 200, 113, 67, 52, 44, 5 ])
      
      sol1 = curve_fit(func_powerlaw, x, y, maxfev=2000 )
      sol2 = curve_fit(func_powerlaw, x, y, p0 = np.asarray([-1,10**5,0]))
      

      【讨论】:

      • 感谢您的帮助和解释。我试过增加 maxfev,但在我的机器上 2,000 还不够,我没有进一步增加它,认为问题出在其他地方。不过,我还没有尝试设置初始估计值,它确实很有魅力。
      • 当我尝试了答案时,我从func_powerlaw 函数中的电源运算符那里得到了一个ValueError。将p0 更改为np.asarray([0,10**5,0] 解决了这个问题
      猜你喜欢
      • 2017-04-07
      • 2016-06-24
      • 1970-01-01
      • 2020-05-15
      • 1970-01-01
      • 2015-11-23
      • 1970-01-01
      • 1970-01-01
      • 2021-05-30
      相关资源
      最近更新 更多