質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.30%

Q&A

解決済

2回答

525閲覧

python 最小二乗法によるデータの多項式近似の計算エラー

Saku

総合スコア1

0グッド

0クリップ

投稿2023/12/26 12:27

編集2023/12/27 01:05

実現したいこと

多項式の係数を正しく導出する.

前提

pythonでcsvからデータを読み込み,
lu分解,前進代入,後退代入を用いて最小二乗法を行い,
得られた係数から曲線をプロットするプログラムを作りました.
しかし,検算のためにライブラリ(polyfit)を使用し求めたところ
自作したものから導出された係数と違う値が出ました.
1次式,2次式は正しくもとまりましたが,
3次式以降の係数が間違って導出されてしまいます.

発生している問題・エラーメッセージ

3次式近似の時の自作プログラムから導出された値 -62289409924.034035 93004892.05532321 -46288.47113927059 7.679190499441964 polyfit関数から求められた値 -3.12020048e+10 4.65818816e+07 -2.31807582e+04 3.84516090e+00

該当のソースコード

python

1import matplotlib.pyplot as plt 2import numpy as np 3import csv 4 5def read(filename): 6 csv_file = open(filename,'r') 7 x = [] 8 y = [] 9 for row in csv.reader(csv_file): 10 x.append(float(row[0])) 11 y.append(float(row[1])) 12 return x,y 13 14def a_list(x,n): 15 m = len(x) #データの個数 16 a = [[float(0.0) for i in range(n + 1)] for j in range(n + 1)] 17 for i in range(n + 1): 18 for j in range(n + 1): #n 19 sum_ = 0.0 20 for k in range(m): 21 sum_ += pow(x[k],i + j) 22 a[i][j] = sum_ 23 return a 24 25def b_list(x,y,n): 26 n += 1 27 b = [float(0.0) for i in range(n)] 28 m = len(x) 29 for i in range(n): 30 sum_ = 0.0 31 for k in range(m): 32 sum_ += y[k] * pow(x[k],i) 33 b[i] = sum_ 34 return b 35 36#a行列をLU分解 37def luBunkai(data,n): 38 n += 1 39 a = [[float(0.0) for i in range(n)] for j in range(n)] 40 b = [[float(0.0) for i in range(n)] for j in range(n)] 41 for i in range(n): 42 a[i][i] = 1.0 43 for j in range(n): 44 for i in range(j + 1): #j+1 45 sum_ = 0.0 46 for k in range(i): #i 47 sum_ += a[i][k] * b[k][j] 48 b[i][j] = data[i][j] - sum_ 49 50 for i in range(j + 1,n): #j #j+1 51 sum_ = 0.0 52 for k in range(j): #j 53 sum_ += a[i][k] * b[k][j] 54 a[i][j] = (data[i][j] - sum_) / b[j][j] #j 55 return a,b 56 57#前進代入 58def zensin(l,b,n): 59 n += 1 60 y = [float(0.0) for i in range(n)] 61 y[0] = b[0] / l[0][0] 62 for i in range(1,n): 63 sum_ = 0.0 64 for j in range(i): #i 65 sum_ += l[i][j] * y[j] 66 y[i] = (b[i] - sum_) / l[i][i] 67 return y 68 69#後退代入 70def koutai(y,u,n): 71 n += 1 72 x = [float(0.0) for i in range(n)] 73 x[n - 1] = y[n - 1] / u[n - 1][n - 1] 74 for i in range(n - 2,-1,-1): #n-1 75 sum_ = 0.0 76 for j in range(i + 1,n): #n 77 sum_ += u[i][j] * x[j] 78 x[i] = (y[i] - sum_) / u[i][i] 79 80 print("係数") 81 for _ in x: 82 print(_) 83 84 return x 85 86#得られた係数でプロットする関数 87def fit_plot(c,x,y,n): 88 n += 1 89 fit = 0.0 90 p = np.linspace(1996,2022,1000) 91 for i in range(n): 92 fit += c[i] * pow(p,i) 93 plt.plot(p,fit,label = "n =" + str(n - 1)) 94 plt.xlabel("year") 95 plt.ylabel("y") 96 plt.legend() 97 return 0 98 99#1~nまでプロットする 100#最終的に1~nとするためfor 101for n in range(3,4): 102 x,y = read("data.csv") 103 a = a_list(x,n) 104 b = b_list(x,y,n) 105 l,u = luBunkai(a,n) 106 lu_y = zensin(l,b,n) 107 c = koutai(lu_y,u,n) 108 fit_plot(c,x,y,n) 109plt.xlim(1995,2022) 110plt.ylim(3000,20000) 111plt.scatter(x,y,label = "data") 112plt.show() 113 114#検算 115for i in range(3,4): 116 p4 = np.polyfit(x, y, i) 117 print('ライブラリの値{0}'.format(p4)) 118 plt.plot(x, np.poly1d(np.polyfit(x, y, i))(x)) 119plt.xlim(1995,2022) 120plt.ylim(3000,20000) 121plt.scatter(x,y,label = "data") 122plt.show() 123 124"""data.datの内容 1251996,6216.833333333333 1261997,10050.819444444443 1271998,12947.568287037036 1281999,11282.380690586418 1292000,12065.031724215534 1302001,9488.919310351295 1312002,11673.326609195943 1322003,14021.860550766329 1332004,16618.821712563862 1342005,16300.151809380322 1352006,17646.09598411503 1362007,21248.091332009586 1372008,13241.674277667466 1382009,12615.556189805624 1392010,16353.713015817135 1402011,9480.726084651427 1412012,5124.893840387619 1422013,4430.157820032302 1432014,4850.513151669358 1442015,14092.876095972446 1452016,18467.406341331036 1462017,20329.700528444253 1472018,17423.975044037023 1482019,17081.74792033642 1492020,14347.562326694702 1502021,18582.130193891226 1512022,11001.927516157602 152""" 153 154

