sigmoidal回归与scipy,numpy,python等

时间:2010-11-29 21:15:02

标签: python statistics numpy scipy scientific-computing

我有两个变量(x和y)彼此有一些S形关系,我需要找到某种预测方程,这个方程可以让我在给定任何x值的情况下预测y的值。我的预测方程需要显示两个变量之间的某种S形关系。因此,我不能满足于产生线的线性回归方程。我需要看到两个变量图的右侧和左侧出现的斜率的逐渐曲线变化。

我在googling曲线回归和python之后开始使用numpy.polyfit,但这给了我可怕的结果,如果你运行下面的代码就可以看到。 任何人都可以告诉我如何重新编写下面的代码以获得我想要的S形回归方程式吗?

如果你运行下面的代码,你可以看到它给出了一个向下的抛物线,这不是我的变量之间的关系应该是什么样子。相反,我的两个变量之间应该存在更多的S形关系,但是与我在下面的代码中使用的数据紧密相符。下面代码中的数据来自大样本研究的手段,因此它们的统计功效比五个数据点所暗示的要多。我没有大样本研究的实际数据,但我确实有下面的方法和他们的标准偏差(我没有显示)。我更愿意用下面列出的平均数据绘制一个简单的函数,但如果复杂性会带来实质性的改进,代码可能会变得更加复杂。

如何更改我的代码以显示最适合的sigmoidal函数,最好使用scipy,numpy和python? 这是我的代码的当前版本,需要修复:

import numpy as np
import matplotlib.pyplot as plt

# Create numpy data arrays
x = np.array([821,576,473,377,326])
y = np.array([255,235,208,166,157])

# Use polyfit and poly1d to create the regression equation
z = np.polyfit(x, y, 3)
p = np.poly1d(z)
xp = np.linspace(100, 1600, 1500)
pxp=p(xp)

# Plot the results
plt.plot(x, y, '.', xp, pxp, '-')
plt.ylim(140,310)
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True)
plt.show()

以下编辑:(重新构建问题)

您的回答及其速度令人印象深刻。谢谢你,unutbu。 但是,为了产生更有效的结果,我需要重新构建我的数据值。这意味着将x值重新转换为max x值的百分比,同时将y值重新转换为原始数据中x值的百分比。我尝试使用您的代码执行此操作,并提出以下内容:

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

# Create numpy data arrays 
'''
# Comment out original data
#x = np.array([821,576,473,377,326]) 
#y = np.array([255,235,208,166,157]) 
'''

# Re-calculate x values as a percentage of the first (maximum)
# original x value above
x = np.array([1.000,0.702,0.576,0.459,0.397])

# Recalculate y values as a percentage of their respective x values
# from original data above
y = np.array([0.311,0.408,0.440,0.440,0.482])

def sigmoid(p,x): 
    x0,y0,c,k=p 
    y = c / (1 + np.exp(-k*(x-x0))) + y0 
    return y 

def residuals(p,x,y): 
    return y - sigmoid(p,x) 

p_guess=(600,200,100,0.01) 
(p,  
 cov,  
 infodict,  
 mesg,  
 ier)=scipy.optimize.leastsq(residuals,p_guess,args=(x,y),full_output=1,warning=True)  

'''
# comment out original xp to allow for better scaling of
# new values
#xp = np.linspace(100, 1600, 1500) 
'''

xp = np.linspace(0, 1.1, 1100) 
pxp=sigmoid(p,xp) 

x0,y0,c,k=p 
print('''\ 
x0 = {x0}
y0 = {y0}
c = {c}
k = {k}
'''.format(x0=x0,y0=y0,c=c,k=k)) 

# Plot the results 
plt.plot(x, y, '.', xp, pxp, '-') 
plt.ylim(0,1) 
plt.xlabel('x') 
plt.ylabel('y') 
plt.grid(True) 
plt.show()

您能告诉我如何解决此修订后的代码吗?
注意:通过重新投射数据,我基本上围绕z轴旋转了2d(x,y)sigmoid 180度。此外,1.000实际上不是x值的最大值。相反,1.000是最大测试条件下不同测试参与者的值范围的平均值。


下面的第二个编辑:

