見出し画像

#45 Pythonで始める回帰分析(多次数)、チャレンジ AI×100業務(製造業)

前回、過去の設計データや試験データをもとに性能を予測するような、設計者目線の回帰分析を取り上げ、Pythonで形にしてみました。前回は単回帰でしたので、今回は2次以上の多次数を扱います。


それでは、スタートです。
*執筆者は以下を参照してください。


使ったツール・準備したこと

  • Visual Studio Code環境下でのPython

  • Chat GPT 4o -> GPT 4.5

やってみたこと|コード・ステップ紹介

実行した内容の簡単な流れ

 ChatGPT 4oへの会話:

上の内容を参考に重回帰分析の別のコードを生成してください。 ・CSVファイルをよみとり ・Y、及びX(3因子まで)を入力 ・重回帰分析 ・出力は、予測式、各因子のP値、R値、分散分析の結果

*上の内容:この会話の前に単回帰分析の会話がなされており、その続きなので。

下の画面で、X, Yを選択し、フィッティングする次数を決めます。本アプリは5次までできるようにしています。

次数を選択できます。
プロットは、単回帰の場合と同じように予測区間を示しています。

グラフは画像でアクティブにはなりませんので、軸範囲の設定GUIにて横軸、縦軸の範囲を設定するようにしています。

今回もGPT-4oで進めていましたが、プロンプトが悪いのか、なぜかうまく収束しませんでした。コードに反映せず、うまく描画できないため、GPT-4.5に切り替え実施しました。

今後の展開・アイデア

次回取り組みたいこと

・重回帰分析です。

■ 今回のコードです。

import pandas as pd
import numpy as np
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
import matplotlib.pyplot as plt
from scipy.stats import t
import matplotlib
matplotlib.rcParams['font.family'] = 'MS Gothic'

# CSVファイル選択
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(title="CSVファイルを選択", filetypes=[("CSV files", "*.csv")])
if not file_path:
    raise Exception("ファイルが選択されていません。");

# CSV読み込み
try:
    df = pd.read_csv(file_path, encoding="cp932", engine='python')
except Exception:
    df = pd.read_csv(file_path, encoding="utf-8", engine='python')

# GUIによるX, Y列とラベル選択
root = tk.Tk()
root.title("XとYの列を選択")
root.geometry("600x250")

x_var = tk.StringVar(root)
y_var = tk.StringVar(root)
xlabel_var = tk.StringVar(root)
ylabel_var = tk.StringVar(root)
degree_var = tk.IntVar(root, value=1)

x_var.set(df.columns[0])
y_var.set(df.columns[1] if len(df.columns) > 1 else df.columns[0])
xlabel_var.set(x_var.get())
ylabel_var.set(y_var.get())

# X列選択時にラベルを自動更新する
def update_xlabel(*args):
    xlabel_var.set(x_var.get())
x_var.trace_add('write', update_xlabel)

# Y列選択時にラベルを自動更新する
def update_ylabel(*args):
    ylabel_var.set(y_var.get())
y_var.trace_add('write', update_ylabel)

# GUIレイアウト
tk.Label(root, text="X列を選択:").grid(row=0, column=0, sticky='w')
ttk.Combobox(root, textvariable=x_var, values=list(df.columns), state="readonly", width=50).grid(row=0, column=1)
tk.Label(root, text="Y列を選択:").grid(row=1, column=0, sticky='w')
ttk.Combobox(root, textvariable=y_var, values=list(df.columns), state="readonly", width=50).grid(row=1, column=1)
tk.Label(root, text="X軸ラベル:").grid(row=2, column=0, sticky='w')
tk.Entry(root, textvariable=xlabel_var, width=50).grid(row=2, column=1)
tk.Label(root, text="Y軸ラベル:").grid(row=3, column=0, sticky='w')
tk.Entry(root, textvariable=ylabel_var, width=50).grid(row=3, column=1)
tk.Label(root, text="回帰の次数:").grid(row=4, column=0, sticky='w')
tk.Spinbox(root, from_=1, to=5, textvariable=degree_var, width=5).grid(row=4, column=1, sticky='w')

def submit():
    root.quit()
    root.destroy()

tk.Button(root, text="OK", command=submit).grid(row=5, column=1, pady=10, sticky='e')
root.mainloop()

# データ準備(数値型へ安全に変換)
x = pd.to_numeric(df[x_var.get()], errors='coerce').values.flatten()
y = pd.to_numeric(df[y_var.get()], errors='coerce').values.flatten()

