Python 近似式を作成してグラフに記入する方法「Numpy×SciPy」

'21/10/04更新:汎用性を高めるため、関数の引数を変更しました。3つの引数は順に、pandas DataFrame, X軸に指定する列名, Y軸に指定する列名です。
  本記事では、下図3つのような近似式を作成する雛形コードを載せました。scipyのoptimize.curve_fitを利用します。

▼y = ax に近似したい場合(切片0)

f:id:HK29:20200215230156p:plain

▼ y = ax + b に近似したい場合

f:id:HK29:20200215230205p:plain

▼ y = ax^2 + bx + c に近似したい場合

f:id:HK29:20200215230217p:plain

■本プログラム

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import scipy as sp
from scipy import optimize
import math

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

import datetime
now = datetime.datetime.now()
now = now.strftime("%y%m%d")

# 近似式 y = ax 用の関数A:切片なしの1次式
def approximation_expression_A(x, a):
    return a * x

# 近似式 y = ax + b 用の関数AB:切片ありの1次式
def approximation_expression_AB(x, a, b):
    return a * x + b

# 近似式 y = ax^2 + bx + c 用の関数ABC:2次式
def approximation_expression_ABC(x, a, b, c):
    return a * pow(x, 2) + b * x + c

# 関数A:y = ax
def plot_reg_A(DF, X_name, Y_name):
    save_fname = X_name + Y_name + "_LinearRegression_A"
    
    # 近似式の作成
    popt, pcov = optimize.curve_fit(approximation_expression_A, DF[X_name], DF[Y_name])
    
    ax = plt.figure(num=0, dpi=120).gca() 
    ax.set_title("pred vs real ", fontsize=14)
    ax.set_xlabel(X_name, fontsize=16)
    ax.set_ylabel(Y_name, fontsize=16)
    rp = ax.scatter(x = X_name,
                    y = Y_name,
                    data = DF,
                    facecolors="none",
                    edgecolors='black') # purple

    # Xデータの最小値と最大値
    x_min = DF[X_name].min()
    x_max = DF[X_name].max()
    # Yデータの最小値と最大値
    y_min = DF[Y_name].min()
    y_max = DF[Y_name].max()
    # プロットするためのxデータの範囲を決める
    x_min = min(x_min, y_min)
    x_max = min(x_max, y_max)
    print(x_min)
    print(x_max)

    # グラフのレンジを決める
    x_range = x_max - x_min # データの範囲が100以下か以上かで分ける
    print("x_range = x_max - x_min = " + str(x_range))
    if x_max > 1:
        min_lim = 0
        if x_range <= 10:
            max_lim = math.floor(x_max + 1)
        else:
            max_lim = math.floor(x_max + 10)
        rp.axes.set_xlim(min_lim, max_lim)
        rp.axes.set_ylim(min_lim, max_lim)
    else:
        max_lim = 0
        if x_range <= 100:
            max_lim = math.floor(x_max - 1)
        else:
            max_lim = math.floor(x_max - 10)
        rp.axes.set_xlim(min_lim, max_lim)
        rp.axes.set_ylim(min_lim, max_lim)

    # 近似式プロットのためのデータを作成
    x_approximation = np.linspace(min_lim, max_lim, 10) # numpyでxデータを作成
    y_approximation = popt[0] * x_approximation # 近似式に代入してyデータを作成
    line_approximation = ax.plot(x_approximation, y_approximation,
                                 linestyle = 'dashed', linewidth = 3, color='r') 
    
    rp.axes.set_aspect('equal', adjustable='box')
    plt.grid(True)
    # 凡例を指定して記入する場合は、リストで指定する
    ax.legend([line_approximation[0]], ["y = {:.2f}x".format(popt[0])],
               loc='upper left',
               numpoints=1,
               fontsize=15)
    plt.tick_params(labelsize=15)
    plt.tight_layout() 
    plt.savefig(save_fname + '.png') 
    plt.close()

