SciPy + Numpy:找到S形曲线的斜率

时间:2014-10-28 11:30:55

标签: python numpy curve-fitting least-squares

我有一些遵循sigmoid分布的数据,如下图所示:Sigmoid data for 2003

在对数据进行标准化和缩放后,我使用scipy.optimize.curve_fit和一些初始参数调整了底部的曲线:

popt, pcov = curve_fit(sigmoid_function, xdata, ydata, p0 = [0.05, 0.05, 0.05])
>>> print popt
[  2.82019932e+02  -1.90996563e-01   5.00000000e-02]

所以popt,根据the documentation,返回*“参数的最佳值,以便f(xdata, popt) - ydata的平方误差之和最小化”< / em>的。我在这里理解,没有用curve_fit计算斜率,因为我认为这条平缓曲线的斜率是282,也不是负数。

然后我尝试使用scipy.optimize.leastsq,因为文档说它返回“解决方案(或不成功调用的最后一次迭代的结果)。”,所以我认为斜率将被退回。像这样:

p, cov, infodict, mesg, ier = leastsq(residuals, p_guess, args = (nxdata, nydata), full_output=True)
>>> print p
Param(x0=281.73193626250207, y0=-0.012731420027056234, c=1.0069006606656596, k=0.18836680131910222)

但同样,我没有得到我的预期。 curve_fitleastsq返回了几乎相同的值,我猜这并不奇怪,因为curve_fit正在使用最小二乘法的实现来查找曲线。但没有斜坡......除非我忽略了什么。

那么,如何计算一个点的斜率,比如,X = 285和Y = 0.5?

我试图避免手动方法,例如calculating the derivative,例如,(285.5,0.55)和(284.5,0.45),并减去并除以结果等等。我想知道是否有更自动的方法。

谢谢大家!

编辑#1

这是我的“sigmoid_function”,由curve_fit和leastsq方法使用:

def sigmoid_function(xdata, x0, k, p0): # p0 not used anymore, only its components (x0, k)
    # This function is called by two different methods: curve_fit and leastsq,
    # this last one through function "residuals". I don't know if it makes sense
    # to use a single function for two (somewhat similar) methods, but there 
    # it goes.

    # p0:
    #   + Is the initial parameter for scipy.optimize.curve_fit. 
    #   + For residuals calculation is left empty
    #   + It is initialized to [0.05, 0.05, 0.05]
    # x0:
    #   + Is the convergence parameter in X-axis and also the shift
    #   + It starts with 0.05 and ends up being around ~282 (days in a year)
    # k:
    #   + Set up either by curve_fit or leastsq
    #   + In least squares it is initially fixed at 0.5 and in curve_fit
    #   + to 0.05. Why? Just did this approach in two different ways and 
    #   + it seems it is working. 
    #   + But honestly, I have no clue on what it represents
    # xdata: 
    #   + Positions in X-axis. In this case from 240 to 365

# Finally I changed those parameters as suggested in the answer. 
# Sigmoid curve has 2 degrees of freedom, therefore, the initial 
# guess only needs to be this size. In this case, p0 = [282, 0.5]


    y = np.exp(-k*(xdata-x0)) / (1 + np.exp(-k*(xdata-x0)))
    return y

def residuals(p_guess, xdata, ydata):
    # For the residuals calculation, there is no need of setting up the initial parameters
    # After fixing the initial guess and sigmoid_function header, remove [] 
    # return ydata - sigmoid_function(xdata, p_guess[0], p_guess[1], [])
    return ydata - sigmoid_function(xdata, p_guess[0], p_guess[1], [])

如果我在描述参数或混淆技术术语时犯了错误,我很抱歉。我很笨,我多年没学过数学,所以我又回来了。

那么,再次,您对计算此数据集的X = 285,Y = 0.5(或多或少中间点)的斜率有何建议?谢谢!!

编辑#2

感谢Oliver W.,我按照他的建议更新了我的代码并理解了问题。

我还没有完全掌握最后的细节。显然,curve_fit返回一个popt数组(x0,k),其中包含拟合的最佳参数:

  • x0似乎是通过指示曲线的中心点来改变曲线
  • k参数是y = 0.5时的斜率,也是曲线的中心(我想!)

为什么如果sigmoid函数是一个增长函数,popt中的导数/斜率是负的?它有意义吗?

我使用sigmoid_derivative来计算斜率,是的,我获得的结果与popt相同,但带有正号。

# Year 2003, 2005, 2007. Slope in midpoint.
k = [-0.1910, -0.2545, -0.2259] # Values coming from popt
slope = [0.1910, 0.2545, 0.2259] # Values coming from sigmoid_derivative function

我知道这有点高峰,因为我可以同时使用它们。相关数据在那里,但有负号,但我想知道为什么会发生这种情况。

因此,只有当我需要知道除y = 0.5 之外的其他点的斜率时,才需要按照您的建议计算导数函数。仅限中点,我可以使用popt

感谢您的帮助,这为我节省了很多时间。 : - )

1 个答案:

答案 0 :(得分:2)

你永远不会使用你传递给sigmoid函数的参数p0。因此,曲线拟合不具有找到收敛的任何好的量度,因为它可以取该参数的任何值。你应该首先重写你的sigmoid函数:

def sigmoid_function(xdata, x0, k):

    y = np.exp(-k*(xdata-x0)) / (1 + np.exp(-k*(xdata-x0)))
    return y

这意味着你的模型(sigmoid)只有两个自由度。这将在popt

中返回
initial_guess = [282, 1]  # (x0, k): at x0, the sigmoid reaches 50%, k is slope related
popt, pcov = curve_fit(sigmoid_function, xdata, ydata, p0=initial_guess)

现在popt将是一个元组(或2个值的数组),是最好的x0k

为了在任何一点得到这个函数的斜率,说实话,我只是象征性地计算导数,因为sigmoid不是那么难的函数。你最终会得到:

def sigmoid_derivative(x, x0, k):
    f = np.exp(-k*(x-x0))
    return -k / f

如果曲线拟合的结果存储在popt中,您可以轻松地将其传递给此函数:

print(sigmoid_derivative(285, *popt))

将返回x=285的衍生物。但是,因为你专门询问中点,所以当x==x0y==.5时,你会看到(来自sigmoid_derivative)导数只有-k,可以立即观察到来自您已经获得的curve_fit输出。在您显示的输出中,大约是0.19。