Wen1201 commited on
Commit
662a0be
·
verified ·
1 Parent(s): 754d71d

Upload 7 files

Browse files
README.md CHANGED
@@ -1,12 +1,292 @@
1
  ---
2
- title: BayesianPyMc1
3
- emoji: 💻
4
- colorFrom: pink
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 6.3.0
8
  app_file: app.py
9
  pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Pokemon Speed Bayesian Analysis System
3
+ emoji: 🔬
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: streamlit
7
+ sdk_version: 1.31.0
8
  app_file: app.py
9
  pinned: false
10
+
11
+ ---
12
+ # 🎲 貝氏階層模型分析系統 - 寶可夢速度對勝率影響分析
13
+
14
+ ## 📋 系統簡介
15
+
16
+ 這是一個基於 Streamlit 和 PyMC 的貝氏階層模型分析系統,專為分析寶可夢速度對勝率的影響而設計,結合 AI 助手提供統計解釋和對戰策略建議。
17
+
18
+ ## 🎯 主要功能
19
+
20
+ ### 1. 貝氏階層模型分析
21
+ - ✅ MCMC 抽樣(NUTS 採樣器)
22
+ - ✅ 後驗分佈估計
23
+ - ✅ 95% HDI(最高密度區間)
24
+ - ✅ 階層結構(借用資訊)
25
+ - ✅ 異質性評估(sigma)
26
+ - ✅ 收斂診斷(R-hat, ESS)
27
+
28
+ ### 2. 四種視覺化圖表
29
+ - 📈 **Trace Plot**: MCMC 收斂診斷
30
+ - 📊 **Posterior Plot**: 後驗分佈圖
31
+ - 🌲 **Forest Plot**: 各屬性效應圖
32
+ - 🔍 **DAG 圖**: 模型結構圖
33
+
34
+ ### 3. AI 智能助手
35
+ - 💬 自然語言對話
36
+ - 📖 統計指標解釋
37
+ - 🎮 對戰策略建議
38
+ - 📚 貝氏統計教學
39
+ - 🔍 結果深度分析
40
+
41
+ ## 📦 安裝步驟
42
+
43
+ ### 1. Python 環境要求
44
+ ```bash
45
+ # 需要 Python 3.11
46
+ python --version # 應顯示 Python 3.11.x
47
+ ```
48
+
49
+ ### 2. 安裝依賴套件
50
+ ```bash
51
+ pip install -r requirements_bayesian.txt
52
+ ```
53
+
54
+ ### 3. 安裝 Graphviz(用於生成 DAG 圖)
55
+
56
+ #### Windows
57
+ ```bash
58
+ # 使用 Chocolatey
59
+ choco install graphviz
60
+
61
+ # 或從官網下載:https://graphviz.org/download/
62
+ ```
63
+
64
+ #### macOS
65
+ ```bash
66
+ brew install graphviz
67
+ ```
68
+
69
+ #### Linux (Ubuntu/Debian)
70
+ ```bash
71
+ sudo apt-get update
72
+ sudo apt-get install graphviz
73
+ ```
74
+
75
+ ### 4. 準備資料
76
+ 將寶可夢速度對戰資料 CSV 檔放在同一目錄下,檔名為 `pokemon_speed_meta_results.csv`
77
+
78
+ **資料格式要求:**
79
+
80
+ | Trial_Type | rc | nc | rt | nt |
81
+ |------------|----|----|----|----|
82
+ | Water | 45 | 100| 62 | 100|
83
+ | Fire | 38 | 100| 55 | 100|
84
+ | Grass | 42 | 100| 58 | 100|
85
+
86
+ **欄位說明:**
87
+ - `Trial_Type`: 屬性名稱(例如:Water, Fire, Grass)
88
+ - `rc`: 控制組(速度慢)的勝場數
89
+ - `nc`: 控制組的總場數
90
+ - `rt`: 實驗組(速度快)的勝場數
91
+ - `nt`: 實驗組的總場數
92
+
93
+ ### 5. 設定 Google Gemini API Key
94
+ - 在系統左側邊欄輸入您的 Google Gemini API Key
95
+ - API Key 用於 AI 助手功能
96
+ - 獲取 API Key: https://makersuite.google.com/app/apikey
97
+
98
+ ### 6. 執行程式
99
+ ```bash
100
+ streamlit run app_bayesian.py
101
+ ```
102
+
103
+ ## 🔧 檔案結構
104
+
105
+ ```
106
+ bayesian_analysis/
107
+ ├── app_bayesian.py # Streamlit 主程式
108
+ ├── bayesian_core.py # 貝氏模型核心邏輯
109
+ ├── bayesian_llm_assistant.py # AI 對話助手
110
+ ├── bayesian_utils.py # 視覺化工具
111
+ ├── requirements_bayesian.txt # 依賴套件
112
+ ├── README.md # 說明文件
113
+ └── pokemon_speed_meta_results.csv # 資料檔(需自行準備)
114
+ ```
115
+
116
+ ## 📊 使用方式
117
+
118
+ ### Step 1: 載入資料
119
+ 1. 選擇「使用預設資料集」或「上傳您的資料」
120
+ 2. 如果上傳,請確保 CSV 格式正確
121
+
122
+ ### Step 2: 設定 MCMC 參數
123
+ 在左側邊欄設定:
124
+ - **抽樣數 (Samples)**: 預設 2000(建議 1000-5000)
125
+ - **調整期 (Tune)**: 預設 1000(建議 500-2000)
126
+ - **鏈數 (Chains)**: 預設 2(建議 2-4)
127
+ - **目標接受率**: 預設 0.95(建議 0.90-0.99)
128
+
129
+ ### Step 3: 執行分析
130
+ 1. 點擊「開始貝氏分析」按鈕
131
+ 2. 等待 MCMC 抽樣完成(可能需要數分鐘)
132
+ 3. 查看結果的五個子頁面:
133
+ - **📊 概覽**: 關鍵指標和摘要
134
+ - **📈 Trace & Posterior**: 收斂診斷和後驗分佈
135
+ - **🌲 Forest Plot**: 各屬性效應比較
136
+ - **🔍 DAG 模型圖**: 模型結構視覺化
137
+ - **📋 詳細報告**: 完整文字報告
138
+
139
+ ### Step 4: 使用 AI 助手
140
+ 1. 切換到「AI 助手」頁面
141
+ 2. 在聊天框輸入問題,或點擊快速問題按鈕
142
+ 3. AI 會根據分析結果提供解釋和建議
143
+
144
+ ## 💡 統計指標說明
145
+
146
+ ### 貝氏階層模型參數
147
+
148
+ #### d (整體效應)
149
+ - **意義**: 所有屬性的平均對數勝算比
150
+ - **解讀**: d > 0 表示速度快整體有利
151
+
152
+ #### sigma (屬性間變異)
153
+ - **意義**: 不同屬性對速度反應的差異程度
154
+ - **解讀**:
155
+ - sigma < 0.3: 低異質性(屬性反應相似)
156
+ - 0.3 ≤ sigma ≤ 0.5: 中等異質性
157
+ - sigma > 0.5: 高異質性(屬性反應差異大)
158
+
159
+ #### or_speed (勝算比)
160
+ - **意義**: exp(d),速度快的勝算倍數
161
+ - **解讀**: OR = 1.5 表示速度快的勝率是速度慢的 1.5 倍
162
+
163
+ #### delta (屬性特定效應)
164
+ - **意義**: 每個屬性的個別速度效應
165
+ - **解讀**: delta[i] 告訴我們第 i 個屬性對速度的反應
166
+
167
+ ### HDI (Highest Density Interval)
168
+ - **95% HDI**: 參數有 95% 機率落在此區間
169
+ - **與信賴區間的差異**: HDI 是貝氏可信區間,直接表示參數的機率分佈
170
+
171
+ ### 收斂診斷
172
+
173
+ #### R-hat
174
+ - **目標**: < 1.1
175
+ - **意義**: 鏈間與鏈內變異的比例
176
+ - **解讀**: R-hat ≈ 1.0 表示良好收斂
177
+
178
+ #### ESS (Effective Sample Size)
179
+ - **目標**: > 100(最好 > 400)
180
+ - **意義**: 考慮自相關後的有效樣本數
181
+ - **解讀**: ESS 越高,估計越精確
182
+
183
+ ## 🎮 應用場景
184
+
185
+ ### 1. 速度重要性分析
186
+ 判斷速度對整體勝率的影響有多大
187
+
188
+ ### 2. 屬性異質性評估
189
+ 了解哪些屬性特別依賴速度,哪些不依賴
190
+
191
+ ### 3. 組隊策略制定
192
+ 根據統計結果選擇合適的寶可夢
193
+
194
+ ### 4. 教學用途
195
+ 學習貝氏階層模型的原理和應用
196
+
197
+ ## ⚙️ 技術架構
198
+
199
+ ### 核心技術
200
+ - **Streamlit**: Web 應用框架
201
+ - **PyMC**: 貝氏推論引擎
202
+ - **ArviZ**: 貝氏分析視覺化
203
+ - **pandas**: 資料處理
204
+ - **plotly**: 互動式視覺化
205
+ - **matplotlib**: 靜態圖表
206
+ - **Google Gemini**: AI 助手
207
+
208
+ ### 模型特色
209
+ - ✅ 階層結構(借用資訊)
210
+ - ✅ NUTS 採樣器(自動調整)
211
+ - ✅ 完整的收斂診斷
212
+ - ✅ 多鏈並行
213
+ - ✅ Session 隔離(多用戶支援)
214
+
215
+ ## 🔒 隱私與安全
216
+
217
+ - 所有分析在本地執行
218
+ - Session 資料獨立儲存
219
+ - 超過 1 小時自動清理
220
+ - API Key 不會被儲存
221
+
222
+ ## 📝 範例問題(給 AI 助手)
223
+
224
+ ### 基礎概念
225
+ - "什麼是貝氏階層模型?"
226
+ - "為什麼要用 HDI 而不是 p 值?"
227
+ - "什麼是收縮效應?"
228
+
229
+ ### 結果解讀
230
+ - "為什麼 d 是正的?這代表什麼?"
231
+ - "sigma 告訴我們什麼資訊?"
232
+ - "哪些屬性對速度最敏感?"
233
+
234
+ ### 實戰應用
235
+ - "我該如何組建隊伍?"
236
+ - "飛行系需要速度嗎?"
237
+ - "這個結果對競技對戰有什麼啟示?"
238
+
239
+ ### 統計方法
240
+ - "貝氏和頻率論有什麼差別?"
241
+ - "R-hat 為什麼重要?"
242
+ - "為什麼用階層模型而不是分開分析?"
243
+
244
+ ## 🚀 進階功能
245
+
246
+ ### 自訂 MCMC 參數
247
+ 根據資料大小和複雜度調整:
248
+ - 資料少(< 10 個屬性): Samples=1000, Chains=2
249
+ - 資料中等(10-20 個屬性): Samples=2000, Chains=2
250
+ - 資料多(> 20 個屬性): Samples=3000, Chains=4
251
+
252
+ ### 診斷技巧
253
+ 1. 檢查 Trace Plot 是否平穩
254
+ 2. 確認 R-hat < 1.1
255
+ 3. 確保 ESS > 400
256
+ 4. 觀察後驗分佈是否合理
257
+
258
+ ## 🐛 常見問題
259
+
260
+ ### Q1: DAG 圖無法生成
261
+ **A**: 需要安裝系統級 Graphviz,請參考「安裝步驟」第 3 步
262
+
263
+ ### Q2: MCMC 很慢
264
+ **A**:
265
+ - 減少抽樣數或鏈數
266
+ - 使用更強的電腦
267
+ - 簡化模型結構
268
+
269
+ ### Q3: R-hat > 1.1
270
+ **A**:
271
+ - 增加抽樣數
272
+ - 增加調整期
273
+ - 增加鏈數
274
+ - 檢查資料是否有問題
275
+
276
+ ### Q4: AI 助手回應錯誤
277
+ **A**:
278
+ - 檢查 API Key 是否正確
279
+ - 確認網路連線
280
+ - 重新整理頁面
281
+
282
+ ## 📧 聯絡資訊
283
+
284
+ 如有問題或建議,歡迎聯繫開發團隊。
285
+
286
+ ## 📄 授權
287
+
288
+ 本專案僅供學術研究和教學使用。
289
+
290
  ---
