我有一组数据,我想比较哪条线最能描述它(不同阶的多项式,指数或对数)。

我使用Python和Numpy和多项式拟合有一个函数polyfit()。但我没有发现这样的函数的指数和对数拟合。

有吗?或者如何解决这个问题?


当前回答

我们在解决这两个问题的同时演示了lmfit的特性。

鉴于

import lmfit

import numpy as np

import matplotlib.pyplot as plt


%matplotlib inline
np.random.seed(123)
# General Functions
def func_log(x, a, b, c):
    """Return values from a general log function."""
    return a * np.log(b * x) + c


# Data
x_samp = np.linspace(1, 5, 50)
_noise = np.random.normal(size=len(x_samp), scale=0.06)
y_samp = 2.5 * np.exp(1.2 * x_samp) + 0.7 + _noise
y_samp2 = 2.5 * np.log(1.2 * x_samp) + 0.7 + _noise

Code

方法1 - lmfit模型

拟合指数数据

regressor = lmfit.models.ExponentialModel()                # 1    
initial_guess = dict(amplitude=1, decay=-1)                # 2
results = regressor.fit(y_samp, x=x_samp, **initial_guess)
y_fit = results.best_fit    

plt.plot(x_samp, y_samp, "o", label="Data")
plt.plot(x_samp, y_fit, "k--", label="Fit")
plt.legend()

方法2 -自定义模型

拟合日志数据

regressor = lmfit.Model(func_log)                          # 1
initial_guess = dict(a=1, b=.1, c=.1)                      # 2
results = regressor.fit(y_samp2, x=x_samp, **initial_guess)
y_fit = results.best_fit

plt.plot(x_samp, y_samp2, "o", label="Data")
plt.plot(x_samp, y_fit, "k--", label="Fit")
plt.legend()


细节

选择一个回归类 提供命名的、尊重函数域的初始猜测

您可以从回归器对象确定推断出的参数。例子:

regressor.param_names
# ['decay', 'amplitude']

要进行预测,请使用ModelResult.eval()方法。

model = results.eval
y_pred = model(x=np.array([1.5]))

注意:ExponentialModel()遵循一个衰减函数,它接受两个参数,其中一个是负的。

请参见ExponentialGaussianModel(),它接受更多参数。

通过> pip Install lmfit安装库。

其他回答

我想你可以用:

np.log   -->  natural log
np.log10 -->  base 10
np.log2  -->  base 2

稍微修改一下IanVS的答案:

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

def func(x, a, b, c):
  #return a * np.exp(-b * x) + c
  return a * np.log(b * x) + c

x = np.linspace(1,5,50)   # changed boundary conditions to avoid division by 0
y = func(x, 2.5, 1.3, 0.5)
yn = y + 0.2*np.random.normal(size=len(x))

popt, pcov = curve_fit(func, x, yn)

plt.figure()
plt.plot(x, yn, 'ko', label="Original Noised Data")
plt.plot(x, func(x, *popt), 'r-', label="Fitted Curve")
plt.legend()
plt.show()

结果如下图所示:

要拟合y = A + B log x,只需将y与(log x)拟合。

>>> x = numpy.array([1, 7, 20, 50, 79])
>>> y = numpy.array([10, 19, 30, 35, 51])
>>> numpy.polyfit(numpy.log(x), y, 1)
array([ 8.46295607,  6.61867463])
# y ≈ 8.46 log(x) + 6.62

为了拟合y = AeBx,两边取对数得到log y = log A + Bx。把log y和x匹配起来。

请注意,拟合(log y)如果是线性的,将强调y的小值,导致大y的大偏差。这是因为polyfit(线性回归)通过最小化∑i (ΔY)2 =∑i (Yi−Ŷi)2来工作。当Yi = log Yi时,余数ΔYi = Δ(log Yi)≈Δyi / | Yi |。因此,即使多拟合对大y做出了非常糟糕的决定,“除以-|y|”因素也会补偿它,导致多拟合倾向于小值。

这可以通过给每个条目一个与y成比例的“权重”来缓解。polyfit通过w关键字参数支持加权最小二乘。

>>> x = numpy.array([10, 19, 30, 35, 51])
>>> y = numpy.array([1, 7, 20, 50, 79])
>>> numpy.polyfit(x, numpy.log(y), 1)
array([ 0.10502711, -0.40116352])
#    y ≈ exp(-0.401) * exp(0.105 * x) = 0.670 * exp(0.105 * x)
# (^ biased towards small values)
>>> numpy.polyfit(x, numpy.log(y), 1, w=numpy.sqrt(y))
array([ 0.06009446,  1.41648096])
#    y ≈ exp(1.42) * exp(0.0601 * x) = 4.12 * exp(0.0601 * x)
# (^ not so biased)

请注意,Excel、LibreOffice和大多数科学计算器通常使用未加权(有偏差)公式来计算指数回归/趋势线。如果您希望您的结果与这些平台兼容,即使它提供了更好的结果,也不要包含权重。


如果你可以使用scipy,你可以使用scipy。optimize。Curve_fit适合任何不需要转换的模型。

对于y = A + B log x,结果与变换方法相同:

>>> x = numpy.array([1, 7, 20, 50, 79])
>>> y = numpy.array([10, 19, 30, 35, 51])
>>> scipy.optimize.curve_fit(lambda t,a,b: a+b*numpy.log(t),  x,  y)
(array([ 6.61867467,  8.46295606]), 
 array([[ 28.15948002,  -7.89609542],
        [ -7.89609542,   2.9857172 ]]))