# 関数AB:y = ax + b
def plot_reg_AB(DF, X_name, Y_name):
    save_fname = X_name + Y_name + "_LinearRegression_AB"

    # 近似式の作成
    popt, pcov = optimize.curve_fit(approximation_expression_AB, DF[X_name], DF[Y_name])
    
    ax = plt.figure(num=0, dpi=120).gca() 
    #plt.rcParams["axes.labelsize"] = 15
    ax.set_title("pred vs real ", fontsize=14)
    ax.set_xlabel(X_name, fontsize=16)
    ax.set_ylabel(Y_name, fontsize=16)
    rp = ax.scatter(x = X_name,
                    y = Y_name,
                    data = DF,
                    facecolors="none",
                    edgecolors='black') # purple

    # Xデータの最小値と最大値
    x_min = DF[X_name].min()
    x_max = DF[X_name].max()
    # Yデータの最小値と最大値
    y_min = DF[Y_name].min()
    y_max = DF[Y_name].max()
    # プロットするためのxデータの範囲を決める
    x_min = min(x_min, y_min)
    x_max = min(x_max, y_max)
    print(x_min)
    print(x_max)

    # グラフのレンジを決める
    x_range = x_max - x_min # データの範囲が100以下か以上かで分ける
    print("x_range = x_max - x_min = " + str(x_range))
    if x_max > 1:
        min_lim = 0
        if x_range <= 10:
            max_lim = math.floor(x_max + 1)
        else:
            max_lim = math.floor(x_max + 10)
        rp.axes.set_xlim(min_lim, max_lim)
        rp.axes.set_ylim(min_lim, max_lim)
    else:
        max_lim = 0
        if x_range <= 100:
            max_lim = math.floor(x_max - 1)
        else:
            max_lim = math.floor(x_max - 10)
        rp.axes.set_xlim(min_lim, max_lim)
        rp.axes.set_ylim(min_lim, max_lim)

    # 近似式プロットのためのデータを作成
    x_approximation = np.linspace(min_lim, max_lim, 10) # numpyでxデータを作成
    y_approximation = popt[0] * x_approximation + popt[1] # 近似式に代入してyデータを作成
    line_approximation = ax.plot(x_approximation, y_approximation,
                                 linestyle = 'dashed', linewidth = 3, color='r') 
    
    rp.axes.set_aspect('equal', adjustable='box')
    plt.grid(True)
    # 凡例を指定して記入する場合は、リストで指定する
    ax.legend([line_approximation[0]], ["y = {0:.2f}x + {1:.2f}".format(popt[0], popt[1])],
               loc='upper left',
               numpoints=1,
               fontsize=15)
    plt.tick_params(labelsize=15)
    plt.tight_layout() 
    plt.savefig(save_fname + '.png') 
    plt.close()

# 関数ABC:y = ax^2 + bx + c
def plot_reg_ABC(DF, X_name, Y_name):
    save_fname = X_name + Y_name +  "_LinearRegression_ABC"

    # 近似式の作成
    popt, pcov = optimize.curve_fit(approximation_expression_ABC, DF[X_name], DF[Y_name])
    
    ax = plt.figure(num=0, dpi=120).gca() 
    #plt.rcParams["axes.labelsize"] = 15
    ax.set_title("pred vs real ", fontsize=14)
    ax.set_xlabel(X_name, fontsize=16)
    ax.set_ylabel(Y_name, fontsize=16)
    rp = ax.scatter(x = X_name,
                    y = Y_name,
                    data = DF,
                    facecolors="none",
                    edgecolors='black') # purple

    # Xデータの最小値と最大値
    x_min = DF[X_name].min()
    x_max = DF[X_name].max()
    # Yデータの最小値と最大値
    y_min = DF[Y_name].min()
    y_max = DF[Y_name].max()
    # プロットするためのxデータの範囲を決める
    x_min = min(x_min, y_min)
    x_max = min(x_max, y_max)
    print(x_min)
    print(x_max)

    # グラフのレンジを決める
    x_range = x_max - x_min # データの範囲が100以下か以上かで分ける
    print("x_range = x_max - x_min = " + str(x_range))
    if x_max > 1:
        min_lim = 0
        if x_range <= 10:
            max_lim = math.floor(x_max + 1)
        else:
            max_lim = math.floor(x_max + 10)
        rp.axes.set_xlim(min_lim, max_lim)
        rp.axes.set_ylim(min_lim, max_lim)
    else:
        max_lim = 0
        if x_range <= 100:
            max_lim = math.floor(x_max - 1)
        else:
            max_lim = math.floor(x_max - 10)
        rp.axes.set_xlim(min_lim, max_lim)
        rp.axes.set_ylim(min_lim, max_lim)

    # 近似式プロットのためのデータを作成
    x_approximation = np.linspace(min_lim, max_lim, 10) # numpyでxデータを作成
    # 近似式に代入してyデータを作成
    y_approximation = popt[0] * pow(x_approximation, 2) + popt[1] * x_approximation + popt[2]
    line_approximation = ax.plot(x_approximation, y_approximation,
                                 linestyle = 'dashed', linewidth = 3, color='r') 
    
    rp.axes.set_aspect('equal', adjustable='box')
    plt.grid(True)
    # 凡例を指定して記入する場合は、リストで指定する
    ax.legend([line_approximation[0]], ["y = {0:.2f}x^2 + {1:.2f}x + {2:.2f}".format(popt[0], popt[1], popt[2])],
               loc='upper left',
               numpoints=1,
               fontsize=15)
    plt.tick_params(labelsize=15)
    plt.tight_layout() 
    plt.savefig(save_fname + '.png') 
    plt.close()

if __name__ == '__main__':
    ##### 基本パラメータ #####
    file_path = "test_data.csv"
    df = pd.read_csv(file_path)
    
    # 散布図(近似式付き)関数を呼び出して実行する
    # 引数3つは順に、pandasデータフレーム, X軸にプロットしたい列名, Y軸にプロットしたい列名
    plot_reg_A(df, "PRICE", "PRICE_pred")
    plot_reg_AB(df, "PRICE", "PRICE_pred")
    plot_reg_ABC(df, "PRICE", "PRICE_pred")
    
    print("finished")

▼手法は同じで、その他活用例として下記リンクを貼り付けます。

hk29.hatenablog.jp

以上

<広告>