291
 
292
+ **Powered by Streamlit, PyMC & Google Gemini** 🎲
app.py ADDED
@@ -0,0 +1,654 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import uuid
4
+ from datetime import datetime, timedelta
5
+ import atexit
6
+ import os
7
+ import sys
8
+
9
+ # 頁面配置
10
+ st.set_page_config(
11
+ page_title="Bayesian Hierarchical Model - Pokémon Speed Analysis",
12
+ page_icon="🎲",
13
+ layout="wide",
14
+ initial_sidebar_state="expanded"
15
+ )
16
+
17
+ # 自定義 CSS
18
+ st.markdown("""
19
+ <style>
20
+ .streamlit-expanderHeader {
21
+ background-color: #e8f1f8;
22
+ border: 1px solid #b0cfe8;
23
+ border-radius: 5px;
24
+ font-weight: 600;
25
+ color: #1b4f72;
26
+ }
27
+ .streamlit-expanderHeader:hover {
28
+ background-color: #d0e7f8;
29
+ }
30
+ .stMetric {
31
+ background-color: #f8fbff;
32
+ padding: 10px;
33
+ border-radius: 5px;
34
+ border: 1px solid #d0e4f5;
35
+ }
36
+ .stButton > button {
37
+ width: 100%;
38
+ border-radius: 20px;
39
+ font-weight: 600;
40
+ transition: all 0.3s ease;
41
+ }
42
+ .stButton > button:hover {
43
+ transform: translateY(-2px);
44
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
45
+ }
46
+ .success-box {
47
+ background-color: #d4edda;
48
+ border: 1px solid #c3e6cb;
49
+ border-radius: 5px;
50
+ padding: 10px;
51
+ margin: 10px 0;
52
+ }
53
+ .warning-box {
54
+ background-color: #fff3cd;
55
+ border: 1px solid #ffeaa7;
56
+ border-radius: 5px;
57
+ padding: 10px;
58
+ margin: 10px 0;
59
+ }
60
+ </style>
61
+ """, unsafe_allow_html=True)
62
+
63
+ # 導入自定義模組
64
+ from bayesian_core import BayesianHierarchicalAnalyzer
65
+ from bayesian_llm_assistant import BayesianLLMAssistant
66
+ from bayesian_utils import (
67
+ plot_trace,
68
+ plot_posterior,
69
+ plot_forest,
70
+ plot_model_dag,
71
+ create_summary_table,
72
+ create_trial_results_table,
73
+ export_results_to_text,
74
+ plot_odds_ratio_comparison
75
+ )
76
+
77
+ # 清理函數
78
+ def cleanup_old_sessions():
79
+ """清理超過 1 小時的 session"""
80
+ current_time = datetime.now()
81
+ for session_id in list(BayesianHierarchicalAnalyzer._session_results.keys()):
82
+ result = BayesianHierarchicalAnalyzer._session_results.get(session_id)
83
+ if result:
84
+ result_time = datetime.fromisoformat(result['timestamp'])
85
+ if current_time - result_time > timedelta(hours=1):
86
+ BayesianHierarchicalAnalyzer.clear_session_results(session_id)
87
+
88
+ # 註冊清理函數
89
+ atexit.register(cleanup_old_sessions)
90
+
91
+ # 初始化 session state
92
+ if 'session_id' not in st.session_state:
93
+ st.session_state.session_id = str(uuid.uuid4())
94
+ if 'analysis_results' not in st.session_state:
95
+ st.session_state.analysis_results = None
96
+ if 'chat_history' not in st.session_state:
97
+ st.session_state.chat_history = []
98
+ if 'analyzer' not in st.session_state:
99
+ st.session_state.analyzer = None
100
+ if 'trace_img' not in st.session_state:
101
+ st.session_state.trace_img = None
102
+ if 'posterior_img' not in st.session_state:
103
+ st.session_state.posterior_img = None
104
+ if 'forest_img' not in st.session_state:
105
+ st.session_state.forest_img = None
106
+ if 'dag_img' not in st.session_state:
107
+ st.session_state.dag_img = None
108
+
109
+ # 標題
110
+ st.title("🎲 Bayesian Hierarchical Model Analysis")
111
+ st.markdown("### 寶可夢速度對勝率影響的貝氏階層分析")
112
+ st.markdown("---")
113
+
114
+ # Sidebar
115
+ with st.sidebar:
116
+ st.header("⚙️ 配置設定")
117
+
118
+ # Google Gemini API Key
119
+ api_key = st.text_input(
120
+ "Google Gemini API Key",
121
+ type="password",
122
+ help="輸入您的 Google Gemini API Key 以使用 AI 助手"
123
+ )
124
+
125
+ if api_key:
126
+ st.session_state.api_key = api_key
127
+ st.success("✅ API Key 已載入")
128
+
129
+ st.markdown("---")
130
+
131
+ # MCMC 參數設定
132
+ st.subheader("🔬 MCMC 參數")
133
+
134
+ n_samples = st.number_input(
135
+ "抽樣數 (Samples)",
136
+ min_value=500,
137
+ max_value=10000,
138
+ value=2000,
139
+ step=500,
140
+ help="每條鏈的抽樣數量"
141
+ )
142
+
143
+ n_tune = st.number_input(
144
+ "調整期 (Tune)",
145
+ min_value=200,
146
+ max_value=5000,
147
+ value=1000,
148
+ step=200,
149
+ help="調整期的樣本數"
150
+ )
151
+
152
+ n_chains = st.selectbox(
153
+ "鏈數 (Chains)",
154
+ options=[1, 2, 4],
155
+ index=1,
156
+ help="平行運行的鏈數"
157
+ )
158
+
159
+ target_accept = st.slider(
160
+ "目標接受率",
161
+ min_value=0.80,
162
+ max_value=0.99,
163
+ value=0.95,
164
+ step=0.01,
165
+ help="NUTS 採樣器的目標接受率"
166
+ )
167
+
168
+ st.markdown("---")
169
+
170
+ # 清理按鈕
171
+ if st.button("🧹 清理過期資料"):
172
+ cleanup_old_sessions()
173
+ st.success("✅ 清理完成")
174
+ st.rerun()
175
+
176
+ st.markdown("---")
177
+
178
+ # 資料來源選擇
179
+ st.subheader("📊 資料來源")
180
+ data_source = st.radio(
181
+ "選擇資料來源:",
182
+ ["使用預設資料集", "上傳您的資料"]
183
+ )
184
+
185
+ uploaded_file = None
186
+ if data_source == "上傳您的資料":
187
+ uploaded_file = st.file_uploader(
188
+ "上傳 CSV 檔案",
189
+ type=['csv'],
190
+ help="上傳寶可夢速度對戰資料"
191
+ )
192
+
193
+ with st.expander("📖 資料格式說明"):
194
+ st.markdown("""
195
+ **必要欄位格式:**
196
+ - `Trial_Type`: 屬性名稱(例如:Water, Fire, Grass)
197
+ - `rc`: 控制組(速度慢)的勝場數
198
+ - `nc`: 控制組的總場數
199
+ - `rt`: 實驗組(速度快)的勝場數
200
+ - `nt`: 實驗組的總場數
201
+
202
+ **範例:**
203
+ ```
204
+ Trial_Type,rc,nc,rt,nt
205
+ Water,45,100,62,100
206
+ Fire,38,100,55,100
207
+ Grass,42,100,58,100
208
+ ```
209
+ """)
210
+
211
+ st.markdown("---")
212
+
213
+ # 關於系統
214
+ with st.expander("ℹ️ 關於此系統"):
215
+ st.markdown("""
216
+ **貝氏階層模型分析系統**
217
+
218
+ 本系統使用貝氏階層模型來分析速度對寶可夢勝率的影響,
219
+ 並考慮不同屬性之間的異質性。
220
+
221
+ **主要功能:**
222
+ - 🎲 貝氏推論與後驗分佈
223
+ - 📊 階層模型(借用資訊)
224
+ - 📈 4 種視覺化圖表
225
+ - 💬 AI 助手解釋
226
+ - 🎮 對戰策略建議
227
+
228
+ **適用場景:**
229
+ - 分析速度對不同屬性的影響
230
+ - 理解屬性間的異質性
231
+ - 制定基於統計的對戰策略
232
+ """)
233
+
234
+ # 主要內容區 - 雙 Tab
235
+ tab1, tab2 = st.tabs(["📊 貝氏分析", "💬 AI 助手"])
236
+
237
+ # Tab 1: 貝氏分析
238
+ with tab1:
239
+ st.header("📊 貝氏階層模型分析")
240
+
241
+ # 載入資料
242
+ if data_source == "使用預設資料集":
243
+ # 檢查預設資料是否存在
244
+ default_data_path = "pokemon_speed_meta_results.csv"
245
+ if os.path.exists(default_data_path):
246
+ df = pd.read_csv(default_data_path)
247
+ st.success(f"✅ 已載入預設資料集({len(df)} 個屬性)")
248
+ else:
249
+ st.warning("⚠️ 找不到預設資料集,請上傳您的資料")
250
+ df = None
251
+ else:
252
+ if uploaded_file is not None:
253
+ df = pd.read_csv(uploaded_file)
254
+ st.success(f"✅ 已載入資料({len(df)} 個屬性)")
255
+ else:
256
+ df = None
257
+ st.info("📁 請在左側上傳 CSV 檔案")
258
+
259
+ if df is not None:
260
+ # 顯示資料預覽
261
+ with st.expander("👀 資料預覽"):
262
+ st.dataframe(df, use_container_width=True)
263
+
264
+ st.markdown("---")
265
+
266
+ # 分析按鈕
267
+ col1, col2, col3 = st.columns([1, 2, 1])
268
+
269
+ with col2:
270
+ analyze_button = st.button(
271
+ "🔬 開始貝氏分析",
272
+ type="primary",
273
+ use_container_width=True
274
+ )
275
+
276
+ # 執行分析
277
+ if analyze_button:
278
+ with st.spinner(f"正在執行貝氏分析... (抽樣 {n_samples} × {n_chains} 條鏈)"):
279
+ try:
280
+ # 初始化分析器
281
+ if st.session_state.analyzer is None:
282
+ st.session_state.analyzer = BayesianHierarchicalAnalyzer(st.session_state.session_id)
283
+
284
+ # 載入資料
285
+ st.session_state.analyzer.load_data(df)
286
+
287
+ # 執行分析
288
+ results = st.session_state.analyzer.run_analysis(
289
+ n_samples=n_samples,
290
+ n_tune=n_tune,
291
+ n_chains=n_chains,
292
+ target_accept=target_accept
293
+ )
294
+
295
+ st.session_state.analysis_results = results
296
+
297
+ # 生成圖表
298
+ with st.spinner("生成視覺化圖表..."):
299
+ st.session_state.trace_img = plot_trace(st.session_state.analyzer.trace)
300
+ st.session_state.posterior_img = plot_posterior(st.session_state.analyzer.trace)
301
+ st.session_state.forest_img = plot_forest(
302
+ st.session_state.analyzer.trace,
303
+ results['trial_labels']
304
+ )
305
+ st.session_state.dag_img = plot_model_dag(st.session_state.analyzer)
306
+
307
+ st.success("✅ 分析完成!")
308
+ st.balloons()
309
+
310
+ except Exception as e:
311
+ st.error(f"❌ 分析失敗: {str(e)}")
312
+
313
+ # 顯示結果
314
+ if st.session_state.analysis_results is not None:
315
+ results = st.session_state.analysis_results
316
+
317
+ st.markdown("---")
318
+ st.subheader("📊 分析結果")
319
+
320
+ # 創建 4 個子頁面
321
+ result_tabs = st.tabs([
322
+ "📊 概覽",
323
+ "📈 Trace & Posterior",
324
+ "🌲 Forest Plot",
325
+ "🔍 DAG 模型圖",
326
+ "📋 詳細報告"
327
+ ])
328
+
329
+ # Tab: 概覽
330
+ with result_tabs[0]:
331
+ st.markdown("### 🎯 整體效應摘要")
332
+
333
+ overall = results['overall']
334
+ interp = results['interpretation']
335
+
336
+ # 關鍵指標
337
+ col1, col2, col3 = st.columns(3)
338
+
339
+ with col1:
340
+ st.metric(
341
+ "d (整體效應)",
342
+ f"{overall['d_mean']:.4f}",
343
+ delta=f"HDI: [{overall['d_hdi_low']:.3f}, {overall['d_hdi_high']:.3f}]"
344
+ )
345
+
346
+ with col2:
347
+ st.metric(
348
+ "勝算比 (OR)",
349
+ f"{overall['or_mean']:.3f}",
350
+ delta=f"HDI: [{overall['or_hdi_low']:.3f}, {overall['or_hdi_high']:.3f}]"
351
+ )
352
+
353
+ with col3:
354
+ st.metric(
355
+ "sigma (異質性)",
356
+ f"{overall['sigma_mean']:.4f}",
357
+ delta=f"HDI: [{overall['sigma_hdi_low']:.3f}, {overall['sigma_hdi_high']:.3f}]"
358
+ )
359
+
360
+ st.markdown("---")
361
+
362
+ # 結果解釋
363
+ st.markdown("### 📖 結果解釋")
364
+
365
+ st.info(f"""
366
+ **整體效應**: {interp['overall_effect']}
367
+
368
+ **顯著性**: {interp['overall_significance']}
369
+
370
+ **效果大小**: {interp['effect_size']}
371
+
372
+ **異質性**: {interp['heterogeneity']}
373
+ """)
374
+
375
+ st.markdown("---")
376
+
377
+ # 收斂診斷
378
+ st.markdown("### 🔍 模型收斂診斷")
379
+
380
+ diag = results['diagnostics']
381
+
382
+ col1, col2 = st.columns(2)
383
+
384
+ with col1:
385
+ st.markdown("**R-hat 診斷** (應 < 1.1):")
386
+ if diag['rhat_d']:
387
+ st.metric("R-hat (d)", f"{diag['rhat_d']:.4f}",
388
+ delta="✓ 良好" if diag['rhat_d'] < 1.1 else "✗ 需改善")
389
+ if diag['rhat_sigma']:
390
+ st.metric("R-hat (sigma)", f"{diag['rhat_sigma']:.4f}",
391
+ delta="✓ 良好" if diag['rhat_sigma'] < 1.1 else "✗ 需改善")
392
+
393
+ with col2:
394
+ st.markdown("**有效樣本數 (ESS)**:")
395
+ if diag['ess_d']:
396
+ st.metric("ESS (d)", f"{int(diag['ess_d'])}")
397
+ if diag['ess_sigma']:
398
+ st.metric("ESS (sigma)", f"{int(diag['ess_sigma'])}")
399
+
400
+ if diag['converged']:
401
+ st.success("✅ 模型已收斂,結果可信")
402
+ else:
403
+ st.warning("⚠️ 模型可能未完全收斂,建議增加抽樣數或鏈數")
404
+
405
+ st.markdown("---")
406
+
407
+ # 摘要表格
408
+ st.markdown("### 📊 統計摘要表")
409
+ summary_df = create_summary_table(results)
410
+ st.dataframe(summary_df, use_container_width=True)
411
+
412
+ st.markdown("---")
413
+
414
+ # 各屬性結果
415
+ st.markdown("### 🎮 各屬性詳細結果")
416
+ trial_df = create_trial_results_table(results)
417
+ st.dataframe(trial_df, use_container_width=True)
418
+
419
+ st.markdown("---")
420
+
421
+ # 勝算比比較圖
422
+ st.markdown("### 📊 各屬性速度效應比較")
423
+ or_fig = plot_odds_ratio_comparison(results)
424
+ st.plotly_chart(or_fig, use_container_width=True)
425
+
426
+ # Tab: Trace & Posterior
427
+ with result_tabs[1]:
428
+ st.markdown("### 📈 Trace Plot(收斂診斷)")
429
+ st.markdown("""
430
+ **Trace Plot 用途**:
431
+ - 檢查 MCMC 抽樣是否收斂
432
+ - 左圖:抽樣軌跡(應該像「毛毛蟲」)
433
+ - 右圖:後驗分佈密度
434
+ """)
435
+
436
+ if st.session_state.trace_img:
437
+ st.image(st.session_state.trace_img, use_column_width=True)
438
+ else:
439
+ st.info("請先執行分析以生成 Trace Plot")
440
+
441
+ st.markdown("---")
442
+
443
+ st.markdown("### 📊 Posterior Plot(後驗分佈)")
444
+ st.markdown("""
445
+ **Posterior Plot 用途**:
446
+ - 顯示參數的後驗分佈
447
+ - 包含 95% HDI(最高密度區間)
448
+ - 顯示平均值
449
+ """)
450
+
451
+ if st.session_state.posterior_img:
452
+ st.image(st.session_state.posterior_img, use_column_width=True)
453
+ else:
454
+ st.info("請先執行分析以生成 Posterior Plot")
455
+
456
+ # Tab: Forest Plot
457
+ with result_tabs[2]:
458
+ st.markdown("### 🌲 Forest Plot(各屬性效應)")
459
+ st.markdown("""
460
+ **Forest Plot 用途**:
461
+ - 顯示每個屬性的速度效應(delta)
462
+ - 點:平均效應
463
+ - 線:95% HDI
464
+ - ★ 標記:顯著正效應(HDI 不包含 0)
465
+ - ☆ 標記:顯著負效應
466
+ """)
467
+
468
+ if st.session_state.forest_img:
469
+ st.image(st.session_state.forest_img, use_column_width=True)
470
+ else:
471
+ st.info("請先執行分析以生成 Forest Plot")
472
+
473
+ # Tab: DAG 模型圖
474
+ with result_tabs[3]:
475
+ st.markdown("### 🔍 模型結構圖 (DAG)")
476
+ st.markdown("""
477
+ **DAG(有向無環圖)用途**:
478
+ - 視覺化模型的階層結構
479
+ - 顯示變數之間的依賴關係
480
+ - 圓形/橢圓:隨機變數
481
+ - 矩形:觀測資料
482
+ - 菱形:推導變數
483
+ """)
484
+
485
+ if st.session_state.dag_img:
486
+ st.image(st.session_state.dag_img, use_column_width=True)
487
+ else:
488
+ st.warning("⚠️ 無法生成 DAG 圖(可能需要安裝 Graphviz)")
489
+ st.markdown("""
490
+ **安裝 Graphviz:**
491
+ - Windows: `choco install graphviz`
492
+ - Mac: `brew install graphviz`
493
+ - Ubuntu: `sudo apt-get install graphviz`
494
+ """)
495
+
496
+ # Tab: 詳細報告
497
+ with result_tabs[4]:
498
+ st.markdown("### 📋 完整分析報告")
499
+
500
+ # 生成文字報告
501
+ text_report = export_results_to_text(results)
502
+
503
+ st.text_area(
504
+ "報告內容",
505
+ text_report,
506
+ height=500
507
+ )
508
+
509
+ # 下載按鈕
510
+ st.download_button(
511
+ label="📥 下載完整報告 (.txt)",
512
+ data=text_report,
513
+ file_name=f"bayesian_report_{results['timestamp'][:10]}.txt",
514
+ mime="text/plain"
515
+ )
516
+
517
+ # Tab 2: AI 助手
518
+ with tab2:
519
+ st.header("💬 AI 分析助手")
520
+
521
+ if not st.session_state.get('api_key'):
522
+ st.warning("⚠️ 請在左側輸入您的 Google Gemini API Key 以使用 AI 助手")
523
+ elif st.session_state.analysis_results is None:
524
+ st.info("ℹ️ 請先在「貝氏分析」頁面執行分析")
525
+ else:
526
+ # 初始化 LLM 助手
527
+ if 'llm_assistant' not in st.session_state:
528
+ st.session_state.llm_assistant = BayesianLLMAssistant(
529
+ api_key=st.session_state.api_key,
530
+ session_id=st.session_state.session_id
531
+ )
532
+
533
+ # 聊天容器
534
+ chat_container = st.container()
535
+
536
+ with chat_container:
537
+ for message in st.session_state.chat_history:
538
+ with st.chat_message(message["role"]):
539
+ st.markdown(message["content"])
540
+
541
+ # 使用者輸入
542
+ if prompt := st.chat_input("詢問關於分析結果的任何問題..."):
543
+ # 添加使用者訊息
544
+ st.session_state.chat_history.append({
545
+ "role": "user",
546
+ "content": prompt
547
+ })
548
+
549
+ with st.chat_message("user"):
550
+ st.markdown(prompt)
551
+
552
+ # AI 回應
553
+ with st.chat_message("assistant"):
554
+ with st.spinner("思考中..."):
555
+ try:
556
+ response = st.session_state.llm_assistant.get_response(
557
+ user_message=prompt,
558
+ analysis_results=st.session_state.analysis_results
559
+ )
560
+ st.markdown(response)
561
+ except Exception as e:
562
+ error_msg = f"❌ 錯誤: {str(e)}\n\n請檢查 API key 或重新表達問題。"
563
+ st.error(error_msg)
564
+ response = error_msg
565
+
566
+ # 添加助手回應
567
+ st.session_state.chat_history.append({
568
+ "role": "assistant",
569
+ "content": response
570
+ })
571
+
572
+ st.markdown("---")
573
+
574
+ # 快速問題按鈕
575
+ st.subheader("💡 快速問題")
576
+
577
+ quick_questions = [
578
+ "📊 給我這次分析的總結",
579
+ "🎯 解釋 d 和勝算比",
580
+ "🔍 解釋 sigma(異質性)",
581
+ "❓ 什麼是階層模型?",
582
+ "🆚 貝氏 vs 頻率論",
583
+ "⚔️ 對戰策略建議",
584
+ "🎮 比較不同屬性"
585
+ ]
586
+
587
+ cols = st.columns(4)
588
+ for idx, question in enumerate(quick_questions):
589
+ col_idx = idx % 4
590
+ if cols[col_idx].button(question, key=f"quick_{idx}"):
591
+ # 根據問題選擇對應的方法
592
+ if "總結" in question:
593
+ response = st.session_state.llm_assistant.generate_summary(
594
+ st.session_state.analysis_results
595
+ )
596
+ elif "d 和勝算比" in question:
597
+ response = st.session_state.llm_assistant.explain_metric(
598
+ 'd',
599
+ st.session_state.analysis_results
600
+ )
601
+ elif "sigma" in question or "異質性" in question:
602
+ response = st.session_state.llm_assistant.explain_metric(
603
+ 'sigma',
604
+ st.session_state.analysis_results
605
+ )
606
+ elif "階層模型" in question:
607
+ response = st.session_state.llm_assistant.explain_hierarchical_model()
608
+ elif "貝氏" in question and "頻率論" in question:
609
+ response = st.session_state.llm_assistant.explain_bayesian_vs_frequentist()
610
+ elif "策略" in question:
611
+ response = st.session_state.llm_assistant.battle_strategy_advice(
612
+ st.session_state.analysis_results
613
+ )
614
+ elif "比較" in question:
615
+ response = st.session_state.llm_assistant.compare_types(
616
+ st.session_state.analysis_results
617
+ )
618
+ else:
619
+ response = st.session_state.llm_assistant.get_response(
620
+ question,
621
+ st.session_state.analysis_results
622
+ )
623
+
624
+ st.session_state.chat_history.append({
625
+ "role": "user",
626
+ "content": question
627
+ })
628
+
629
+ st.session_state.chat_history.append({
630
+ "role": "assistant",
631
+ "content": response
632
+ })
633
+
634
+ st.rerun()
635
+
636
+ # 重置對話按鈕
637
+ st.markdown("---")
638
+ if st.button("🔄 重置對話"):
639
+ st.session_state.llm_assistant.reset_conversation()
640
+ st.session_state.chat_history = []
641
+ st.success("✅ 對話已重置")
642
+ st.rerun()
643
+
644
+ # Footer
645
+ st.markdown("---")
646
+ st.markdown(
647
+ f"""
648
+ <div style='text-align: center'>
649
+ <p>🎲 Bayesian Hierarchical Model Analysis for Pokémon Speed | Built with Streamlit & PyMC</p>
650
+ <p>Session ID: {st.session_state.session_id[:8]} | Powered by Google Gemini 2.0 Flash</p>
651
+ </div>
652
+ """,
653
+ unsafe_allow_html=True
654
+ )
bayesian_core.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import pymc as pm
4
+ import arviz as az
5
+ import threading
6
+ from datetime import datetime
7
+ import warnings
8
+ warnings.filterwarnings('ignore')
9
+
10
+ class BayesianHierarchicalAnalyzer:
11
+ """
12
+ 貝氏階層模型分析器
13
+ 用於分析寶可夢速度對勝率的影響(跨屬性)
14
+ """
15
+
16
+ # 類別級的鎖,用於執行緒安全
17
+ _lock = threading.Lock()
18
+
19
+ # 儲存各 session 的分析結果
20
+ _session_results = {}
21
+
22
+ def __init__(self, session_id):
23
+ """
24
+ 初始化分析器
25
+
26
+ Args:
27
+ session_id: 唯一的 session 識別碼
28
+ """
29
+ self.session_id = session_id
30
+ self.df = None
31
+ self.model = None
32
+ self.trace = None
33
+
34
+ def load_data(self, csv_path_or_df):
35
+ """
36
+ 載入資料
37
+
38
+ Args:
39
+ csv_path_or_df: CSV 檔案路徑或 DataFrame
40
+
41
+ Expected columns:
42
+ - Trial_Type: 屬性名稱 (e.g., Water, Fire, Grass)
43
+ - rc: 控制組(速度慢)的勝場數
44
+ - nc: 控制組的總場數
45
+ - rt: 實驗組(速度快)的勝場數
46
+ - nt: 實驗組的總場數
47
+ """
48
+ if isinstance(csv_path_or_df, str):
49
+ self.df = pd.read_csv(csv_path_or_df)
50
+ else:
51
+ self.df = csv_path_or_df.copy()
52
+
53
+ # 驗證必要欄位
54
+ required_cols = ['Trial_Type', 'rc', 'nc', 'rt', 'nt']
55
+ missing_cols = [col for col in required_cols if col not in self.df.columns]
56
+
57
+ if missing_cols:
58
+ raise ValueError(f"資料缺少必要欄位: {missing_cols}")
59
+
60
+ return True
61
+
62
+ def validate_data(self):
63
+ """驗證資料有效性"""
64
+ if self.df is None:
65
+ raise ValueError("請先載入資料")
66
+
67
+ # 檢查數值欄位
68
+ for col in ['rc', 'nc', 'rt', 'nt']:
69
+ if not pd.api.types.is_numeric_dtype(self.df[col]):
70
+ raise ValueError(f"欄位 {col} 必須是數值類型")
71
+
72
+ # 檢查邏輯約束
73
+ if (self.df['rc'] > self.df['nc']).any():
74
+ raise ValueError("rc (勝場數) 不能大於 nc (總場數)")
75
+
76
+ if (self.df['rt'] > self.df['nt']).any():
77
+ raise ValueError("rt (勝場數) 不能大於 nt (總場數)")
78
+
79
+ return True
80
+
81
+ def run_analysis(self, n_samples=2000, n_tune=1000, n_chains=2, target_accept=0.95):
82
+ """
83
+ 執行貝氏階層模型分析
84
+
85
+ Args:
86
+ n_samples: MCMC 抽樣數
87
+ n_tune: 調整期樣本數
88
+ n_chains: 鏈數
89
+ target_accept: 目標接受率
90
+
91
+ Returns:
92
+ dict: 包含所有分析結果的字典
93
+ """
94
+ with self._lock:
95
+ try:
96
+ self.validate_data()
97
+
98
+ # 準備資料
99
+ trial_labels = self.df['Trial_Type'].values
100
+ num_trials = len(self.df)
101
+
102
+ # 建立模型
103
+ with pm.Model() as self.model:
104
+ # --- 先驗分佈 (Priors) ---
105
+ d = pm.Normal('d', mu=0, sigma=10)
106
+ tau = pm.Gamma('tau', alpha=0.001, beta=0.001)
107
+ sigma = pm.Deterministic('sigma', 1 / pm.math.sqrt(tau))
108
+
109
+ # --- 各屬性特定效應 (Trial-specific effects) ---
110
+ mu = pm.Normal('mu', mu=0, sigma=10, shape=num_trials)
111
+ delta = pm.Normal('delta', mu=d, sigma=1 / pm.math.sqrt(tau), shape=num_trials)
112
+
113
+ # --- 轉換與似然函數 (Logit Link & Likelihood) ---
114
+ pc = pm.Deterministic('pc', pm.math.invlogit(mu))
115
+ pt = pm.Deterministic('pt', pm.math.invlogit(mu + delta))
116
+
117
+ rc_obs = pm.Binomial('rc_obs', n=self.df['nc'].values, p=pc, observed=self.df['rc'].values)
118
+ rt_obs = pm.Binomial('rt_obs', n=self.df['nt'].values, p=pt, observed=self.df['rt'].values)
119
+
120
+ # --- 其他統計量 ---
121
+ delta_new = pm.Normal('delta_new', mu=d, sigma=1 / pm.math.sqrt(tau))
122
+ or_speed = pm.Deterministic('or_speed', pm.math.exp(d))
123
+
124
+ # 執行 MCMC 抽樣
125
+ self.trace = pm.sample(
126
+ draws=n_samples,
127
+ tune=n_tune,
128
+ chains=n_chains,
129
+ target_accept=target_accept,
130
+ return_inferencedata=True,
131
+ progressbar=False # 在 Streamlit 中關閉進度條
132
+ )
133
+
134
+ # 生成摘要統計
135
+ summary = az.summary(self.trace, var_names=['d', 'sigma', 'or_speed'], hdi_prob=0.95)
136
+
137
+ # 計算各屬性的 delta 統計量
138
+ delta_posterior = self.trace.posterior['delta'].values.reshape(-1, num_trials)
139
+ delta_mean = delta_posterior.mean(axis=0)
140
+ delta_std = delta_posterior.std(axis=0)
141
+ delta_hdi = az.hdi(self.trace, var_names=['delta'], hdi_prob=0.95)['delta'].values
142
+
143
+ # 判斷顯著性(HDI 不包含 0)
144
+ delta_significant = (delta_hdi[:, 0] > 0) | (delta_hdi[:, 1] < 0)
145
+
146
+ # 計算控制組和實驗組的勝率
147
+ pc_posterior = self.trace.posterior['pc'].values.reshape(-1, num_trials)
148
+ pt_posterior = self.trace.posterior['pt'].values.reshape(-1, num_trials)
149
+
150
+ pc_mean = pc_posterior.mean(axis=0)
151
+ pt_mean = pt_posterior.mean(axis=0)
152
+
153
+ # 整理結果
154
+ results = {
155
+ 'timestamp': datetime.now().isoformat(),
156
+ 'n_trials': num_trials,
157
+ 'trial_labels': trial_labels.tolist(),
158
+
159
+ # 整體效應
160
+ 'overall': {
161
+ 'd_mean': float(summary.loc['d', 'mean']),
162
+ 'd_sd': float(summary.loc['d', 'sd']),
163
+ 'd_hdi_low': float(summary.loc['d', 'hdi_2.5%']),
164
+ 'd_hdi_high': float(summary.loc['d', 'hdi_97.5%']),
165
+
166
+ 'sigma_mean': float(summary.loc['sigma', 'mean']),
167
+ 'sigma_sd': float(summary.loc['sigma', 'sd']),
168
+ 'sigma_hdi_low': float(summary.loc['sigma', 'hdi_2.5%']),
169
+ 'sigma_hdi_high': float(summary.loc['sigma', 'hdi_97.5%']),
170
+
171
+ 'or_mean': float(summary.loc['or_speed', 'mean']),
172
+ 'or_sd': float(summary.loc['or_speed', 'sd']),
173
+ 'or_hdi_low': float(summary.loc['or_speed', 'hdi_2.5%']),
174
+ 'or_hdi_high': float(summary.loc['or_speed', 'hdi_97.5%']),
175
+ },
176
+
177
+ # 各屬性的效應
178
+ 'by_trial': {
179
+ 'delta_mean': delta_mean.tolist(),
180
+ 'delta_std': delta_std.tolist(),
181
+ 'delta_hdi_low': delta_hdi[:, 0].tolist(),
182
+ 'delta_hdi_high': delta_hdi[:, 1].tolist(),
183
+ 'delta_significant': delta_significant.tolist(),
184
+ 'pc_mean': pc_mean.tolist(),
185
+ 'pt_mean': pt_mean.tolist(),
186
+ },
187
+
188
+ # 原始資料
189
+ 'data': self.df.to_dict('records'),
190
+
191
+ # 模型參數
192
+ 'model_params': {
193
+ 'n_samples': n_samples,
194
+ 'n_tune': n_tune,
195
+ 'n_chains': n_chains,
196
+ 'target_accept': target_accept
197
+ },
198
+
199
+ # 收斂診斷
200
+ 'diagnostics': self._compute_diagnostics(summary),
201
+
202
+ # 解釋
203
+ 'interpretation': self._interpret_results(
204
+ summary.loc['or_speed', 'mean'],
205
+ summary.loc['or_speed', 'hdi_2.5%'],
206
+ summary.loc['or_speed', 'hdi_97.5%'],
207
+ summary.loc['sigma', 'mean']
208
+ )
209
+ }
210
+
211
+ # 儲存到 session results
212
+ self._session_results[self.session_id] = results
213
+
214
+ return results
215
+
216
+ except Exception as e:
217
+ raise Exception(f"分析失敗: {str(e)}")
218
+
219
+ def _compute_diagnostics(self, summary):
220
+ """計算收斂診斷指標"""
221
+ try:
222
+ # R-hat (應該接近 1.0)
223
+ rhat_d = float(summary.loc['d', 'r_hat']) if 'r_hat' in summary.columns else None
224
+ rhat_sigma = float(summary.loc['sigma', 'r_hat']) if 'r_hat' in summary.columns else None
225
+
226
+ # ESS (有效樣本數)
227
+ ess_d = float(summary.loc['d', 'ess_bulk']) if 'ess_bulk' in summary.columns else None
228
+ ess_sigma = float(summary.loc['sigma', 'ess_bulk']) if 'ess_bulk' in summary.columns else None
229
+
230
+ return {
231
+ 'rhat_d': rhat_d,
232
+ 'rhat_sigma': rhat_sigma,
233
+ 'ess_d': ess_d,
234
+ 'ess_sigma': ess_sigma,
235
+ 'converged': (rhat_d is None or rhat_d < 1.1) and (rhat_sigma is None or rhat_sigma < 1.1)
236
+ }
237
+ except:
238
+ return {
239
+ 'converged': None,
240
+ 'rhat_d': None,
241
+ 'rhat_sigma': None,
242
+ 'ess_d': None,
243
+ 'ess_sigma': None
244
+ }
245
+
246
+ def _interpret_results(self, or_mean, or_low, or_high, sigma_mean):
247
+ """解釋分析結果"""
248
+ # 整體效應顯著性
249
+ if or_low > 1:
250
+ overall_effect = "速度快的寶可夢顯著更容易獲勝"
251
+ overall_significance = "顯著正效應"
252
+ elif or_high < 1:
253
+ overall_effect = "速度慢的寶可夢顯著更容易獲勝(罕見)"
254
+ overall_significance = "顯著負效應"
255
+ else:
256
+ overall_effect = "速度對勝率無顯著影響"
257
+ overall_significance = "不顯著"
258
+
259
+ # 效果大小
260
+ if or_mean > 2:
261
+ effect_size = "大效果 (OR > 2)"
262
+ elif or_mean > 1.5:
263
+ effect_size = "中等效果 (OR > 1.5)"
264
+ elif or_mean > 1:
265
+ effect_size = "小效果 (OR > 1)"
266
+ elif or_mean == 1:
267
+ effect_size = "無差異 (OR = 1)"
268
+ else:
269
+ effect_size = "反向效果 (OR < 1)"
270
+
271
+ # 異質性評估
272
+ if sigma_mean > 0.5:
273
+ heterogeneity = "高異質性 - 不同屬性對速度的反應差異很大"
274
+ elif sigma_mean > 0.3:
275
+ heterogeneity = "中等異質性 - 不同屬性對速度的反應有一定差異"
276
+ else:
277
+ heterogeneity = "低異質性 - 不同屬性對速度的反應相似"
278
+
279
+ return {
280
+ 'overall_effect': overall_effect,
281
+ 'overall_significance': overall_significance,
282
+ 'effect_size': effect_size,
283
+ 'heterogeneity': heterogeneity
284
+ }
285
+
286
+ def get_model_graph(self):
287
+ """生成模型 DAG 圖(返回 graphviz 物件)"""
288
+ if self.model is None:
289
+ raise ValueError("請先執行分析")
290
+
291
+ try:
292
+ gv = pm.model_to_graphviz(self.model)
293
+ return gv
294
+ except Exception as e:
295
+ raise Exception(f"無法生成 DAG 圖: {str(e)}")
296
+
297
+ @classmethod
298
+ def get_session_results(cls, session_id):
299
+ """獲取特定 session 的結果"""
300
+ return cls._session_results.get(session_id)
301
+
302
+ @classmethod
303
+ def clear_session_results(cls, session_id):
304
+ """清除特定 session 的結果"""
305
+ if session_id in cls._session_results:
306
+ del cls._session_results[session_id]
bayesian_llm_assistant.py ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import json
3
+
4
+ class BayesianLLMAssistant:
5
+ """
6
+ 貝氏階層模型 LLM 問答助手
7
+ 協助用戶理解貝氏分析結果
8
+ """
9
+
10
+ def __init__(self, api_key, session_id):
11
+ """
12
+ 初始化 LLM 助手
13
+
14
+ Args:
15
+ api_key: Google Gemini API key
16
+ session_id: 唯一的 session 識別碼
17
+ """
18
+ genai.configure(api_key=api_key)
19
+ self.model = genai.GenerativeModel('gemini-2.0-flash-exp')
20
+ self.session_id = session_id
21
+ self.conversation_history = []
22
+
23
+ # 系統提示詞
24
+ self.system_prompt = """You are an expert Bayesian statistician specializing in hierarchical models and meta-analysis, particularly in the context of Pokémon battle statistics.
25
+
26
+ **IMPORTANT - Language Instruction:**
27
+ - Always respond in the SAME language as the user's question
28
+ - If user asks in Traditional Chinese (繁體中文), respond in Traditional Chinese
29
+ - If user asks in English, respond in English
30
+ - Maintain language consistency throughout the conversation
31
+
32
+ 你是一位精通貝氏階層模型和統合分析的統計專家,特別專注於寶可夢對戰統計分析。
33
+
34
+ Your role is to help users understand Bayesian hierarchical model results analyzing how speed affects win rates across different Pokémon types.
35
+ 你的角色是幫助使用者理解貝氏階層模型分析結果,了解速度如何影響不同屬性寶可夢的勝率。
36
+
37
+ You should:
38
+ 1. Explain Bayesian concepts in simple, accessible terms
39
+ 2. Interpret posterior distributions, HDI (Highest Density Interval), and credible intervals
40
+ 3. Explain hierarchical structure and why it's useful
41
+ 4. Help users understand heterogeneity (sigma) between types
42
+ 5. Discuss the practical significance of results for Pokémon battles
43
+ 6. Provide insights about which types benefit most from speed
44
+ 7. Suggest battle strategies based on the statistical findings
45
+ 8. Clarify differences between Bayesian and frequentist approaches
46
+ 9. Explain MCMC diagnostics (R-hat, ESS) when relevant
47
+
48
+ 你應該:
49
+ 1. 用簡單易懂的方式解釋貝氏概念
50
+ 2. 詮釋後驗分佈、HDI(最高密度區間)和可信區間
51
+ 3. 解釋階層結構及其優勢
52
+ 4. 幫助使用者理解屬性間的異質性(sigma)
53
+ 5. 討論統計結果對寶可夢對戰的實際意義
54
+ 6. 提供哪些屬性最受益於速度的見解
55
+ 7. 根據統計發現提出對戰策略建議
56
+ 8. 說明貝氏方法與頻率論方法的差異
57
+ 9. 適時解釋 MCMC 診斷指標(R-hat、ESS)
58
+
59
+ Key concepts to explain when relevant:
60
+ - **Bayesian Hierarchical Model**: Borrows strength across types, shrinkage effect
61
+ - **Prior & Posterior**: How data updates beliefs
62
+ - **HDI (Highest Density Interval)**: 95% most credible values
63
+ - **d (overall effect)**: Average log odds ratio across all types
64
+ - **sigma (between-type variation)**: How much types differ in speed effect
65
+ - **delta (type-specific effects)**: Each type's individual speed effect
66
+ - **Odds Ratio**: exp(d) - how much more likely fast Pokémon are to win
67
+ - **MCMC**: Markov Chain Monte Carlo sampling method
68
+ - **Convergence**: R-hat < 1.1, good ESS (effective sample size)
69
+
70
+ 重要概念解釋(當相關時):
71
+ - **貝氏階層模型**:跨屬性借用資訊,收縮效應
72
+ - **先驗與後驗**:資料如何更新信念
73
+ - **HDI(最高密度區間)**:95% 最可信的數值範圍
74
+ - **d(整體效應)**:所有屬性的平均對數勝算比
75
+ - **sigma(屬性間變異)**:不同屬性的速度效應差異程度
76
+ - **delta(屬性特定效應)**:每個屬性的個別速度效應
77
+ - **勝算比**:exp(d) - 速度快的寶可夢獲勝的可能性倍數
78
+ - **MCMC**:馬可夫鏈蒙地卡羅抽樣方法
79
+ - **收斂性**:R-hat < 1.1,良好的 ESS(有效樣本數)
80
+
81
+ When discussing Pokémon battles:
82
+ - Connect statistical findings to battle mechanics
83
+ - Explain why speed matters (determines attack order)
84
+ - Discuss type advantages and strategies
85
+ - Use Pokémon-specific terminology naturally
86
+ - Consider the meta-game implications
87
+
88
+ 討論寶可夢對戰時:
89
+ - 將統計發現連結到對戰機制
90
+ - 解釋為何速度重要(決定攻擊順序)
91
+ - 討論屬性優勢和策略
92
+ - 自然地使用寶可夢相關術語
93
+ - 考慮競技環境的影響
94
+
95
+ Always be clear, educational, and engaging. Use examples when helpful.
96
+ Format responses with proper markdown for better readability.
97
+
98
+ 請務必清晰、具教育性、引人入勝。適時使用範例說明。使用適當的 Markdown 格式以提升可讀性。"""
99
+
100
+ def get_response(self, user_message, analysis_results=None):
101
+ """
102
+ 獲取 AI 回應
103
+
104
+ Args:
105
+ user_message: 用戶訊息
106
+ analysis_results: 分析結果字典(可選)
107
+
108
+ Returns:
109
+ str: AI 回應
110
+ """
111
+ # 準備上下文資訊
112
+ context = ""
113
+ if analysis_results:
114
+ context = self._prepare_context(analysis_results)
115
+
116
+ # 添加用戶訊息到歷史
117
+ self.conversation_history.append({
118
+ "role": "user",
119
+ "content": user_message
120
+ })
121
+
122
+ try:
123
+ # 構建完整的提示詞
124
+ full_prompt = self.system_prompt
125
+
126
+ if context:
127
+ full_prompt += f"\n\n## Current Analysis Context:\n{context}"
128
+
129
+ # 構建對話歷史文字
130
+ conversation_text = "\n\n## Conversation History:\n"
131
+ for msg in self.conversation_history[:-1]:
132
+ role = "User" if msg["role"] == "user" else "Assistant"
133
+ conversation_text += f"\n{role}: {msg['content']}\n"
134
+
135
+ # 組合最終提示詞
136
+ final_prompt = full_prompt + conversation_text + f"\nUser: {user_message}\n\nAssistant:"
137
+
138
+ # 調用 Gemini API
139
+ response = self.model.generate_content(
140
+ final_prompt,
141
+ generation_config=genai.types.GenerationConfig(
142
+ temperature=0.7,
143
+ max_output_tokens=4000,
144
+ )
145
+ )
146
+
147
+ assistant_message = response.text
148
+
149
+ # 添加助手回應到歷史
150
+ self.conversation_history.append({
151
+ "role": "assistant",
152
+ "content": assistant_message
153
+ })
154
+
155
+ return assistant_message
156
+
157
+ except Exception as e:
158
+ return f"❌ Error: {str(e)}\n\nPlease check your API key and try again."
159
+
160
+ def _prepare_context(self, results):
161
+ """準備分析結果的上下文資訊"""
162
+
163
+ if not results:
164
+ return "目前尚無分析結果。No analysis results available yet."
165
+
166
+ overall = results['overall']
167
+ interp = results['interpretation']
168
+ diag = results['diagnostics']
169
+
170
+ # 找出顯著的屬性
171
+ sig_types = [
172
+ results['trial_labels'][i]
173
+ for i, sig in enumerate(results['by_trial']['delta_significant'])
174
+ if sig
175
+ ]
176
+
177
+ context = f"""
178
+ ## Current Bayesian Hierarchical Model Analysis | 目前的貝氏階層模型分析
179
+
180
+ ### Overall Effect | 整體效應
181
+ - **d (Log Odds Ratio) | d(對數勝算比)**:
182
+ - Mean | 平均: {overall['d_mean']:.4f}
183
+ - SD | 標準差: {overall['d_sd']:.4f}
184
+ - 95% HDI: [{overall['d_hdi_low']:.4f}, {overall['d_hdi_high']:.4f}]
185
+
186
+ - **sigma (Between-type Variation) | sigma(屬性間變異)**:
187
+ - Mean | 平均: {overall['sigma_mean']:.4f}
188
+ - SD | 標準差: {overall['sigma_sd']:.4f}
189
+ - 95% HDI: [{overall['sigma_hdi_low']:.4f}, {overall['sigma_hdi_high']:.4f}]
190
+
191
+ - **Odds Ratio | 勝算比**:
192
+ - Mean | 平均: {overall['or_mean']:.4f}
193
+ - SD | 標準差: {overall['or_sd']:.4f}
194
+ - 95% HDI: [{overall['or_hdi_low']:.4f}, {overall['or_hdi_high']:.4f}]
195
+
196
+ ### Model Diagnostics | 模型診斷
197
+ - **R-hat (d)**: {diag['rhat_d']:.4f if diag['rhat_d'] else 'N/A'} {'✓' if diag['rhat_d'] and diag['rhat_d'] < 1.1 else '✗'}
198
+ - **R-hat (sigma)**: {diag['rhat_sigma']:.4f if diag['rhat_sigma'] else 'N/A'} {'✓' if diag['rhat_sigma'] and diag['rhat_sigma'] < 1.1 else '✗'}
199
+ - **ESS (d)**: {int(diag['ess_d']) if diag['ess_d'] else 'N/A'}
200
+ - **ESS (sigma)**: {int(diag['ess_sigma']) if diag['ess_sigma'] else 'N/A'}
201
+ - **Convergence | 收斂狀態**: {'✓ Converged 已收斂' if diag['converged'] else '✗ Not Converged 未收斂'}
202
+
203
+ ### Interpretation | 結果解釋
204
+ - **Overall Effect | 整體效應**: {interp['overall_effect']}
205
+ - **Significance | 顯著性**: {interp['overall_significance']}
206
+ - **Effect Size | 效果大小**: {interp['effect_size']}
207
+ - **Heterogeneity | 異質性**: {interp['heterogeneity']}
208
+
209
+ ### Significant Types | 顯著的屬性
210
+ {len(sig_types)} out of {results['n_trials']} types show significant speed effects:
211
+ {len(sig_types)} 個屬性(共 {results['n_trials']} 個)顯示顯著的速度效應:
212
+ {', '.join(sig_types) if sig_types else 'None 無'}
213
+
214
+ ### Number of Types Analyzed | 分析的屬性數量
215
+ {results['n_trials']} types in total 共 {results['n_trials']} 個屬性
216
+
217
+ ### Key Finding | 關鍵發現
218
+ {
219
+ f"On average, faster Pokémon are {overall['or_mean']:.2f} times more likely to win than slower ones (95% HDI: [{overall['or_hdi_low']:.2f}, {overall['or_hdi_high']:.2f}]). 平均而言,速度較快的寶可夢獲勝的可能性是速度較慢者的 {overall['or_mean']:.2f} 倍(95% HDI: [{overall['or_hdi_low']:.2f}, {overall['or_hdi_high']:.2f}])。"
220
+ if overall['or_mean'] > 1
221
+ else f"Interestingly, the data suggests no clear speed advantage or even a slight disadvantage. 有趣的是,資料顯示速度並無明顯優勢,甚至可能略有劣勢。"
222
+ }
223
+
224
+ The variation between types (sigma = {overall['sigma_mean']:.3f}) indicates {interp['heterogeneity'].lower()}.
225
+ 屬性間的變異(sigma = {overall['sigma_mean']:.3f})表示{interp['heterogeneity'].lower()}。
226
+ """
227
+ return context
228
+
229
+ def generate_summary(self, analysis_results):
230
+ """自動生成分析結果總結"""
231
+
232
+ summary_prompt = """請根據提供的貝氏階層模型分析結果生成一份完整的總結報告,包含:
233
+
234
+ 1. **模型目的**:簡述這個階層模型在分析什麼
235
+ 2. **整體發現**:
236
+ - 速度對勝率有什麼整體影響?
237
+ - d 和勝算比告訴我們什麼?
238
+ - HDI 的意義是什麼?
239
+ 3. **屬性間差異**:
240
+ - sigma 告訴我們什麼?
241
+ - 哪些屬性特別受速度影響?
242
+ 4. **模型品質**:
243
+ - 模型收斂得好嗎?(R-hat、ESS)
244
+ - 結果可信嗎?
245
+ 5. **實戰啟示**:
246
+ - 訓練師如何運用這些資訊?
247
+ - 哪些屬性應該優先考慮速度?
248
+
249
+ 請用清楚的繁體中文 Markdown 格式撰寫,包含適當的章節標題。"""
250
+
251
+ return self.get_response(summary_prompt, analysis_results)
252
+
253
+ def explain_metric(self, metric_name, analysis_results):
254
+ """解釋特定指標"""
255
+
256
+ metric_explanations = {
257
+ 'd': 'd (整體對數勝算比)',
258
+ 'sigma': 'sigma (屬性間變異)',
259
+ 'or_speed': 'Odds Ratio (勝算比)',
260
+ 'hdi': '95% HDI (最高密度區間)',
261
+ 'delta': 'delta (屬性特定效應)',
262
+ 'rhat': 'R-hat (收斂診斷)',
263
+ 'ess': 'ESS (有效樣本數)'
264
+ }
265
+
266
+ metric_display = metric_explanations.get(metric_name, metric_name)
267
+
268
+ explain_prompt = f"""請在這次貝氏階層模型分析的脈絡下,解釋以下指標:
269
+
270
+ 指標:{metric_display}
271
+
272
+ 請包含:
273
+ 1. 這個指標在貝氏統計中測量什麼?
274
+ 2. 在本次分析中得到的數值是多少?
275
+ 3. 如何從寶可夢對戰的角度詮釋這個數值?
276
+ 4. 與頻率論統計的對應指標有何不同?
277
+ 5. 有什麼需要注意的限制或注意事項?
278
+
279
+ 請用繁體中文回答。"""
280
+
281
+ return self.get_response(explain_prompt, analysis_results)
282
+
283
+ def explain_bayesian_vs_frequentist(self):
284
+ """解釋貝氏與頻率論的差異"""
285
+
286
+ explain_prompt = """請用簡單的方式解釋貝氏統計和頻率論統計的差異,特別是在寶可夢對戰分析的情境下。
287
+
288
+ 請涵蓋:
289
+ 1. 兩者的根本哲學差異是什麼?
290
+ 2. p 值 vs HDI(可信區間)有什麼不同?
291
+ 3. 為什麼我們用階層模型來分析多個屬性?
292
+ 4. 貝氏方法的優勢和限制是什麼?
293
+ 5. 什麼時候該用貝氏、什麼時候該用頻率論?
294
+
295
+ 請用寶可夢的實際例子讓說明更具體易懂,全程使用繁體中文。"""
296
+
297
+ return self.get_response(explain_prompt, None)
298
+
299
+ def explain_hierarchical_model(self):
300
+ """解釋階層模型的概念"""
301
+
302
+ explain_prompt = """請用簡單的方式解釋貝氏階層模型,特別是在寶可夢屬性分析的情境下。
303
+
304
+ 請涵蓋:
305
+ 1. 什麼是階層模型?為什麼要用階層結構?
306
+ 2. 「借用資訊」(borrowing strength) 是什麼意思?
307
+ 3. 收縮效應 (shrinkage) 如何運作?
308
+ 4. 為什麼階層模型適合分析多個屬性?
309
+ 5. d、sigma、delta 之間的關係是什麼?
310
+
311
+ 請用寶可夢的實際例子讓說明更具體易懂,全程使用繁體中文。"""
312
+
313
+ return self.get_response(explain_prompt, None)
314
+
315
+ def battle_strategy_advice(self, analysis_results):
316
+ """提供對戰策略建議"""
317
+
318
+ strategy_prompt = """根據貝氏階層模型的分析結果,請為寶可夢訓練師提供實際的對戰策略建議。
319
+
320
+ 請考慮:
321
+ 1. 整體而言,速度對勝率的影響有多大?
322
+ 2. 哪些屬性特別受益於速度?哪些不受影響?
323
+ 3. 訓練師在組建隊伍時應該如何權衡速度?
324
+ 4. 有沒有屬性可以忽略速度、專注其他數值?
325
+ 5. 對競技對戰有什麼啟示?
326
+
327
+ 請具體且可操作,使用繁體中文回答。"""
328
+
329
+ return self.get_response(strategy_prompt, analysis_results)
330
+
331
+ def compare_types(self, analysis_results):
332
+ """比較不同屬性"""
333
+
334
+ compare_prompt = """請比較分析結果中不同屬性對速度的反應差異。
335
+
336
+ 請說明:
337
+ 1. 哪些屬性對速度最敏感?為什麼?
338
+ 2. 哪些屬性對速度不敏感?可能的原因是什麼?
339
+ 3. 屬性間的異質性(sigma)告訴我們什麼?
340
+ 4. 有沒有令人意外的發現?
341
+ 5. 這些差異對組隊策略有什麼啟示?
342
+
343
+ 請用繁體中文回答。"""
344
+
345
+ return self.get_response(compare_prompt, analysis_results)
346
+
347
+ def reset_conversation(self):
348
+ """重置對話歷史"""
349
+ self.conversation_history = []
bayesian_utils.py ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import plotly.graph_objects as go
2
+ import plotly.express as px
3
+ import pandas as pd
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+ import matplotlib
7
+ matplotlib.use('Agg') # 使用非互動式後端
8
+ import arviz as az
9
+ import io
10
+ import base64
11
+ from PIL import Image
12
+
13
+ def plot_trace(trace, var_names=['d', 'sigma']):
14
+ """
15
+ 繪製 Trace Plot(MCMC 收斂診斷)
16
+
17
+ Args:
18
+ trace: ArviZ InferenceData 物件
19
+ var_names: 要繪製的變數名稱
20
+
21
+ Returns:
22
+ PIL Image
23
+ """
24
+ fig, axes = plt.subplots(len(var_names), 2, figsize=(14, 4 * len(var_names)))
25
+ if len(var_names) == 1:
26
+ axes = axes.reshape(1, -1)
27
+
28
+ az.plot_trace(trace, var_names=var_names, axes=axes)
29
+ plt.tight_layout()
30
+
31
+ # 轉換為圖片
32
+ buf = io.BytesIO()
33
+ plt.savefig(buf, format='png', dpi=300, bbox_inches='tight')
34
+ buf.seek(0)
35
+ img = Image.open(buf)
36
+ plt.close()
37
+
38
+ return img
39
+
40
+ def plot_posterior(trace, var_names=['d', 'sigma', 'or_speed'], hdi_prob=0.95):
41
+ """
42
+ 繪製後驗分佈圖
43
+
44
+ Args:
45
+ trace: ArviZ InferenceData 物件
46
+ var_names: 要繪製的變數名稱
47
+ hdi_prob: HDI 機率
48
+
49
+ Returns:
50
+ PIL Image
51
+ """
52
+ fig = az.plot_posterior(trace, var_names=var_names, hdi_prob=hdi_prob, figsize=(14, 5))
53
+ plt.tight_layout()
54
+
55
+ # 轉換為圖片
56
+ buf = io.BytesIO()
57
+ plt.savefig(buf, format='png', dpi=300, bbox_inches='tight')
58
+ buf.seek(0)
59
+ img = Image.open(buf)
60
+ plt.close()
61
+
62
+ return img
63
+
64
+ def plot_forest(trace, trial_labels, title='Effect of Speed on Win Rate by Type'):
65
+ """
66
+ 繪製 Forest Plot(各屬性效應)
67
+
68
+ Args:
69
+ trace: ArviZ InferenceData 物件
70
+ trial_labels: 屬性標籤列表
71
+ title: 圖表標題
72
+
73
+ Returns:
74
+ PIL Image
75
+ """
76
+ num_trials = len(trial_labels)
77
+
78
+ # 計算統計量
79
+ delta_posterior = trace.posterior['delta'].values.reshape(-1, num_trials)
80
+ delta_mean = delta_posterior.mean(axis=0)
81
+ delta_hdi = az.hdi(trace, var_names=['delta'], hdi_prob=0.95)['delta'].values
82
+
83
+ # 建立圖表
84
+ fig, ax = plt.subplots(figsize=(12, max(10, num_trials * 0.4)))
85
+ y_pos = np.arange(num_trials)
86
+
87
+ # 繪製信賴區間(橫線)
88
+ ax.hlines(y_pos, delta_hdi[:, 0], delta_hdi[:, 1], color='steelblue', linewidth=3, label='95% HDI')
89
+
90
+ # 繪製平均值(點)
91
+ ax.scatter(delta_mean, y_pos, color='darkblue', s=120, zorder=3,
92
+ edgecolors='white', linewidth=1.5, label='Mean')
93
+
94
+ # 標註顯著的點
95
+ for i, (mean, hdi) in enumerate(zip(delta_mean, delta_hdi)):
96
+ if hdi[0] > 0: # 顯著正效應
97
+ ax.text(mean, i, ' ★', fontsize=15, ha='left', va='center', color='gold')
98
+ elif hdi[1] < 0: # 顯著負效應
99
+ ax.text(mean, i, ' ☆', fontsize=15, ha='left', va='center', color='red')
100
+
101
+ # 設定軸
102
+ ax.set_yticks(y_pos)
103
+ ax.set_yticklabels(trial_labels, fontsize=11)
104
+ ax.invert_yaxis()
105
+ ax.axvline(0, color='red', linestyle='--', linewidth=2, label='No Effect (δ=0)')
106
+ ax.set_xlabel('Delta (Log Odds Ratio)', fontsize=13)
107
+ ax.set_title(title, fontsize=15, fontweight='bold', pad=20)
108
+ ax.legend(loc='lower right')
109
+ ax.grid(axis='x', alpha=0.3)
110
+
111
+ plt.tight_layout()
112
+
113
+ # 轉換為圖片
114
+ buf = io.BytesIO()
115
+ plt.savefig(buf, format='png', dpi=300, bbox_inches='tight')
116
+ buf.seek(0)
117
+ img = Image.open(buf)
118
+ plt.close()
119
+
120
+ return img
121
+
122
+ def plot_model_dag(analyzer):
123
+ """
124
+ 繪製模型 DAG 圖
125
+
126
+ Args:
127
+ analyzer: BayesianHierarchicalAnalyzer 物件
128
+
129
+ Returns:
130
+ PIL Image 或 None
131
+ """
132
+ try:
133
+ gv = analyzer.get_model_graph()
134
+
135
+ # 轉換為 PNG
136
+ png_bytes = gv.pipe(format='png')
137
+
138
+ # 轉換為 PIL Image
139
+ img = Image.open(io.BytesIO(png_bytes))
140
+
141
+ return img
142
+ except Exception as e:
143
+ print(f"無法生成 DAG 圖: {e}")
144
+ return None
145
+
146
+ def create_summary_table(results):
147
+ """
148
+ 創建結果摘要表格
149
+
150
+ Args:
151
+ results: 分析結果字典
152
+
153
+ Returns:
154
+ pandas DataFrame
155
+ """
156
+ overall = results['overall']
157
+
158
+ summary_data = {
159
+ '參數': ['d (整體效應)', 'sigma (屬性間變異)', 'or_speed (勝算比)'],
160
+ '平均值': [
161
+ f"{overall['d_mean']:.4f}",
162
+ f"{overall['sigma_mean']:.4f}",
163
+ f"{overall['or_mean']:.4f}"
164
+ ],
165
+ '標準差': [
166
+ f"{overall['d_sd']:.4f}",
167
+ f"{overall['sigma_sd']:.4f}",
168
+ f"{overall['or_sd']:.4f}"
169
+ ],
170
+ '95% HDI 下界': [
171
+ f"{overall['d_hdi_low']:.4f}",
172
+ f"{overall['sigma_hdi_low']:.4f}",
173
+ f"{overall['or_hdi_low']:.4f}"
174
+ ],
175
+ '95% HDI 上界': [
176
+ f"{overall['d_hdi_high']:.4f}",
177
+ f"{overall['sigma_hdi_high']:.4f}",
178
+ f"{overall['or_hdi_high']:.4f}"
179
+ ]
180
+ }
181
+
182
+ return pd.DataFrame(summary_data)
183
+
184
+ def create_trial_results_table(results):
185
+ """
186
+ 創建各屬性結果表格
187
+
188
+ Args:
189
+ results: 分析結果字典
190
+
191
+ Returns:
192
+ pandas DataFrame
193
+ """
194
+ trial_labels = results['trial_labels']
195
+ by_trial = results['by_trial']
196
+ data = results['data']
197
+
198
+ trial_data = {
199
+ '屬性': trial_labels,
200
+ 'Delta (平均)': [f"{x:.4f}" for x in by_trial['delta_mean']],
201
+ 'Delta (標準差)': [f"{x:.4f}" for x in by_trial['delta_std']],
202
+ '95% HDI 下界': [f"{x:.4f}" for x in by_trial['delta_hdi_low']],
203
+ '95% HDI 上界': [f"{x:.4f}" for x in by_trial['delta_hdi_high']],
204
+ '顯著性': ['★ 顯著' if sig else '不顯著' for sig in by_trial['delta_significant']],
205
+ '控制組勝率': [f"{x:.2%}" for x in by_trial['pc_mean']],
206
+ '實驗組勝率': [f"{x:.2%}" for x in by_trial['pt_mean']],
207
+ '控制組 (勝/總)': [f"{d['rc']}/{d['nc']}" for d in data],
208
+ '實驗組 (勝/總)': [f"{d['rt']}/{d['nt']}" for d in data]
209
+ }
210
+
211
+ return pd.DataFrame(trial_data)
212
+
213
+ def export_results_to_text(results):
214
+ """
215
+ 匯出結果為純文字格式
216
+
217
+ Args:
218
+ results: 分析結果字典
219
+
220
+ Returns:
221
+ str: 格式化的文字報告
222
+ """
223
+ overall = results['overall']
224
+ interp = results['interpretation']
225
+ diag = results['diagnostics']
226
+
227
+ report = f"""
228
+ ==============================================
229
+ 貝氏階層模型分析報告
230
+ ==============================================
231
+
232
+ 分析時間: {results['timestamp']}
233
+ 屬性數量: {results['n_trials']}
234
+
235
+ ----------------------------------------------
236
+ 1. 整體效應摘要
237
+ ----------------------------------------------
238
+ d (整體效應 - Log OR):
239
+ - 平均值: {overall['d_mean']:.4f}
240
+ - 標準差: {overall['d_sd']:.4f}
241
+ - 95% HDI: [{overall['d_hdi_low']:.4f}, {overall['d_hdi_high']:.4f}]
242
+
243
+ sigma (屬性間變異):
244
+ - 平均值: {overall['sigma_mean']:.4f}
245
+ - 標準差: {overall['sigma_sd']:.4f}
246
+ - 95% HDI: [{overall['sigma_hdi_low']:.4f}, {overall['sigma_hdi_high']:.4f}]
247
+
248
+ or_speed (勝算比):
249
+ - 平均值: {overall['or_mean']:.4f}
250
+ - 標準差: {overall['or_sd']:.4f}
251
+ - 95% HDI: [{overall['or_hdi_low']:.4f}, {overall['or_hdi_high']:.4f}]
252
+
253
+ ----------------------------------------------
254
+ 2. 模型收斂診斷
255
+ ----------------------------------------------
256
+ R-hat (d): {diag['rhat_d']:.4f if diag['rhat_d'] else 'N/A'}
257
+ R-hat (sigma): {diag['rhat_sigma']:.4f if diag['rhat_sigma'] else 'N/A'}
258
+ ESS (d): {int(diag['ess_d']) if diag['ess_d'] else 'N/A'}
259
+ ESS (sigma): {int(diag['ess_sigma']) if diag['ess_sigma'] else 'N/A'}
260
+ 收斂狀態: {'✓ 已收斂' if diag['converged'] else '✗ 未收斂'}
261
+
262
+ ----------------------------------------------
263
+ 3. 結果解釋
264
+ ----------------------------------------------
265
+ 整體效應: {interp['overall_effect']}
266
+ 顯著性: {interp['overall_significance']}
267
+ 效果大小: {interp['effect_size']}
268
+ 異質性: {interp['heterogeneity']}
269
+
270
+ ----------------------------------------------
271
+ 4. 各屬性詳細結果
272
+ ----------------------------------------------
273
+ """
274
+
275
+ # 添加各屬性的詳細資訊
276
+ trial_labels = results['trial_labels']
277
+ by_trial = results['by_trial']
278
+
279
+ for i, label in enumerate(trial_labels):
280
+ sig_marker = "★" if by_trial['delta_significant'][i] else " "
281
+ report += f"""
282
+ {sig_marker} {label}:
283
+ Delta (平均): {by_trial['delta_mean'][i]:.4f}
284
+ 95% HDI: [{by_trial['delta_hdi_low'][i]:.4f}, {by_trial['delta_hdi_high'][i]:.4f}]
285
+ 控制組勝率: {by_trial['pc_mean'][i]:.2%}
286
+ 實驗組勝率: {by_trial['pt_mean'][i]:.2%}
287
+ 勝率差異: {(by_trial['pt_mean'][i] - by_trial['pc_mean'][i]):.2%}
288
+ """
289
+
290
+ report += """
291
+ ==============================================
292
+ """
293
+
294
+ return report
295
+
296
+ def plot_odds_ratio_comparison(results):
297
+ """
298
+ 繪製各屬性的勝算比比較圖(Plotly 版本)
299
+
300
+ Args:
301
+ results: 分析結果字典
302
+
303
+ Returns:
304
+ plotly figure
305
+ """
306
+ trial_labels = results['trial_labels']
307
+ delta_mean = results['by_trial']['delta_mean']
308
+
309
+ # 轉換為勝算比
310
+ or_values = [np.exp(d) for d in delta_mean]
311
+
312
+ # 排序
313
+ sorted_indices = np.argsort(or_values)[::-1]
314
+ sorted_labels = [trial_labels[i] for i in sorted_indices]
315
+ sorted_or = [or_values[i] for i in sorted_indices]
316
+ sorted_sig = [results['by_trial']['delta_significant'][i] for i in sorted_indices]
317
+
318
+ # 顏色標記
319
+ colors = ['#2ecc71' if sig else '#95a5a6' for sig in sorted_sig]
320
+
321
+ fig = go.Figure()
322
+
323
+ fig.add_trace(go.Bar(
324
+ x=sorted_or,
325
+ y=sorted_labels,
326
+ orientation='h',
327
+ marker=dict(
328
+ color=colors,
329
+ line=dict(color='white', width=1)
330
+ ),
331
+ text=[f'{or_val:.2f}' for or_val in sorted_or],
332
+ textposition='outside',
333
+ hovertemplate='%{y}<br>OR: %{x:.3f}<extra></extra>'
334
+ ))
335
+
336
+ # 參考線 (OR = 1)
337
+ fig.add_vline(x=1, line_dash="dash", line_color="red", line_width=2)
338
+
339
+ fig.update_layout(
340
+ title='各屬性速度效應(勝算比)',
341
+ xaxis_title='Odds Ratio',
342
+ yaxis_title='',
343
+ width=800,
344
+ height=max(400, len(trial_labels) * 25),
345
+ template='plotly_white',
346
+ showlegend=False
347
+ )
348
+
349
+ return fig
pokemon_speed_meta_results.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Trial_Type,rt,nt,rc,nc
2
+ Bug,2229,3142,800,3660
3
+ Dark,1559,2083,369,931
4
+ Drago,1264,1715,298,889
5
+ Elect,1935,2499,373,1174
6
+ Fairy,310,432,309,1320
7
+ Fight,800,1134,402,1458
8
+ Fire,2547,3530,487,1535
9
+ Flyin,102,107,39,110
10
+ Ghost,639,937,331,1259
11
+ Grass,1591,2196,1418,4598
12
+ Groun,1100,1529,529,1574
13
+ Ice,826,1288,354,1296
14
+ Norma,4258,5748,1107,3989
15
+ Poiso,997,1571,431,1411
16
+ Psych,2002,2747,334,1926
17
+ Rock,864,1255,998,3392
18
+ Steel,609,804,428,1584
19
+ Water,3601,5492,1814,5793
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit==1.31.0
2
+ pandas==2.1.4
3
+ numpy==1.26.3
4
+ plotly==5.18.0
5
+ pymc==5.10.4
6
+ arviz==0.17.0
7
+ matplotlib==3.8.2
8
+ google-generativeai>=0.3.0
9
+ pillow==10.2.0
10
+ graphviz==0.20.1
11
+ scipy==1.11.4
12
+ pytensor==2.18.6