ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【python】pythonを使った最小二乗法でデータから数式を推定する方法

 今回はタイトルにある通り、pythonを使ってデータから数式を推定する方法の備忘録です。

 最小二乗法とか難しい言葉を使ってはいますが、要は「データをプロットすると一定の法則性があって数式化できそう」という場合に用いる方法になります。

 詳しい理論などは置いておいて、とりあえずデータから数式を推定するという部分のみの解説になるので注意してください。

 では始めます。


0:matplotlibを使えるようにする
 今回はmatplotlibというライブラリを使います。importしようとしてもエラーが発生する場合は以下の参考にさせていただいたページ様のやり方で解決できます。

 要は以下のコマンドを実行して、そのファイルの中身を開いて「backend : macosx」となっているところを「backend : Tkagg」に書き換えるというだけです。

$ python -c "import matplotlib;print(matplotlib.matplotlib_fname())"

 この方法はMacOSのみの場合なので、Windowsでエラーが出て使えない場合は各自で調べるようお願いします。


1:データの準備
 それでは本筋に入っていきますが、事前準備としてデータを準備します。今回は以下の適当なデータを使うことにします。

・x.csv

180,211,224,235,247,256,265,271,275,279,284,287,289,292,295,296,298,300,301,302,304,302,306,306,308,309,309

・y.csv

357,337,329,322,315,311,305,301,298,295,293,293,290,288,286,287,285,284,282,282,282,281,281,279,279,278,279

 上記の2つのデータをそれぞれ「x.csv」、「y.csv」として保存したファイルを使って以降の話を進めていきます。


2:データのプロット
 まずは上記のデータをプロットしたグラフを作成して傾向を見てみます。

 以下のプログラムをコピペして「dataPlot.py」という名前で保存します。

・dataPlot.py

#
# データプロット
#
import numpy as np
from matplotlib import pyplot as plt

def main():
    X_CSV_PATH = "x.csv"
    Y_CSV_PATH = "y.csv"
    SAVE_NAME = "result.png"

    x, y = loadCSVFile(X_CSV_PATH, Y_CSV_PATH) # CSVファイルを読み込み
    plt.scatter(x , y)     # データプロット
    plt.savefig(SAVE_NAME) # グラフを画像として保存

# CSVファイルを読み込む関数
def loadCSVFile(x_path, y_path):
    try:
        x_csv = np.loadtxt(x_path, delimiter=',')
        y_csv = np.loadtxt(y_path, delimiter=',')
    except Exception as e:
        raise e
    return x_csv, y_csv

if __name__ == '__main__':
    main()

 少し解説すると、「x_csv = np.loadtxt(x_path, delimiter=',')」、「y_csv = np.loadtxt(y_path, delimiter=',')」のところで第一引数のファイルパスをCSVとして読み込んでnumpyの配列として取得しています。この取得したデータを「plt.scatter(x , y)」に入れてグラフにプロットし、「plt.savefig(SAVE_NAME)」で画像として保存しています。

 pythonのファイルを作成したら、次は以下のように同じ階層にx.csvとy.csvを配置します。
f:id:rikoubou:20190228172205p:plain

 その状態でdataPlot.pyを実行すると「result.png」という名前でグラフの画像ファイルが作成されます。
f:id:rikoubou:20190228172301p:plain

 このグラフの傾向を見ると、直線の一次関数(y = ax + b)に近い形でデータがプロットされていることがわかります。


3:数式の推定
 2でデータをプロットしたことにより、なんとなく一次関数っぽくなっていることがわかりました。
 ここからは実際に数式を推定していきます。

 数式の推定にはpythonのライブラリであるnumpyの「polyfit関数」を使えば計算できます。

coefficient = np.polyfit([Xデータ], [Yデータ], [次元数])

 第一引数にXのデータ、第二引数にYのデータ、第三引数に次元数(整数)を入力すれば係数を計算してくれます。例えば次元数を3にすると「y = ax^3 + bx^2 + cx + d」の数式の各係数a,b,c,dの4つを得ることができます。
 またこれらの係数を使った数式のグラフは以下の記述で作成できます。

plt.plot(x, np.poly1d(coefficient)(x)) # グラフを記述

 係数の計算方法とグラフの記述方法がわかったので、2のプログラムにこの係数を推定する記述を追加して計算してみます。

・dataPlot2.py

#
# データプロットと次元の計算
#
import numpy as np
from matplotlib import pyplot as plt

def main():
    DIMENSION = 1 # 次元数

    X_CSV_PATH = "x.csv"
    Y_CSV_PATH = "y.csv"
    SAVE_NAME = "result.png"

    x, y = loadCSVFile(X_CSV_PATH, Y_CSV_PATH) # CSVファイルを読み込み
    plt.scatter(x , y)     # データプロット

    coefficient = np.polyfit(x, y, DIMENSION) # 係数を計算
    print(coefficient) # 係数を確認

    plt.plot(x, np.poly1d(coefficient)(x)) # グラフを記述

    plt.savefig(SAVE_NAME) # グラフを画像として保存

# CSVファイルを読み込む関数
def loadCSVFile(x_path, y_path):
    try:
        x_csv = np.loadtxt(x_path, delimiter=',')
        y_csv = np.loadtxt(y_path, delimiter=',')
    except Exception as e:
        raise e
    return x_csv, y_csv

if __name__ == '__main__':
    main()

 実行すると以下のように係数とグラフが得られます。

・係数

[ -0.60376793 464.66578936]

・グラフ
f:id:rikoubou:20190228174553p:plain

 グラフを見る限り、割と合っていそうな感じがします。


 以上がpythonを使ってデータから数式を推定する方法です。

 あらかじめ係数を計算しておきcoefficientを定数として持っておけば、他のxのデータが入ってきても推定した数式からおおよそのyの値を得ることができます。ちょっとした傾向などから値を推測する程度であればこのやり方で十分かと思います。
 ライブラリを使って楽に数式を作れるのはありがたいですね(正直原理はちゃんと理解していないです…)。

 また参考資料で紹介しているサイト様で他の関数を使った数式の推定も行なっているので、気になる方はそちらも参照してください。

 最近はビッグデータだなんだと言われる時代でもあるので、ちょっとだけでもデータを使って色々できるようになりたいです。

 今回作成したプログラムと使用したCSVを公開しておきます。


・参考資料