# NaNを含む行を除去
mask = ~np.isnan(x) & ~np.isnan(y)
x = x[mask]
y = y[mask]

# 多項式回帰
try:
    coefs = np.polyfit(x, y, degree_var.get())
except np.linalg.LinAlgError:
    messagebox.showerror("エラー", "回帰計算が収束しませんでした。次数を下げて再試行してください。");
    raise

p = np.poly1d(coefs)
y_pred = p(x)
r = np.corrcoef(y, y_pred)[0, 1]

# プロット用データ
x_range = np.linspace(x.min(), x.max(), 100)
y_fit = p(x_range)

# 標準誤差と予測区間の計算
se = np.sqrt(np.sum((y - y_pred)**2) / (len(x) - degree_var.get() - 1))
mean_x = np.mean(x)
t_val = t.ppf(0.975, len(x)-degree_var.get()-1)
y_std_error = se * np.sqrt(1 + 1/len(x) + (x_range - mean_x)**2 / np.sum((x - mean_x)**2))
y_upper = y_fit + t_val * y_std_error
y_lower = y_fit - t_val * y_std_error

# 軸範囲設定用の変数
x_min_var = tk.StringVar()
x_max_var = tk.StringVar()
y_min_var = tk.StringVar()
y_max_var = tk.StringVar()

def open_axis_gui():
    gui = tk.Toplevel()
    gui.title("軸範囲の設定")
    gui.geometry("400x200")

    tk.Label(gui, text="X軸 最小値:").grid(row=0, column=0)
    tk.Entry(gui, textvariable=x_min_var).grid(row=0, column=1)
    tk.Label(gui, text="X軸 最大値:").grid(row=1, column=0)
    tk.Entry(gui, textvariable=x_max_var).grid(row=1, column=1)
    tk.Label(gui, text="Y軸 最小値:").grid(row=2, column=0)
    tk.Entry(gui, textvariable=y_min_var).grid(row=2, column=1)
    tk.Label(gui, text="Y軸 最大値:").grid(row=3, column=0)
    tk.Entry(gui, textvariable=y_max_var).grid(row=3, column=1)

    def apply_and_plot():
        try:
            current_xlim = plt.gca().get_xlim()
            current_ylim = plt.gca().get_ylim()
            xlim = (float(x_min_var.get()) if x_min_var.get() else current_xlim[0],
                    float(x_max_var.get()) if x_max_var.get() else current_xlim[1])
            ylim = (float(y_min_var.get()) if y_min_var.get() else current_ylim[0],
                    float(y_max_var.get()) if y_max_var.get() else current_ylim[1])
            plot_regression(xlim, ylim)
        except ValueError:
            messagebox.showerror("エラー", "数値を正しく入力してください。")

    tk.Button(gui, text="再描画", command=apply_and_plot).grid(row=4, column=1, pady=10)
    gui.bind('<Return>', lambda event: apply_and_plot())

# 描画関数(軸指定対応、前のグラフを閉じる)
active_fig = None

def plot_regression(xlim=None, ylim=None):
    global active_fig
    if active_fig:
        plt.close(active_fig)
    active_fig = plt.figure(figsize=(10, 6))
    plt.scatter(x, y, color='black', s=10)
    plt.plot(x_range, y_fit, color='blue', label=f'{degree_var.get()}次回帰曲線')
    plt.plot(x_range, y_upper, 'r--', label='予測区間(95%)')
    plt.plot(x_range, y_lower, 'r--')
    plt.title('回帰分析プロット')
    plt.xlabel(xlabel_var.get())
    plt.ylabel(ylabel_var.get())
    equation = " + ".join([f"{c:.3f}X^{degree_var.get() - i}" for i, c in enumerate(coefs)])
    plt.text(0.05, 0.95, f"Y={equation}", transform=plt.gca().transAxes)
    plt.text(0.05, 0.90, f"R={r:.3f}", transform=plt.gca().transAxes)
    plt.text(0.05, 0.85, f"S={se:.3f}", transform=plt.gca().transAxes)
    if xlim:
        plt.xlim(xlim)
    if ylim:
        plt.ylim(ylim)
    plt.grid()
    plt.legend()
    plt.tight_layout()
    plt.show()

# 初回プロットと軸設定GUI表示
open_axis_gui()
plot_regression()

いいなと思ったら応援しよう!