Wen1201 commited on
Commit
9d4e286
·
verified ·
1 Parent(s): 0010b2e

Upload app_bayesian.py

Browse files
Files changed (1) hide show
  1. app_bayesian.py +654 -0
app_bayesian.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
+ )