見出し画像

#42 Python+GPTでつくる統計解析アプリ(正規性検定→F検定→t検定)、チャレンジ AI×100業務(製造業)

以前の記事では、以下のような各項目に対してコードを生成していました。
#10 Pythonで始めるt検定
#11 Pythonで始める箱ひげ図
#12 Pythonで始めるF検定

今日は、これらをまとめて描画するとともに、csvファイルからよみとり*、任意の2群の検定を実施するものとします。
*上記のコード生成していたアプリはデータを直接入力、もしくはコピペで入力していました。


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


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

  • Visual Studio Code環境下でのPython

  • ChatGPT

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

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

ChatGPTに次の会話をしました。

アプリを生成してください。*Pythonで ・データは2群、n=30まで入力可能(エクセルからのコピペOK) ・正規性の検定を実施(表示はp値) ・F検定を実施(正規分布、非正規分布の場合両方) ・F検定で有意差がない場合;等分散を仮定としてt検定を実施(表示はp値) ・F検定で有意差がある場合;等分散を仮定としないt検定を実施(表示はp値) ・グラフは箱ひげ図も合わせて描画

入力したプロンプト

コードを修正して下さい。データはcsvファイルから読み取ります。csvファイルの列にはデータがあり、先頭が因子名です。

修正依頼の一部

■ 下のグラフが描画されました。

グループ名は変更可能になっています。データ列はプルダウンで選択可能です。
t検定は、等分散検定の結果を受けて、等分散である場合、ない場合の両方で検定します。

箱ひげ図が整列している点、まとまっている点などわかりやすいですね。

今後の展開・アイデア

次回取り組みたいこと;

・ANOVAを予定しています。

■ 今日のコード

import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import pandas as pd
import numpy as np
import scipy.stats as stats
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

        # コンボボックスに列名を設定
        columns = list(df.columns)
        group1_combo['values'] = columns
        group2_combo['values'] = columns
        group1_combo.current(0)
        group2_combo.current(1 if len(columns) > 1 else 0)

        group1_name.set(columns[0])
        group2_name.set(columns[1] if len(columns) > 1 else columns[0])
    except Exception as e:
        messagebox.showerror("読み込みエラー", f"CSVの読み込みに失敗しました: {e}")

def run_analysis():
    global df
    if df is None:
        messagebox.showwarning("警告", "先にCSVファイルを読み込んでください。")
        return
    try:
        col1 = group1_combo.get()
        col2 = group2_combo.get()
        data1 = df[col1].dropna().astype(float).tolist()[:30]
        data2 = df[col2].dropna().astype(float).tolist()[:30]

        group1_name.set(col1)
        group2_name.set(col2)
        analyze(data1, data2)
    except Exception as e:
        messagebox.showerror("解析エラー", f"データの読み込みに失敗しました: {e}")

def analyze(group1, group2):
    label1 = group1_name.get().strip() or "Group1"
    label2 = group2_name.get().strip() or "Group2"

    if len(group1) < 2 or len(group2) < 2:
        messagebox.showwarning("入力エラー", "各グループに最低2つの数値が必要です。")
        return

    result_text = ""
    # 正規性検定
    sw1 = stats.shapiro(group1)
    sw2 = stats.shapiro(group2)
    result_text += f"正規性検定 (Shapiro-Wilk):\n"
    result_text += f"  {label1} のp値: {sw1.pvalue:.4f} ({'正規分布' if sw1.pvalue > 0.05 else '非正規'})\n"
    result_text += f"  {label2} のp値: {sw2.pvalue:.4f} ({'正規分布' if sw2.pvalue > 0.05 else '非正規'})\n"

    # F検定
    f_stat = np.var(group1, ddof=1) / np.var(group2, ddof=1)
    dfn = len(group1) - 1
    dfd = len(group2) - 1
    p_f = 2 * min(stats.f.cdf(f_stat, dfn, dfd), 1 - stats.f.cdf(f_stat, dfn, dfd))
    result_text += f"\nF検定:\n  F値: {f_stat:.4f}, p値: {p_f:.4f}\n"

    # t検定
    equal_var = p_f > 0.05
    t_res = stats.ttest_ind(group1, group2, equal_var=equal_var)
    result_text += f"\nt検定 ({'等分散仮定' if equal_var else '等分散仮定なし'}):\n  p値: {t_res.pvalue:.4f}\n"

    result_box.delete("1.0", tk.END)
    result_box.insert(tk.END, result_text)

    # 箱ひげ図描画
    ax.clear()
    bp = ax.boxplot([group1, group2], labels=[label1, label2], patch_artist=True)
    means = [np.mean(group1), np.mean(group2)]
    ax.plot([1, 2], means, 'r+', markersize=12, label='平均')
    ax.set_title("箱ひげ図", fontsize=12)
    ax.legend()
    canvas.draw()

# GUI作成
root = tk.Tk()
root.title("2群比較ツール(CSV対応)")

frame = tk.Frame(root)
frame.pack(padx=10, pady=10)

# グループ選択
tk.Label(frame, text="グループ1列").grid(row=0, column=0)
group1_combo = ttk.Combobox(frame, width=20)
group1_combo.grid(row=0, column=1)

tk.Label(frame, text="グループ2列").grid(row=0, column=2)
group2_combo = ttk.Combobox(frame, width=20)
group2_combo.grid(row=0, column=3)

# 表示名
tk.Label(frame, text="グループ1名").grid(row=1, column=0)
group1_name = tk.StringVar(value="Group1")
tk.Entry(frame, textvariable=group1_name, width=20).grid(row=1, column=1)

tk.Label(frame, text="グループ2名").grid(row=1, column=2)
group2_name = tk.StringVar(value="Group2")
tk.Entry(frame, textvariable=group2_name, width=20).grid(row=1, column=3)

# ボタン
load_btn = tk.Button(frame, text="CSVファイル読込", command=load_csv)
load_btn.grid(row=2, column=0, columnspan=2, pady=10)
run_btn = tk.Button(frame, text="解析実行", command=run_analysis)
run_btn.grid(row=2, column=2, columnspan=2, pady=10)

result_box = tk.Text(root, width=70, height=10)
result_box.pack(padx=10, pady=5)

fig, ax = plt.subplots(figsize=(5, 4))
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack()

root.mainloop()

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