#44 Pythonで始める回帰分析(単回帰)、チャレンジ AI×100業務(製造業)
今回は、過去の設計データや試験データをもとに性能を予測するような、設計者目線の回帰分析を取り上げ、Pythonで形にしてみました。
それでは、スタートです。
*執筆者は以下を参照してください。
使ったツール・準備したこと
Visual Studio Code環境下でのPython
Chat GPT 4o
やってみたこと|コード・ステップ紹介
実行した内容の簡単な流れ
ChatGPT 4oへの会話:
回帰分析のコードを生成してください。以下留意してください
・データはcsvファイルを読み込み、Y、Xを選択するGUIを追加
・回帰式、相関係数(R)の表示
・予測区間を赤点線で描画


グラフは画像でアクティブにはなりませんので、軸範囲の設定GUIにて横軸、縦軸の範囲を設定するようにしています。
赤点線は95%予測区間を示しており、グラフをマウスオーバーすれば、右下に x, y の数値が表示されているので、大よその数値は把握できます。
今後の展開・アイデア
次回取り組みたいこと
・多次数の回帰分析です。
■ 今回のコードです。
import pandas as pd
import numpy as np
import tkinter as tk
from tkinter import filedialog, simpledialog, ttk, messagebox
import matplotlib.pyplot as plt
from matplotlib.backend_bases import MouseEvent
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from scipy.stats import t
import os
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読み込み(全行対象でエラー時fallback)
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("列と次数の選択")
root.geometry("600x250")
x_var = tk.StringVar()
y_var = tk.StringVar()
xlabel_var = tk.StringVar()
ylabel_var = tk.StringVar()
degree_var = tk.IntVar(value=2)
x_var.set(df.columns[0])
y_var.set(df.columns[1] if len(df.columns) > 1 else df.columns[0])
def update_labels(*args):
print("X列:", x_var.get(), "→ X軸ラベルに反映")
xlabel_var.set(x_var.get())
ylabel_var.set(y_var.get())
def update_labels(*args):
print("更新: X→", x_var.get(), "Y→", y_var.get())
xlabel_var.set(x_var.get())
ylabel_var.set(y_var.get())
x_var.trace_add('write', update_labels)
y_var.trace_add('write', update_labels)
update_labels()
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="回帰次数 (2~5):").grid(row=4, column=0, sticky='w')
ttk.Combobox(root, textvariable=degree_var, values=list(range(2, 6)), state="readonly", width=10).grid(row=4, column=1, sticky='w')
def submit(event=None):
root.quit()
root.destroy()
tk.Button(root, text="OK", command=submit).grid(row=5, column=1, pady=10, sticky='e')
root.bind('<Return>', submit)
root.mainloop()
# データ準備
x_col = x_var.get()
y_col = y_var.get()
xlabel = xlabel_var.get()
ylabel = ylabel_var.get()
df = df[[x_col, y_col]].dropna()
x = df[[x_col]].values
y = df[y_col].values
degree = degree_var.get()
# 多項式回帰
poly = PolynomialFeatures(degree=degree, include_bias=False)
x_poly = poly.fit_transform(x)
model = LinearRegression()
model.fit(x_poly, y)
y_pred = model.predict(x_poly)
r2 = r2_score(y, y_pred)
# 式の生成
coef_str = " + ".join([f"{c:.3f}x^{i+1}" for i, c in enumerate(model.coef_)])
eq = f"Y = {model.intercept_:.3f} + {coef_str}"
# 予測区間
x_range = np.linspace(x.min(), x.max(), 100).reshape(-1, 1)
x_range_poly = poly.transform(x_range)
y_fit = model.predict(x_range_poly)
# 残差と予測誤差(簡易版)
n = len(x)
p = x_poly.shape[1]
se = np.sqrt(np.sum((y - y_pred)**2) / (n - p - 1))
mean_x = np.mean(x)
y_std_error = se * np.sqrt(1 + 1/n + ((x_range - mean_x)**2 / np.sum((x - mean_x)**2)))
t_val = t.ppf(0.975, df=n - p - 1)
y_lower = y_fit - t_val * y_std_error.flatten()
y_upper = y_fit + t_val * y_std_error.flatten()
# 再描画関数
active_fig = None
x_min_var = tk.StringVar()
x_max_var = tk.StringVar()
y_min_var = tk.StringVar()
y_max_var = tk.StringVar()
def redraw_plot(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}次回帰')
plt.plot(x_range, y_lower, 'r--', label='予測区間(95%)')
plt.plot(x_range, y_upper, 'r--')
plt.title("多項式回帰分析", fontsize=16)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.text(0.05, 0.95, eq, transform=plt.gca().transAxes)
plt.text(0.05, 0.90, f"R² = {r2:.3f}", transform=plt.gca().transAxes)
plt.text(0.05, 0.85, f"S = {se:.4f}", transform=plt.gca().transAxes)
plt.grid(True)
plt.legend()
if xlim:
plt.xlim(xlim)
if ylim:
plt.ylim(ylim)
plt.tight_layout()
plt.show()
def open_axis_gui():
gui = tk.Toplevel()
gui.title("軸範囲の設定")
gui.geometry("500x200")
tk.Label(gui, text="X軸 最小値:").grid(row=0, column=0)
tk.Entry(gui, textvariable=x_min_var, width=30).grid(row=0, column=1)
tk.Label(gui, text="X軸 最大値:").grid(row=1, column=0)
tk.Entry(gui, textvariable=x_max_var, width=30).grid(row=1, column=1)
tk.Label(gui, text="Y軸 最小値:").grid(row=2, column=0)
tk.Entry(gui, textvariable=y_min_var, width=30).grid(row=2, column=1)
tk.Label(gui, text="Y軸 最大値:").grid(row=3, column=0)
tk.Entry(gui, textvariable=y_max_var, width=30).grid(row=3, column=1)
def apply_and_redraw():
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]
)
redraw_plot(xlim, ylim)
except ValueError:
messagebox.showerror("エラー", "数値を正しく入力してください")
redraw_button = tk.Button(gui, text="再描画", command=apply_and_redraw)
redraw_button.grid(row=4, column=1, pady=10)
gui.bind('<Return>', lambda event: apply_and_redraw())
open_axis_gui()
redraw_plot()
def on_click(event: MouseEvent):
if event.inaxes:
open_axis_gui()
fig = plt.figure()
cid = fig.canvas.mpl_connect('button_press_event', on_click)
plt.close(fig)
