线性回归ODR失败

时间:2018-10-09 14:15:55

标签: python scipy linear-regression

按照this answer中的建议,我使用了beta0值的几种组合,如此处所示,是polyfit的值。

此示例已更新,以显示X与Y的相对比例值的影响:

from random import random, seed
from scipy import polyfit
from scipy import odr
import numpy as np
from matplotlib import pyplot as plt

seed(1)
X = np.array([random() for i in range(1000)])
Y = np.array([i + random()**2 for i in range(1000)])

for num in xrange(1, 5):
    plt.subplot(2, 2, num)
    plt.title('X range is %.1f times Y' % (float(100 / max(X))))
    X *= 10
    z = np.polyfit(X, Y, 1)
    plt.plot(X, Y, 'k.', alpha=0.1)

    # Fit using odr
    def f(B, X):
        return B[0]*X + B[1]    

    linear = odr.Model(f)
    mydata = odr.RealData(X, Y)
    myodr = odr.ODR(mydata, linear, beta0=z)
    myodr.set_job(fit_type=0)
    myoutput = myodr.run()
    a, b = myoutput.beta
    sa, sb = myoutput.sd_beta
    xp = np.linspace(plt.xlim()[0], plt.xlim()[1], 1000)
    yp = a*xp+b
    plt.plot(xp, yp, label='ODR')
    yp2 = z[0]*xp+z[1]
    plt.plot(xp, yp2, label='polyfit')
    plt.legend()
    plt.ylim(-1000, 2000)
plt.show()

似乎没有beta0的组合有帮助...使polyfit和ODR拟合相似的唯一方法是交换X和Y,或者如此处所示,以增加X相对于Y的值范围,但仍然没有真的是一个解决方案:)

new example

===编辑===

我不希望ODR与polyfit相同。我展示polyfit只是为了强调ODR拟合是错误的,这不是数据问题。

===解决方案===

感谢@ norok2答案:

from random import random, seed
from scipy import polyfit
from scipy import odr
import numpy as np
from matplotlib import pyplot as plt
seed(1)
X = np.array([random() / 1000 for i in range(1000)])
Y = np.array([i + random()**2 for i in range(1000)])
plt.figure(figsize=(12, 12))
for num in xrange(1, 10):
    plt.subplot(3, 3, num)
    plt.title('Y range is %.1f times X' % (float(100 / max(X))))
    X *= 10
    z = np.polyfit(X, Y, 1)
    plt.plot(X, Y, 'k.', alpha=0.1)
    # Fit using odr
    def f(B, X):
        return B[0]*X + B[1]    
    linear = odr.Model(f)
    mydata = odr.RealData(X, Y, 
                          sy=min(1/np.var(Y), 1/np.var(X)))  # here the trick!! :)
    myodr = odr.ODR(mydata, linear, beta0=z)
    myodr.set_job(fit_type=0)
    myoutput = myodr.run()
    a, b = myoutput.beta
    sa, sb = myoutput.sd_beta
    xp = np.linspace(plt.xlim()[0], plt.xlim()[1], 1000)
    yp = a*xp+b
    plt.plot(xp, yp, label='ODR')
    yp2 = z[0]*xp+z[1]
    plt.plot(xp, yp2, label='polyfit')

    plt.legend()
    plt.ylim(-1000, 2000)
plt.show()

example3

2 个答案:

答案 0 :(得分:2)

polyfit()与正交距离回归(ODR)拟合之间的主要区别在于,在x的误差可忽略的假设下,多边形拟合有效。如果违反了此假设(就像您的数据中那样),则不能期望这两种方法会产生相似的结果。 特别是,ODR()对您指定的错误非常敏感。 如果您未指定任何错误/权重,它将为1x分配一个y值,这意味着x和{{1 }}将影响结果(所谓的数值条件)。

相反,y在计算拟合度之前,对数据进行了某种形式的白化处理(请参见其source code的第577行),以提供更好的数值条件。

因此,如果您希望polyfit()ODR()匹配,则可以简单地微调polyfit()上的错误以更改数值条件。 我测试了此方法适用于您Y的{​​{1}}和1e-10之间的任何数值条件(在您的示例中为1e10Y)。

/ 10.

收件人:

1e-1

(编辑:请注意,上面一行有错字)

我测试了它适用于mydata = odr.RealData(X, Y) # equivalent to: odr.RealData(X, Y, sx=1, sy=1) 的{​​{1}}和mydata = odr.RealData(X, Y, sx=1, sy=1/np.var(Y)) 之间的任何数值条件(在您的示例中为1e-101e10)。

请注意,这仅适用于条件良好的拟合。

答案 1 :(得分:0)

我无法在注释中设置源代码的格式,因此请将其放在此处。该代码使用ODR来计算拟合统计量,请注意具有“ odr的参数顺序”的行,以便我对我的“实际”函数的ODR调用使用包装函数。

from scipy.optimize import curve_fit
import numpy as np
import scipy.odr
import scipy.stats

x = np.array([5.357, 5.797, 5.936, 6.161, 6.697, 6.731, 6.775, 8.442, 9.861])
y = np.array([0.376, 0.874, 1.049, 1.327, 2.054, 2.077, 2.138, 4.744, 7.104])

def f(x,b0,b1):
    return b0 + (b1 * x)


def f_wrapper_for_odr(beta, x): # parameter order for odr
    return f(x, *beta)

parameters, cov= curve_fit(f, x, y)

model = scipy.odr.odrpack.Model(f_wrapper_for_odr)
data = scipy.odr.odrpack.Data(x,y)
myodr = scipy.odr.odrpack.ODR(data, model, beta0=parameters,  maxit=0)
myodr.set_job(fit_type=2)
parameterStatistics = myodr.run()
df_e = len(x) - len(parameters) # degrees of freedom, error
cov_beta = parameterStatistics.cov_beta # parameter covariance matrix from ODR
sd_beta = parameterStatistics.sd_beta * parameterStatistics.sd_beta
ci = []
t_df = scipy.stats.t.ppf(0.975, df_e)
ci = []
for i in range(len(parameters)):
    ci.append([parameters[i] - t_df * parameterStatistics.sd_beta[i], parameters[i] + t_df * parameterStatistics.sd_beta[i]])

tstat_beta = parameters / parameterStatistics.sd_beta # coeff t-statistics
pstat_beta = (1.0 - scipy.stats.t.cdf(np.abs(tstat_beta), df_e)) * 2.0    # coef. p-values

for i in range(len(parameters)):
    print('parameter:', parameters[i])
    print('   conf interval:', ci[i][0], ci[i][1])
    print('   tstat:', tstat_beta[i])
    print('   pstat:', pstat_beta[i])
    print()