谢谢你,ubuntu。我仔细阅读了你的代码,并在scipy文档中查看了它的各个方面。由于你的名字好像是scipy文档的作者,我希望你能回答以下问题:

1。)leastsq()是否调用residuals(),然后返回输入y-vector和sigmoid()函数返回的y-vector之间的差异?如果是这样,它如何解释输入y向量和sigmoid()函数返回的y向量的长度差异?

2。)看起来我可以为任何数学方程式调用leastsq(),只要我通过残差函数访问该数学方程式,而残差函数又调用数学函数。这是真的吗?

3.)另外,我注意到p_guess与p具有相同数量的元素。这是否意味着p_guess的四个元素分别对应于x0,y0,c和k返回的值?

4。)作为参数发送到residuals()和sigmoid()的p是否与将由leastsq()输出的p相同,而leastsq()函数在返回之前在内部使用该p它?

5.。)p和p_guess可以有任意数量的元素,这取决于用作模型的方程的复杂性,只要p中的元素数等于p_guess中的元素数吗?

4 个答案:

答案 0 :(得分:36)

使用scipy.optimize.leastsq

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

def sigmoid(p,x):
    x0,y0,c,k=p
    y = c / (1 + np.exp(-k*(x-x0))) + y0
    return y

def residuals(p,x,y):
    return y - sigmoid(p,x)

def resize(arr,lower=0.0,upper=1.0):
    arr=arr.copy()
    if lower>upper: lower,upper=upper,lower
    arr -= arr.min()
    arr *= (upper-lower)/arr.max()
    arr += lower
    return arr

# raw data
x = np.array([821,576,473,377,326],dtype='float')
y = np.array([255,235,208,166,157],dtype='float')

x=resize(-x,lower=0.3)
y=resize(y,lower=0.3)
print(x)
print(y)
p_guess=(np.median(x),np.median(y),1.0,1.0)
p, cov, infodict, mesg, ier = scipy.optimize.leastsq(
    residuals,p_guess,args=(x,y),full_output=1,warning=True)  

x0,y0,c,k=p
print('''\
x0 = {x0}
y0 = {y0}
c = {c}
k = {k}
'''.format(x0=x0,y0=y0,c=c,k=k))

xp = np.linspace(0, 1.1, 1500)
pxp=sigmoid(p,xp)

# Plot the results
plt.plot(x, y, '.', xp, pxp, '-')
plt.xlabel('x')
plt.ylabel('y',rotation='horizontal') 
plt.grid(True)
plt.show()

产量

alt text

带有sigmoid参数

x0 = 0.826964424481
y0 = 0.151506745435
c = 0.848564826467
k = -9.54442292022

请注意,对于较新版本的scipy(例如0.9),还有scipy.optimize.curve_fit函数比leastsq更容易使用。可以找到使用curve_fit拟合sigmoids的相关讨论here

编辑:添加了resize功能,以便原始数据可以重新调整并移动以适合任何所需的边界框。


  

“你的名字似乎像作家一样出现   scipy文档“

免责声明:我不是scipy文档的作者。我只是一个用户,也是一个新手。我对leastsq的了解大部分来自阅读this tutorial,由Travis Oliphant撰写。

  

1。)leastsq()调用残差(),然后返回差值   在输入y向量和。之间   由sigmoid()返回的y向量   功能

是的!准确。

  

如果是这样,它如何解释   输入长度的差异   y-向量和返回的y向量   sigmoid()函数?

长度相同:

In [138]: x
Out[138]: array([821, 576, 473, 377, 326])

In [139]: y
Out[139]: array([255, 235, 208, 166, 157])

In [140]: p=(600,200,100,0.01)

In [141]: sigmoid(p,x)
Out[141]: 
array([ 290.11439268,  244.02863507,  221.92572521,  209.7088641 ,
        206.06539033])

关于Numpy的一个奇妙的事情是它允许你编写在整个数组上运行的“矢量”方程。

y = c / (1 + np.exp(-k*(x-x0))) + y0