# y ≈ 6.62 + 8.46 log(x)

然而,对于y = AeBx,我们可以得到一个更好的拟合,因为它直接计算Δ(log y)。但是我们需要提供一个初始化的猜测,以便curve_fit能够达到所需的局部最小值。

>>> x = numpy.array([10, 19, 30, 35, 51])
>>> y = numpy.array([1, 7, 20, 50, 79])
>>> scipy.optimize.curve_fit(lambda t,a,b: a*numpy.exp(b*t),  x,  y)
(array([  5.60728326e-21,   9.99993501e-01]),
 array([[  4.14809412e-27,  -1.45078961e-08],
        [ -1.45078961e-08,   5.07411462e+10]]))
# oops, definitely wrong.
>>> scipy.optimize.curve_fit(lambda t,a,b: a*numpy.exp(b*t),  x,  y,  p0=(4, 0.1))
(array([ 4.88003249,  0.05531256]),
 array([[  1.01261314e+01,  -4.31940132e-02],
        [ -4.31940132e-02,   1.91188656e-04]]))
# y ≈ 4.88 exp(0.0553 x). much better.

Wolfram有一个封闭形式的解拟合指数。他们也有类似的解决方案拟合对数和幂律。

我发现这比scipy的curve_fit工作得更好。特别是当你没有“接近零”的数据时。这里有一个例子:

import numpy as np
import matplotlib.pyplot as plt

# Fit the function y = A * exp(B * x) to the data
# returns (A, B)
# From: https://mathworld.wolfram.com/LeastSquaresFittingExponential.html
def fit_exp(xs, ys):
    S_x2_y = 0.0
    S_y_lny = 0.0
    S_x_y = 0.0
    S_x_y_lny = 0.0
    S_y = 0.0
    for (x,y) in zip(xs, ys):
        S_x2_y += x * x * y
        S_y_lny += y * np.log(y)
        S_x_y += x * y
        S_x_y_lny += x * y * np.log(y)
        S_y += y
    #end
    a = (S_x2_y * S_y_lny - S_x_y * S_x_y_lny) / (S_y * S_x2_y - S_x_y * S_x_y)
    b = (S_y * S_x_y_lny - S_x_y * S_y_lny) / (S_y * S_x2_y - S_x_y * S_x_y)
    return (np.exp(a), b)


xs = [33, 34, 35, 36, 37, 38, 39, 40, 41, 42]
ys = [3187, 3545, 4045, 4447, 4872, 5660, 5983, 6254, 6681, 7206]

(A, B) = fit_exp(xs, ys)

plt.figure()
plt.plot(xs, ys, 'o-', label='Raw Data')
plt.plot(xs, [A * np.exp(B *x) for x in xs], 'o-', label='Fit')

plt.title('Exponential Fit Test')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

还可以使用scipy.optimize中的curve_fit将一组数据适合于任何函数。例如,如果你想拟合一个指数函数(来自文档):

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

def func(x, a, b, c):
    return a * np.exp(-b * x) + c

x = np.linspace(0,4,50)
y = func(x, 2.5, 1.3, 0.5)
yn = y + 0.2*np.random.normal(size=len(x))

popt, pcov = curve_fit(func, x, yn)

然后如果你想画出来,你可以这样做:

plt.figure()
plt.plot(x, yn, 'ko', label="Original Noised Data")
plt.plot(x, func(x, *popt), 'r-', label="Fitted Curve")
plt.legend()
plt.show()

(注意:当你绘图时,popt前面的*会将项展开为func所期望的a, b和c。)

我们在解决这两个问题的同时演示了lmfit的特性。

鉴于

import lmfit

import numpy as np

import matplotlib.pyplot as plt


%matplotlib inline
np.random.seed(123)
# General Functions
def func_log(x, a, b, c):
    """Return values from a general log function."""
    return a * np.log(b * x) + c


# Data
x_samp = np.linspace(1, 5, 50)
_noise = np.random.normal(size=len(x_samp), scale=0.06)
y_samp = 2.5 * np.exp(1.2 * x_samp) + 0.7 + _noise
y_samp2 = 2.5 * np.log(1.2 * x_samp) + 0.7 + _noise

Code

方法1 - lmfit模型

拟合指数数据

regressor = lmfit.models.ExponentialModel()                # 1    
initial_guess = dict(amplitude=1, decay=-1)                # 2
results = regressor.fit(y_samp, x=x_samp, **initial_guess)
y_fit = results.best_fit    

plt.plot(x_samp, y_samp, "o", label="Data")
plt.plot(x_samp, y_fit, "k--", label="Fit")
plt.legend()

方法2 -自定义模型

拟合日志数据

regressor = lmfit.Model(func_log)                          # 1
initial_guess = dict(a=1, b=.1, c=.1)                      # 2
results = regressor.fit(y_samp2, x=x_samp, **initial_guess)
y_fit = results.best_fit

plt.plot(x_samp, y_samp2, "o", label="Data")
plt.plot(x_samp, y_fit, "k--", label="Fit")
plt.legend()


细节

选择一个回归类 提供命名的、尊重函数域的初始猜测

您可以从回归器对象确定推断出的参数。例子:

regressor.param_names
# ['decay', 'amplitude']

要进行预测,请使用ModelResult.eval()方法。

model = results.eval
y_pred = model(x=np.array([1.5]))

注意:ExponentialModel()遵循一个衰减函数,它接受两个参数,其中一个是负的。

请参见ExponentialGaussianModel(),它接受更多参数。

通过> pip Install lmfit安装库。