試したこと

プログラム中のa,b,l,u,y,xをトレースして,lu分解,前進代入,後退代入が間違っていないことを確認しました.
data.datを用いて最小二乗法を行う外部のツールで計算したところ,polyfit関数と同じ値になりました.

追記

2023/12/27
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

演算をdecimal型にしたところ,導出された値がライブラリに近い値となったため解決とします.(値に誤差はあります)
原因は精度の問題だったと思います.

投稿2023/12/30 07:13

Saku

総合スコア1

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

winterboum

2023/12/31 00:43

おめでとう。 その精度の問題が私の回答です。 もう解決したの( と codeをきちんと読み解く時間ないの)で簡単に言うと、 X 1996〜2022、 Y 4000〜20000 の世界を 座標移動して X -13〜13、Y -12000 〜 12000 の世界にして計算すると精度を得するのです。 例えば精度8桁の計算機だったとすると 2022を3乗すると 8000000000位 になりますが13だと2197。8000000000 の上の桁は意味がなくて最後の4桁が重要ですが、8000000000 のままだと精度が足りなくなって下4桁部分が吐き出されてしまいます。
guest

0

気になることがあります。
データが 6216.833333333333 と16桁もあります。有効桁数そんなに無いでしょう?
通常の人が手にできる有効桁って4桁、精密な秤で5桁が良いところ。16桁というのは生の測定値ではなく計算結果ですよね? 計算の元になってる生データの有効桁数から計算結果の有効桁数を求めて、その桁で丸めるのが望ましいです。

で、プログラム
分散を求めるのに Σ (X**2) - 1/n (ΣX)**2 してますね?
筆算ならこれですが、計算機使うなら分散の定義通りに Σ(( x - xの平均) **2)でやる方が精度が出ます。
それが原因かどうかは試していただかないとなんとも、ですが。

投稿2023/12/26 13:43

winterboum

総合スコア23653

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

meg_

2023/12/26 22:31

重複投稿の様です。
Saku

2023/12/27 01:34 編集

データに関してはご指摘の通りで計算結果です. 11 y.append(round(float(row[1]))) とround関数で大まかに小数点以下を処理しましたが, ライブラリで求めた値には近づきませんでした. (追記 違いました.計算結果を丸めるということなので,丸めたものを計算することではなかったです) プログラムの方ですが,分散を求めるというのは具体的に何行目のプログラムでしょうか? a_listとb_listというのは,xとyの二乗誤差を最小にするために変微分して0とする という式から持ってきたものですが(質問に貼っておきます),そのあたりでしょうか. 理解不足で申し訳ありません.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問