#43 Python+GPTでつくる統計解析アプリ(ANOVA)、チャレンジ AI×100業務(製造業)
前回の記事(#42 Python+GPTでつくる統計解析Webアプリ;正規性検定→F検定→t検定)では、正規性検定→F検定→t検定をまとめて描画するとともに、データをcsvファイルからよみとり、任意の2群の検定を実施するものとしました。
今日は、上記のANOVA編です。
*それでは、スタートです。
*執筆者は以下を参照してください。
使ったツール・準備したこと
Visual Studio Code環境下でのPython
ChatGPT
やってみたこと|コード・ステップ紹介
実行した内容の簡単な流れ
ChatGPTに次の会話をしました。
多群(3群以上)のANOVA検定対応版を生成してください。
Tukeyの多重比較や非パラメトリック検定(Kruskall-Wallis)への拡張願います。
■ 下のグラフが描画されました。

箱ひげ図と合わせるとわかりやすいですね。
今後の展開・アイデア
次回取り組みたいこと;
・回帰分析を予定しています。
■ 今日のコード
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import pandas as pd
import numpy as np
import scipy.stats as stats
from statsmodels.stats.multicomp import pairwise_tukeyhsd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
plt.rcParams['font.family'] = 'Meiryo' # 日本語フォント
df = None # グローバル変数でDataFrameを保持
def load_csv():
global df
filepath = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
if not filepath:
return
try:
df = pd.read_csv(filepath, encoding='cp932')
if df.shape[1] < 2:
messagebox.showerror("エラー", "CSVファイルには少なくとも2列必要です。")
return
# チェックボックス用の因子列を再構成
for widget in checkbox_frame.winfo_children():
widget.destroy()
var_dict.clear()
for col in df.columns:
var = tk.BooleanVar()
chk = tk.Checkbutton(checkbox_frame, text=col, variable=var)
chk.pack(anchor='w')
var_dict[col] = var
except Exception as e:
messagebox.showerror("読み込みエラー", f"CSVの読み込みに失敗しました: {e}")
def run_anova():
global df
if df is None:
messagebox.showwarning("警告", "先にCSVファイルを読み込んでください。")
return
selected_cols = [col for col, var in var_dict.items() if var.get()]
if len(selected_cols) < 2:
messagebox.showwarning("警告", "2群以上の列を選択してください。")
return
try:
data = [df[col].dropna().astype(float).tolist()[:30] for col in selected_cols]
result_text = "多群比較(ANOVA + Kruskal-Wallis + Tukey)\n\n"
# 正規性検定
for i, col in enumerate(selected_cols):
sw = stats.shapiro(data[i])
result_text += f"{col} の正規性検定 p値: {sw.pvalue:.4f} ({'正規分布' if sw.pvalue > 0.05 else '非正規'})\n"
# 等分散性検定(バートレット)
bart_p = stats.bartlett(*data).pvalue
result_text += f"\nバートレット検定(等分散性)p値: {bart_p:.4f}\n"
# ANOVA検定
anova = stats.f_oneway(*data)
result_text += f"\n一元配置分散分析(ANOVA)結果: F値={anova.statistic:.4f}, p値={anova.pvalue:.4f}\n"
# Kruskal-Wallis検定(非正規性・非等分散性対応)
kw = stats.kruskal(*data)
result_text += f"Kruskal-Wallis検定(ノンパラメトリック): H値={kw.statistic:.4f}, p値={kw.pvalue:.4f}\n"
# Tukey HSD(ANOVAが有意なとき)
melted_df = pd.DataFrame({"値": [], "群": []})
for i, col in enumerate(selected_cols):
temp = pd.DataFrame({"値": data[i], "群": [col] * len(data[i])})
melted_df = pd.concat([melted_df, temp], ignore_index=True)
tukey_result = pairwise_tukeyhsd(endog=melted_df["値"], groups=melted_df["群"], alpha=0.05)
result_text += f"\nTukeyの多重比較結果:\n{tukey_result}\n"
result_box.delete("1.0", tk.END)
result_box.insert(tk.END, result_text)
# 箱ひげ図
ax.clear()
ax.boxplot(data, labels=selected_cols, patch_artist=True)
means = [np.mean(d) for d in data]
ax.plot(range(1, len(data) + 1), means, 'r+', markersize=12, label='平均')
ax.set_title("箱ひげ図(多群)", fontsize=12)
ax.legend()
canvas.draw()
except Exception as e:
messagebox.showerror("解析エラー", f"解析に失敗しました: {e}")
# GUI作成
root = tk.Tk()
root.title("多群比較ツール(ANOVA + Tukey + Kruskal-Wallis 対応)")
frame = tk.Frame(root)
frame.pack(padx=10, pady=10)
load_btn = tk.Button(frame, text="CSVファイル読込", command=load_csv)
load_btn.pack(pady=5)
checkbox_frame = tk.LabelFrame(root, text="比較対象列の選択(2群以上)")
checkbox_frame.pack(fill="both", padx=10, pady=5)
var_dict = {} # 選択された列を保持
run_btn = tk.Button(root, text="解析実行(ANOVA)", command=run_anova)
run_btn.pack(pady=10)
result_box = tk.Text(root, width=90, height=20)
result_box.pack(padx=10, pady=5)
fig, ax = plt.subplots(figsize=(7, 5))
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack()
root.mainloop()