可能看起来适用于浮点数(实际上它会)但是如果你使x成为一个numpy数组,ckx0,{{1然后,等式将y0定义为与y形状相同的numpy数组。所以x返回一个numpy数组。在numpybook中有一个更完整的解释如何工作(严格的numpy用户必读)。

  

2。)看起来我可以为任何数学方程式调用leastsq(),只要我   通过a访问该数学方程式   残差函数,反过来   调用数学函数。这是真的吗?

真。 sigmoid(p,x)尝试最小化残差平方和(差异)。它搜索参数空间(leastsq的所有可能值),寻找最小化该平方和的p。发送到p的{​​{1}}和x是您的原始数据值。他们是固定的。他们不会改变。这是y s(sigmoid函数中的参数)residuals试图最小化。

  

3.)另外,我注意到p_guess与p具有相同数量的元素。是否   这意味着四个要素   p_guess按顺序对应,   分别返回值   由x0,y0,c和k?

确实如此!与Newton的方法一样,p需要leastsq的初始猜测。您将其作为leastsq提供。当你看到

p

你可以认为作为最小化算法(实际上是Levenburg-Marquardt算法)的一部分作为第一遍,最小规则调用p_guess。 注意

之间的视觉相似性
scipy.optimize.leastsq(residuals,p_guess,args=(x,y))

residuals(p_guess,x,y)

它可以帮助您记住(residuals,p_guess,args=(x,y)) 的参数的顺序和含义。

residuals(p_guess,x,y) ,像leastsq一样返回一个numpy数组。数组中的值是平方的,然后求和。这是要击败的数字。然后随residuals查找一组最小化sigmoid的值,p_guess会发生变化。

  

4。)p作为参数发送到残差()和   sigmoid()的功能与p相同   将由leastsq()输出,并且   leastsq()函数正在使用该p   在返回之前内部?

嗯,不完全是。如您所知,leastsq随着residuals(p_guess,x,y)搜索最小化p_guess的{​​{1}}值而变化。发送到leastsq的{​​{1}}(呃,p)形状与residuals(p,x,y)返回的p形状相同。显然,这些数值应该是不同的,除非你是一个猜测者的地狱:)

  

5.。)p和p_guess可以有任意数量的元素,具体取决于   所用方程的复杂性   作为模型,只要数量   p中的元素等于数字   p_guess中的元素?

是。我没有对非常大量的参数进行压力测试p_guess,但它是一个非常强大的工具。

答案 1 :(得分:2)

我认为你不会用任何程度的多项式拟合得到好结果 - 因为 对于足够大和小的X,所有多项式都会进入无穷大,但是S形曲线将渐近地在每个方向上接近某个有限值。

我不是Python程序员,所以我不知道numpy是否有更通用的曲线拟合 常规。如果你必须自己动手,也许Logistic regression上的这篇文章会给你一些想法。

答案 2 :(得分:1)

对于Python中的逻辑回归,scikits-learn公开了高性能拟合代码:

http://scikit-learn.sourceforge.net/modules/linear_model.html#logistic-regression

答案 3 :(得分:1)

正如@unutbu在scipy上面指出的那样,现在提供了scipy.optimize.curve_fit,它具有一个不太复杂的调用。如果有人想快速了解一下这些过程中的相同过程,我将在下面提供一个最小的示例:

def sigmoid(x, k, x0):

    return 1.0 / (1 + np.exp(-k * (x - x0)))

# Parameters of the true function
n_samples = 1000
true_x0 = 15
true_k = 1.5
sigma = 0.2

# Build the true function and add some noise
x = np.linspace(0, 30, num=n_samples)
y = sigmoid(x, k=true_k, x0=true_x0) 
y_with_noise = y + sigma * np.random.randn(n_samples)

# Sample the data from the real function (this will be your data)
some_points = np.random.choice(1000, size=30)  # take 30 data points
xdata = x[some_points]
ydata = y_with_noise[some_points]

# Fit the curve
popt, pcov = curve_fit(return_sigmoid, xdata, ydata)
estimated_k, estimated_x0 = popt

# Plot the fitted curve
y_fitted = sigmoid(x, k=estimated_k, x0=estimated_x0)

# Plot everything for illustration
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y_fitted, '--', label='fitted')
ax.plot(x, y, '-', label='true')
ax.plot(xdata, ydata, 'o', label='samples')

ax.legend()

其结果如下图所示:

enter image description here