Wen1201 commited on
Commit
64eb4e1
·
verified ·
1 Parent(s): 0ba59a3

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -621
app.py DELETED
@@ -1,621 +0,0 @@
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 base64
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
- .info-box {
61
- background-color: #d1ecf1;
62
- border: 1px solid #bee5eb;
63
- border-radius: 5px;
64
- padding: 10px;
65
- margin: 10px 0;
66
- }
67
- </style>
68
- """, unsafe_allow_html=True)
69
-
70
- # 導入自定義模組
71
- from bayesian_core import BayesianHierarchicalAnalyzer
72
- from bayesian_llm_assistant import BayesianLLMAssistant
73
-
74
- # 清理函數
75
- def cleanup_old_sessions():
76
- """清理超過 1 小時的 session"""
77
- current_time = datetime.now()
78
- for session_id in list(BayesianHierarchicalAnalyzer._session_results.keys()):
79
- result = BayesianHierarchicalAnalyzer._session_results.get(session_id)
80
- if result:
81
- result_time = datetime.fromisoformat(result['timestamp'])
82
- if current_time - result_time > timedelta(hours=1):
83
- BayesianHierarchicalAnalyzer.clear_session_results(session_id)
84
-
85
- # 註冊清理函數
86
- atexit.register(cleanup_old_sessions)
87
-
88
- # 初始化 session state
89
- if 'session_id' not in st.session_state:
90
- st.session_state.session_id = str(uuid.uuid4())
91
- if 'analysis_results' not in st.session_state:
92
- st.session_state.analysis_results = None
93
- if 'chat_history' not in st.session_state:
94
- st.session_state.chat_history = []
95
- if 'analyzer' not in st.session_state:
96
- st.session_state.analyzer = None
97
-
98
- # 標題
99
- st.title("⚡ Bayesian Hierarchical Model Analysis")
100
- st.markdown("### 寶可夢速度對勝率影響的階層貝氏分析")
101
- st.markdown("---")
102
-
103
- # Sidebar
104
- with st.sidebar:
105
- st.header("⚙️ 配置設定")
106
-
107
- # Google Gemini API Key
108
- api_key = st.text_input(
109
- "Google Gemini API Key",
110
- type="password",
111
- help="輸入您的 Google Gemini API Key 以使用 AI 助手"
112
- )
113
-
114
- if api_key:
115
- st.session_state.api_key = api_key
116
- st.success("✅ API Key 已載入")
117
-
118
- st.markdown("---")
119
-
120
- # 清理按鈕
121
- if st.button("🧹 清理過期資料"):
122
- cleanup_old_sessions()
123
- st.success("✅ 清理完成")
124
- st.rerun()
125
-
126
- st.markdown("---")
127
-
128
- # 資料來源選擇
129
- st.subheader("📊 資料來源")
130
- data_source = st.radio(
131
- "選擇資料來源:",
132
- ["使用預設資料集", "上傳您的資料"]
133
- )
134
-
135
- uploaded_file = None
136
- if data_source == "上傳您的資料":
137
- uploaded_file = st.file_uploader(
138
- "上傳 CSV 檔案",
139
- type=['csv'],
140
- help="上傳寶可夢速度分析資料"
141
- )
142
-
143
- with st.expander("📖 資料格式說明"):
144
- st.markdown("""
145
- **必要欄位格式:**
146
- - `Trial_Type`: 寶可夢屬性(如 Water, Fire, Grass)
147
- - `rc`: 控制組(速度慢)的勝場數
148
- - `nc`: 控制組的總場數
149
- - `rt`: 實驗組(速度快)的勝場數
150
- - `nt`: 實驗組的總場數
151
-
152
- **範例:**
153
- ```
154
- Trial_Type, rc, nc, rt, nt
155
- Water, 45, 100, 60, 100
156
- Fire, 38, 100, 55, 100
157
- Grass, 42, 100, 58, 100
158
- ```
159
- """)
160
-
161
- st.markdown("---")
162
-
163
- # MCMC 抽樣參數設定
164
- st.subheader("🎲 MCMC 抽樣參數")
165
-
166
- with st.expander("⚙️ 進階設定"):
167
- n_samples = st.slider(
168
- "抽樣數 (Samples)",
169
- min_value=500,
170
- max_value=5000,
171
- value=2000,
172
- step=500,
173
- help="更多樣本 = 更準確,但更慢"
174
- )
175
-
176
- n_tune = st.slider(
177
- "調整期樣本 (Tuning)",
178
- min_value=500,
179
- max_value=2000,
180
- value=1000,
181
- step=100,
182
- help="調整期用於優化抽樣器"
183
- )
184
-
185
- n_chains = st.selectbox(
186
- "鏈數 (Chains)",
187
- options=[1, 2, 4],
188
- index=0,
189
- help="多條鏈可以檢測收斂問題"
190
- )
191
-
192
- target_accept = st.slider(
193
- "目標接受率",
194
- min_value=0.80,
195
- max_value=0.99,
196
- value=0.95,
197
- step=0.01,
198
- help="更高的接受率 = 更準確,但更慢"
199
- )
200
-
201
- st.markdown("---")
202
-
203
- # 關於系統
204
- with st.expander("ℹ️ 關於此系統"):
205
- st.markdown("""
206
- **貝氏階層模型分析系統**
207
-
208
- 本系統使用貝氏階層模型來分析速度對不同屬性寶可夢勝率的影響。
209
-
210
- **主要功能:**
211
- - 🔬 貝氏推論與 MCMC 抽樣
212
- - 📊 階層模型(跨屬性資訊借用)
213
- - 📈 完整視覺化(4 個圖表)
214
- - 💬 AI 助手解釋
215
- - 🎮 對戰策略建議
216
-
217
- **模型優勢:**
218
- - 量化不確定性
219
- - 處理小樣本
220
- - 估計屬性間異質性
221
- - 穩健的統計推論
222
- """)
223
-
224
- # 主要內容區 - 雙 Tab
225
- tab1, tab2 = st.tabs(["📊 貝氏分析", "💬 AI 助手"])
226
-
227
- # Tab 1: 貝氏分析
228
- with tab1:
229
- st.header("📊 貝氏階層模型分析")
230
-
231
- # 載入資料
232
- if data_source == "使用預設資料集":
233
- # 檢查預設資料是否存在
234
- default_data_path = "pokemon_speed_meta_results.csv"
235
- if os.path.exists(default_data_path):
236
- df = pd.read_csv(default_data_path)
237
- st.success(f"✅ 已載入預設資料集({len(df)} 個屬性)")
238
- else:
239
- st.warning("⚠️ 找不到預設資料集,請上傳您的資料")
240
- df = None
241
- else:
242
- if uploaded_file is not None:
243
- df = pd.read_csv(uploaded_file)
244
- st.success(f"✅ 已載入資料({len(df)} 個屬性)")
245
- else:
246
- df = None
247
- st.info("📁 請在左側上傳 CSV 檔案")
248
-
249
- if df is not None:
250
- # 顯示資料預覽
251
- with st.expander("👀 資料預覽"):
252
- st.dataframe(df, use_container_width=True)
253
-
254
- st.markdown("---")
255
-
256
- # 執行分析按鈕
257
- col1, col2, col3 = st.columns([2, 1, 2])
258
-
259
- with col2:
260
- analyze_button = st.button("🔬 開始貝氏分析", type="primary", use_container_width=True)
261
-
262
- # 執行分析
263
- if analyze_button:
264
- # 初始化分析器
265
- if st.session_state.analyzer is None:
266
- st.session_state.analyzer = BayesianHierarchicalAnalyzer(st.session_state.session_id)
267
-
268
- try:
269
- st.session_state.analyzer.load_data(df)
270
-
271
- # 進度條
272
- progress_bar = st.progress(0)
273
- status_text = st.empty()
274
-
275
- def update_progress(message, percent):
276
- status_text.text(message)
277
- progress_bar.progress(percent / 100)
278
-
279
- # 執行分析
280
- with st.spinner("正在執行貝氏分析..."):
281
- results = st.session_state.analyzer.run_analysis(
282
- n_samples=n_samples,
283
- n_tune=n_tune,
284
- n_chains=n_chains,
285
- target_accept=target_accept,
286
- progress_callback=update_progress
287
- )
288
- st.session_state.analysis_results = results
289
-
290
- progress_bar.empty()
291
- status_text.empty()
292
- st.success("✅ 分析完成!")
293
- st.balloons()
294
-
295
- except Exception as e:
296
- st.error(f"❌ 分析失敗: {str(e)}")
297
-
298
- # 顯示結果
299
- if st.session_state.analysis_results is not None:
300
- results = st.session_state.analysis_results
301
-
302
- st.markdown("---")
303
- st.markdown("## 📈 分析結果")
304
-
305
- # 建立 4 個子 Tab
306
- result_tabs = st.tabs(["📊 概覽", "📉 Trace Plot", "🎯 Posterior", "🌲 Forest Plot"])
307
-
308
- # Tab: 概覽
309
- with result_tabs[0]:
310
- st.markdown("### 🎯 關鍵指標")
311
-
312
- # 顯示關鍵指標
313
- col1, col2, col3 = st.columns(3)
314
-
315
- with col1:
316
- st.metric(
317
- label="整體效應 (d)",
318
- value=f"{results['d_mean']:.4f}",
319
- delta=f"HDI: [{results['d_hdi_lower']:.3f}, {results['d_hdi_upper']:.3f}]"
320
- )
321
-
322
- with col2:
323
- st.metric(
324
- label="屬性間變異 (sigma)",
325
- value=f"{results['sigma_mean']:.4f}",
326
- delta=f"SD: {results['sigma_sd']:.4f}"
327
- )
328
-
329
- with col3:
330
- st.metric(
331
- label="速度勝算比 (OR)",
332
- value=f"{results['or_speed_mean']:.3f}",
333
- delta=f"HDI: [{results['or_speed_hdi_lower']:.3f}, {results['or_speed_hdi_upper']:.3f}]"
334
- )
335
-
336
- st.markdown("---")
337
-
338
- # 顯著性判斷
339
- if results['is_significant']:
340
- st.markdown("""
341
- <div class="success-box">
342
- <h4>✅ 結果顯著</h4>
343
- <p>速度對勝率有<strong>顯著影響</strong>(95% HDI 不包含 0)</p>
344
- </div>
345
- """, unsafe_allow_html=True)
346
- else:
347
- st.markdown("""
348
- <div class="warning-box">
349
- <h4>⚠️ 結果不顯著</h4>
350
- <p>速度對勝率<strong>無顯著影響</strong>(95% HDI 包含 0)</p>
351
- </div>
352
- """, unsafe_allow_html=True)
353
-
354
- st.markdown("---")
355
-
356
- # 文字摘要
357
- st.markdown("### 📋 統計摘要")
358
- st.text_area(
359
- "Summary Statistics",
360
- results['summary_text'],
361
- height=300
362
- )
363
-
364
- # 下載摘要
365
- st.download_button(
366
- label="📥 下載統計摘要 (.txt)",
367
- data=results['summary_text'],
368
- file_name=f"bayesian_summary_{results['timestamp'][:10]}.txt",
369
- mime="text/plain"
370
- )
371
-
372
- st.markdown("---")
373
-
374
- # 各屬性詳細結果
375
- st.markdown("### 🎮 各屬性詳細結果")
376
-
377
- delta_df = pd.DataFrame(results['delta_results'])
378
- delta_df['Significant'] = delta_df['is_significant'].apply(lambda x: '★' if x else '')
379
- delta_df = delta_df[['trial_type', 'delta_mean', 'delta_sd', 'delta_hdi_lower', 'delta_hdi_upper', 'Significant']]
380
- delta_df.columns = ['屬性', 'Delta 平均', 'Delta 標準差', 'HDI 下界', 'HDI 上界', '顯著']
381
-
382
- st.dataframe(
383
- delta_df.style.format({
384
- 'Delta 平均': '{:.4f}',
385
- 'Delta 標準差': '{:.4f}',
386
- 'HDI 下界': '{:.4f}',
387
- 'HDI 上界': '{:.4f}'
388
- }),
389
- use_container_width=True
390
- )
391
-
392
- # Tab: Trace Plot
393
- with result_tabs[1]:
394
- st.markdown("### 📉 Trace Plot - 收斂診斷")
395
-
396
- st.markdown("""
397
- <div class="info-box">
398
- <h4>📖 如何解讀 Trace Plot:</h4>
399
- <ul>
400
- <li><strong>左欄</strong>:MCMC 抽樣軌跡(應該像「毛毛蟲」,平穩無趨勢)</li>
401
- <li><strong>右欄</strong>:後驗分佈密度圖</li>
402
- <li><strong>良好收斂</strong>:軌跡圖混合良好,無明顯趨勢或週期</li>
403
- <li><strong>問題跡象</strong>:軌跡圖有趨勢、卡住、或未混合</li>
404
- </ul>
405
- </div>
406
- """, unsafe_allow_html=True)
407
-
408
- if results['trace_plot']:
409
- st.image(f"data:image/png;base64,{results['trace_plot']}", use_column_width=True)
410
- else:
411
- st.warning("⚠️ Trace Plot 未生成")
412
-
413
- # Tab: Posterior Plot
414
- with result_tabs[2]:
415
- st.markdown("### 🎯 Posterior Distributions - 後驗分佈")
416
-
417
- st.markdown("""
418
- <div class="info-box">
419
- <h4>📖 如何解讀 Posterior Plot:</h4>
420
- <ul>
421
- <li><strong>d</strong>:整體平均效應(log odds ratio)</li>
422
- <li><strong>sigma</strong>:屬性間變異(越大表示屬性間差異越大)</li>
423
- <li><strong>or_speed</strong>:速度勝算比(exp(d))</li>
424
- <li><strong>95% HDI</strong>:最高密度區間(類似信賴區間)</li>
425
- <li><strong>顯著性</strong>:HDI 不包含 0(d)或 1(or_speed)即為顯著</li>
426
- </ul>
427
- </div>
428
- """, unsafe_allow_html=True)
429
-
430
- if results['posterior_plot']:
431
- st.image(f"data:image/png;base64,{results['posterior_plot']}", use_column_width=True)
432
- else:
433
- st.warning("⚠️ Posterior Plot 未生成")
434
-
435
- # Tab: Forest Plot
436
- with result_tabs[3]:
437
- st.markdown("### 🌲 Forest Plot - 各屬性效應")
438
-
439
- st.markdown("""
440
- <div class="info-box">
441
- <h4>📖 如何解讀 Forest Plot:</h4>
442
- <ul>
443
- <li><strong>點</strong>:各屬性的平均效應(delta)</li>
444
- <li><strong>橫線</strong>:95% 信賴區間</li>
445
- <li><strong>紅虛線</strong>:無效應參考線(delta = 0)</li>
446
- <li><strong>星號 ★</strong>:該屬性效應顯著</li>
447
- <li><strong>右側</strong>:速度快有利於該屬性</li>
448
- <li><strong>左側</strong>:速度慢有利於該屬性(罕見)</li>
449
- </ul>
450
- </div>
451
- """, unsafe_allow_html=True)
452
-
453
- if results['forest_plot']:
454
- st.image(f"data:image/png;base64,{results['forest_plot']}", use_column_width=True)
455
- else:
456
- st.warning("⚠️ Forest Plot 未生成")
457
-
458
- st.markdown("---")
459
-
460
- # 顯著屬性總結
461
- significant_types = [dr for dr in results['delta_results'] if dr['is_significant']]
462
-
463
- if significant_types:
464
- st.markdown(f"### ⭐ 顯著屬性總結 ({len(significant_types)}/{results['n_trials']})")
465
-
466
- for dr in significant_types:
467
- if dr['delta_mean'] > 0:
468
- st.success(f"**{dr['trial_type']}**: 速度快有顯著優勢 (Delta = {dr['delta_mean']:.3f})")
469
- else:
470
- st.warning(f"**{dr['trial_type']}**: 速度慢有顯著優勢 (Delta = {dr['delta_mean']:.3f})")
471
- else:
472
- st.info("沒有屬性顯示顯著的速度效應")
473
-
474
- # Tab 2: AI 助手
475
- with tab2:
476
- st.header("💬 AI 分析助手")
477
-
478
- if not st.session_state.get('api_key'):
479
- st.warning("⚠️ 請在左側輸入您的 Google Gemini API Key 以使用 AI 助手")
480
- elif st.session_state.analysis_results is None:
481
- st.info("ℹ️ 請先在「貝氏分析」頁面執行分析")
482
- else:
483
- # 初始化 LLM 助手
484
- if 'llm_assistant' not in st.session_state:
485
- st.session_state.llm_assistant = BayesianLLMAssistant(
486
- api_key=st.session_state.api_key,
487
- session_id=st.session_state.session_id
488
- )
489
-
490
- # 聊天容器
491
- chat_container = st.container()
492
-
493
- with chat_container:
494
- for message in st.session_state.chat_history:
495
- with st.chat_message(message["role"]):
496
- st.markdown(message["content"])
497
-
498
- # 使用者輸入
499
- if prompt := st.chat_input("詢問關於分析結果的任何問題..."):
500
- # 添加使用者訊息
501
- st.session_state.chat_history.append({
502
- "role": "user",
503
- "content": prompt
504
- })
505
-
506
- with st.chat_message("user"):
507
- st.markdown(prompt)
508
-
509
- # AI 回應
510
- with st.chat_message("assistant"):
511
- with st.spinner("思考中..."):
512
- try:
513
- response = st.session_state.llm_assistant.get_response(
514
- user_message=prompt,
515
- analysis_results=st.session_state.analysis_results
516
- )
517
- st.markdown(response)
518
- except Exception as e:
519
- error_msg = f"❌ 錯誤: {str(e)}\n\n請檢查 API key 或重新表達問題。"
520
- st.error(error_msg)
521
- response = error_msg
522
-
523
- # 添加助手回應
524
- st.session_state.chat_history.append({
525
- "role": "assistant",
526
- "content": response
527
- })
528
-
529
- st.markdown("---")
530
-
531
- # 快速問題按鈕
532
- st.subheader("💡 快速問題")
533
-
534
- quick_questions = [
535
- "📊 給我分析總結",
536
- "🎯 解釋 d 參數",
537
- "🔍 解釋 sigma",
538
- "📖 什麼是貝氏統計?",
539
- "🏗️ 什麼是階層模型?",
540
- "📉 如何看 Trace Plot?",
541
- "🎮 比較各屬性",
542
- "⚔️ 對戰策略建議"
543
- ]
544
-
545
- cols = st.columns(4)
546
- for idx, question in enumerate(quick_questions):
547
- col_idx = idx % 4
548
- if cols[col_idx].button(question, key=f"quick_{idx}", use_container_width=True):
549
- # 根據問題選擇對應的方法
550
- if "總結" in question:
551
- response = st.session_state.llm_assistant.generate_summary(
552
- st.session_state.analysis_results
553
- )
554
- elif "d 參數" in question:
555
- response = st.session_state.llm_assistant.explain_metric(
556
- 'd',
557
- st.session_state.analysis_results
558
- )
559
- elif "sigma" in question:
560
- response = st.session_state.llm_assistant.explain_metric(
561
- 'sigma',
562
- st.session_state.analysis_results
563
- )
564
- elif "貝氏統計" in question:
565
- response = st.session_state.llm_assistant.explain_bayesian_concepts()
566
- elif "階層模型" in question:
567
- response = st.session_state.llm_assistant.explain_hierarchical_model()
568
- elif "Trace Plot" in question:
569
- response = st.session_state.llm_assistant.explain_convergence()
570
- elif "比較" in question:
571
- response = st.session_state.llm_assistant.compare_types(
572
- st.session_state.analysis_results
573
- )
574
- elif "策略" in question:
575
- response = st.session_state.llm_assistant.battle_strategy_advice(
576
- st.session_state.analysis_results
577
- )
578
- else:
579
- response = st.session_state.llm_assistant.get_response(
580
- question,
581
- st.session_state.analysis_results
582
- )
583
-
584
- st.session_state.chat_history.append({
585
- "role": "user",
586
- "content": question
587
- })
588
-
589
- st.session_state.chat_history.append({
590
- "role": "assistant",
591
- "content": response
592
- })
593
-
594
- st.rerun()
595
-
596
- # 重置對話按鈕
597
- st.markdown("---")
598
- if st.button("🔄 重置對話"):
599
- st.session_state.llm_assistant.reset_conversation()
600
- st.session_state.chat_history = []
601
- st.success("✅ 對話已重置")
602
- st.rerun()
603
-
604
- # DAG 圖(如果有的話,放在側邊欄底部)
605
- if st.session_state.analysis_results and st.session_state.analysis_results.get('dag_plot'):
606
- with st.sidebar:
607
- st.markdown("---")
608
- with st.expander("🔀 DAG 模型結構圖"):
609
- st.image(f"data:image/png;base64,{st.session_state.analysis_results['dag_plot']}")
610
-
611
- # Footer
612
- st.markdown("---")
613
- st.markdown(
614
- f"""
615
- <div style='text-align: center'>
616
- <p>⚡ Bayesian Hierarchical Model for Pokémon Speed Analysis | Built with PyMC & Streamlit</p>
617
- <p>Session ID: {st.session_state.session_id[:8]} | Powered by Google Gemini</p>
618
- </div>
619
- """,
620
- unsafe_allow_html=True
621
- )