最小2乗法


ラグランジュ補間、スプライン補間と続いて今度は最小2乗法をやってみた。データからそれっぽい直線や曲線を求めるやつ。次のサイトが参考になる。

http://szksrv.isc.chubu.ac.jp/lms/lms1.html
http://www.geocities.jp/supermisosan/saisyounizyouhou2.html

pylabについては次のサイトが参考になる。

http://www.ike-dyn.ritsumei.ac.jp/~uchida/scipy-lecture-notes/intro/matplotlib/matplotlib.html


コードはこんな感じ。今回はpylabを使ったのでちょっとだけ長め。

import pylab

def lsm(x, y, m):    #最小2乗法
	matrix = []
	ans = []
	for i in range(m+1):
		p = []
		t = 0.0
		for j in range(i, i + m + 1):
			s = 0.0
			for k in x:
				s += k ** j
			p.append(s)
		for j in range(len(x)):
			t += y[j] * (x[j] ** i)
		p.append(t)    #各行の右端の列には各行の式の解が入っている
		matrix.append(p)

	hakidashi(matrix, m+1)

	for i in matrix:
		ans.append(i[-1])

	return ans    #ansには次数の低い順に係数が格納されている

def hakidashi(matrix, dim):    #掃き出し法
	for i in range(dim):
		num = matrix[i][i]
		for j in range(dim + 1):
			matrix[i][j] = matrix[i][j] / num
		for j in range(dim):
			if i == j:
				pass
			else:
				a = matrix[j][i]
				for k in range(i, dim + 1):
					matrix[j][k] = matrix[j][k]- a * matrix[i][k]

def show(numlist, m):    #求めた関数を表示
	a = m
	ch = 'y = '
	while a >= 0:
		if a > 0:
			ch += '%f*x^%d' % (numlist[a], a)
			if numlist[a - 1] > 0:
				ch += '+'
		else:
			ch += '%f' % numlist[a]
		a -= 1
	print ch
	return ch

def make_graph(datx, daty, start, end, numlist, m, formula):    #グラフ描画
	a = m
	y = 0
	x = pylab.arange(start, end , 0.01)    #0.01刻みでyを求めることにする
	while a >= 0:    #求めた係数を使って関数を作る
		y += numlist[a] * (x ** a)
		a -= 1
	pylab.plot(x, y, label = formula)
	pylab.plot(datx, daty, 'ro', label = 'data')
	#pylab.legend(loc = 'upper right')    #凡例をつけることができる
	pylab.show()

def main():
	x = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
	y = [2.4824,1.9975,1.6662,1.3775,1.0933,0.7304,0.4344,0.2981,-0.0017,-0.0026]
	m = 2    #近似する関数の次数を設定
	ans = lsm(x, y, m)
	ch = show(ans, m)
	make_graph(x, y, x[0], x[-1] , ans, m, ch)

if __name__ == '__main__':
	main()


実行結果はこんな感じ。

y = 1.581667*x^2-4.551476*x^1+2.901920



今度はmの値をいろいろ変えてグラフを書いてみた。


・m = 1 のとき

直線。


・m = 6 のとき

なるべくデータの点を通るような感じになっている。


・m = 12 のとき

右の方がちょっとおかしくなってきた。


・m = 20 のとき

右の方がかなりおかしくなってしまった。


やっぱり求める関数の次数はなるべく低い方がいいことがわかる。
某教科書ではmはせいぜい5ぐらいまでがいい、と書かれている。