web-dev-qa-db-ja.com

線形回帰でゼロ切片を強制する方法は?

私は少し初心者ですので、この質問にすでに回答されている場合はお詫びします。探していたので、探していたものを具体的に見つけることができませんでした。

次の形式の線形データが多少あります

x = [0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 2.0, 4.0, 6.0, 8.0, 10.0, 20.0, 40.0, 60.0, 80.0]
y = [0.50505332505407008, 1.1207373784533172, 2.1981844719020001, 3.1746209003398689, 4.2905482471260044, 6.2816226678076958, 11.073788414382639, 23.248479770546009, 32.120462301367183, 44.036117671229206, 54.009003143831116, 102.7077685684846, 185.72880217806673, 256.12183145545811, 301.97120103079675]

使ってます scipy.optimize.leastsqこれに線形回帰を当てはめるには:

def lin_fit(x, y):
    '''Fits a linear fit of the form mx+b to the data'''
    fitfunc = lambda params, x: params[0] * x + params[1]    #create fitting function of form mx+b
    errfunc = lambda p, x, y: fitfunc(p, x) - y              #create error function for least squares fit

    init_a = 0.5                            #find initial value for a (gradient)
    init_b = min(y)                         #find initial value for b (y axis intersection)
    init_p = numpy.array((init_a, init_b))  #bundle initial values in initial parameters

    #calculate best fitting parameters (i.e. m and b) using the error function
    p1, success = scipy.optimize.leastsq(errfunc, init_p.copy(), args = (x, y))
    f = fitfunc(p1, x)          #create a fit with those parameters
    return p1, f    

そして、それは美しく機能します(ここでscipy.optimizeを使用するのが適切かどうかはわかりませんが、少しオーバーしているかもしれません)。

ただし、データポイントの配置方法が原因で、0でのy軸のインターセプトは得られません。ただし、この場合はゼロでなければならないことを知っていますが、if x = 0 than y = 0

これを強制する方法はありますか?

19
Kyra Tafar

これらのモジュールは得意ではありませんが、統計にはある程度の経験があるため、ここに表示されます。からフィット関数を変更する必要があります

fitfunc = lambda params, x: params[0] * x + params[1]  

に:

fitfunc = lambda params, x: params[0] * x 

次の行も削除します。

init_b = min(y) 

次の行を次のように変更します。

init_p = numpy.array((init_a))

これにより、y切片を生成している2番目のパラメーターが取り除かれ、フィットされたラインがOriginを通過します。コードの残りの部分で、これを行うために行う必要があるいくつかのマイナーな変更があるかもしれません。

しかし、はい、このように2番目のパラメーターを取り除くだけでこのモジュールが機能するかどうかはわかりません。この変更を受け入れることができるかどうかは、モジュールの内部動作に依存します。たとえば、パラメーターのリストであるparamsが初期化されている場所がわからないので、これを行うだけで長さが変わるかどうかはわかりません。

余談ですが、おっしゃったように、これは実際には勾配だけを最適化するためのちょっとやり過ぎな方法だと思います。あなたは線形回帰を少し読んで、エンベロープ計算の裏側の後で自分で行う小さなコードを書くことができます。本当にシンプルで簡単です。実際、私はいくつかの計算を行っただけで、最適化された勾配は<xy>/<x^2>、つまり、x * y積の平均をx ^ 2の平均で割ったもの。

9
Abhranil Das

@AbhranilDasが述べたように、線形メソッドを使用してください。 scipy.optimize.lstsqのような非線形ソルバーは必要ありません。

通常、numpy.polyfitを使用してデータに線を合わせますが、この場合は切片をゼロに設定するため、numpy.linalg.lstsqを直接使用する必要があります。

簡単な例として:

import numpy as np
import matplotlib.pyplot as plt

x = np.array([0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 2.0, 4.0, 6.0, 8.0, 10.0, 
              20.0, 40.0, 60.0, 80.0])

y = np.array([0.50505332505407008, 1.1207373784533172, 2.1981844719020001,
              3.1746209003398689, 4.2905482471260044, 6.2816226678076958,
              11.073788414382639, 23.248479770546009, 32.120462301367183, 
              44.036117671229206, 54.009003143831116, 102.7077685684846, 
              185.72880217806673, 256.12183145545811, 301.97120103079675])

# Our model is y = a * x, so things are quite simple, in this case...
# x needs to be a column vector instead of a 1D vector for this, however.
x = x[:,np.newaxis]
a, _, _, _ = np.linalg.lstsq(x, y)

plt.plot(x, y, 'bo')
plt.plot(x, a*x, 'r-')
plt.show()

enter image description here

33
Joe Kington