python - SciPy + Numpy : Finding the slope of a sigmoid curve

标签 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 的平方误差之和最小化”。我在这里理解,没有用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 和 lesssq 方法使用:

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], [])

如果我在描述参数时犯了错误或混淆了技术术语,我很抱歉。我对 numpy 很陌生,而且我已经很多年没有学习数学了,所以我正在重新学习。

那么,对于计算此数据集的 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

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

最佳答案

您从未使用过传递给 sigmoid 函数的参数 p0。因此,曲线拟合不会有任何好的方法来找到收敛性,因为它可以为此参数取任何值。您应该首先像这样重写 sigmoid 函数:

def sigmoid_function(xdata, x0, k):

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

这意味着您的模型(S形)只有两个自由度。这将在 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。

关于python - SciPy + Numpy : Finding the slope of a sigmoid curve,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26607237/

相关文章:

python - 如何在不使用 html 表单的情况下将文件发布到 django

python - 将元组存储为模型中的字段

python - 如何在 sudo 上运行 Anaconda Python

python - 计算多项 Logit 模型预测概率

r - 在 R 中使用 gamlss::lms 选择百分位曲线

python - 根据条件对数据帧行进行分组和平均

python - 使用numpy区分两个 "symmetrical"数组

python - OpenCV python canny 所需参数 'threshold2' (pos 4) 未找到

python - opt.curve_fit 只有一个参数

r - R中的曲线拟合