File size: 20,911 Bytes
c43ac66
 
c7930dd
 
a196335
c43ac66
 
 
 
 
 
 
 
 
 
c7930dd
c43ac66
 
c7930dd
 
 
 
c43ac66
c7930dd
c43ac66
 
 
c7930dd
 
25bf28f
 
 
c7930dd
 
25bf28f
c7930dd
25bf28f
c7930dd
25bf28f
c7930dd
 
 
 
 
25bf28f
 
 
c7930dd
25bf28f
 
 
 
c7930dd
 
 
 
 
25bf28f
 
 
c7930dd
25bf28f
 
 
 
c7930dd
 
 
c43ac66
 
c7930dd
 
 
 
 
 
 
 
a196335
c7930dd
 
 
a196335
c43ac66
 
 
 
 
 
 
c7930dd
 
 
 
a196335
c7930dd
 
 
a196335
c7930dd
 
 
a196335
c7930dd
 
 
a196335
c7930dd
 
 
a196335
c7930dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c43ac66
c7930dd
 
 
 
 
 
 
 
 
 
 
c43ac66
c7930dd
 
 
c43ac66
 
 
 
c7930dd
 
 
c43ac66
c7930dd
 
 
c43ac66
c7930dd
 
c43ac66
c7930dd
c43ac66
c7930dd
c43ac66
8e53b26
 
 
 
 
 
 
 
 
 
 
c43ac66
c7930dd
 
 
c43ac66
 
 
c7930dd
 
 
 
 
 
c43ac66
a196335
c7930dd
 
 
 
 
 
 
 
 
 
 
a196335
 
 
c7930dd
a196335
c7930dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a196335
c7930dd
a196335
c7930dd
 
 
c43ac66
 
 
c7930dd
 
 
 
 
 
 
a196335
c7930dd
 
9a9e178
c7930dd
9a9e178
71b92f3
9a9e178
 
71b92f3
9a9e178
 
71b92f3
a196335
 
 
9a9e178
 
71b92f3
9a9e178
71b92f3
9a9e178
 
 
c7930dd
 
 
9a9e178
 
c7930dd
 
9a9e178
 
 
 
 
 
c7930dd
9a9e178
 
 
71b92f3
 
 
 
 
 
 
 
9a9e178
 
71b92f3
9a9e178
 
71b92f3
c43ac66
 
 
 
 
 
 
 
c7930dd
9a9e178
 
c43ac66
 
 
c7930dd
 
c43ac66
 
 
d9f0e73
c43ac66
d9f0e73
c43ac66
d9f0e73
 
 
c43ac66
d9f0e73
c43ac66
d9f0e73
 
c43ac66
d9f0e73
c43ac66
d9f0e73
 
c43ac66
 
 
d9f0e73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c43ac66
d9f0e73
c7930dd
d9f0e73
c7930dd
c43ac66
c7930dd
c43ac66
 
 
 
c7930dd
c43ac66
c7930dd
 
 
 
 
 
 
 
 
 
d9f0e73
c7930dd
 
d9f0e73
 
c7930dd
 
d9f0e73
 
c7930dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d9f0e73
c7930dd
d9f0e73
 
c7930dd
 
c43ac66
c7930dd
 
 
 
c43ac66
c7930dd
 
 
c43ac66
c7930dd
 
 
 
 
 
 
 
 
c43ac66
c7930dd
c43ac66
c7930dd
c43ac66
 
c7930dd
 
9a9e178
 
 
 
 
 
 
 
 
 
 
c7930dd
9a9e178
 
 
 
71b92f3
 
c7930dd
9a9e178
 
 
c7930dd
9a9e178
 
 
 
 
c43ac66
 
c7930dd
c43ac66
c7930dd
c43ac66
c7930dd
 
c43ac66
c7930dd
 
c43ac66
c7930dd
 
c43ac66
c7930dd
 
c43ac66
 
 
 
 
 
c7930dd
 
71b92f3
c43ac66
 
 
 
9a9e178
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
"""
Simon Two-stage Design 互動式教學工具
賽門式二階段試驗設計 - 基於 Tan et al. (2002) BJC 論文
完整版:含 Trial C、Trial P、4種事前分佈
修改版:使用 Google Gemini API
"""

import gradio as gr
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

# ============== 計算函數 ==============

def beta_mean(a, b):
    """Beta 分佈平均值"""
    return a / (a + b)

def beta_var(a, b):
    """Beta 分佈變異數"""
    return (a * b) / ((a + b)**2 * (a + b + 1))

def beta_ci(a, b, level=0.95):
    """Beta 分佈可信區間"""
    lower = (1 - level) / 2
    return stats.beta.ppf(lower, a, b), stats.beta.ppf(1-lower, a, b)

def calc_clinical_prior(r0, r1):
    """
    Clinical Prior - 使用論文的精確數值
    Trial C: Beta(0.7, 2.1)
    Trial P: Beta(0.6, 3.0)
    """
    if r0 == 0.10 and r1 == 0.30:
        return 0.7, 2.1  # Trial C (論文 Table 1)
    elif r0 == 0.05 and r1 == 0.20:
        return 0.6, 3.0  # Trial P (論文 Table 2)
    else:
        # 其他情況用計算
        m = (r0 + r1) / 2
        return m * 2, (1 - m) * 2

def calc_sceptical_prior(r0, r1):
    """
    Sceptical Prior - 使用論文的精確數值
    Trial C: Beta(1, 9)
    Trial P: Beta(0.4, 7.6)
    """
    if r0 == 0.10 and r1 == 0.30:
        return 1, 9  # Trial C (論文 Table 1)
    elif r0 == 0.05 and r1 == 0.20:
        return 0.4, 7.6  # Trial P (論文 Table 2)
    else:
        return r0 * 10, (1 - r0) * 10

def calc_enthusiastic_prior(r0, r1):
    """
    Enthusiastic Prior - 使用論文的精確數值
    Trial C: Beta(3, 7)
    Trial P: Beta(2.4, 9.6)
    """
    if r0 == 0.10 and r1 == 0.30:
        return 3, 7  # Trial C (論文 Table 1)
    elif r0 == 0.05 and r1 == 0.20:
        return 2.4, 9.6  # Trial P (論文 Table 2)
    else:
        return r1 * 10, (1 - r1) * 10

# ============== 主分析函數 ==============

def analyze(trial_type, 
            s1_success, s1_total, s2_success, s2_total,
            prior_type, custom_a, custom_b):
    
    # 根據試驗類型設定參數
    if trial_type == "Trial C (初治組)":
        r0, r1 = 0.10, 0.30
        trial_name = "Trial C:Chemotherapy-naïve(初治組)"
        trial_name_en = "Trial C: Chemotherapy-naive"
    else:
        r0, r1 = 0.05, 0.20
        trial_name = "Trial P:Previously treated(曾治療組)"
        trial_name_en = "Trial P: Previously treated"
    
    # 基本計算
    s1_success, s1_total = int(s1_success), int(s1_total)
    s2_success, s2_total = int(s2_success), int(s2_total)
    total_success = s1_success + s2_success
    total_n = s1_total + s2_total
    
    # 根據事前分佈類型計算參數
    if prior_type == "Clinical Prior(臨床事前分佈)":
        prior_a, prior_b = calc_clinical_prior(r0, r1)
        prior_desc = f"根據專家意見,P(θ<R₀) = P(R₀<θ<R₁) = P(θ>R₁) = 1/3"
        prior_name_en = "Clinical Prior"
    elif prior_type == "Reference Prior(參考事前分佈)":
        prior_a, prior_b = 1, 1  # Uniform
        prior_desc = "無資訊事前分佈(Uniform),代表完全不確定"
        prior_name_en = "Reference Prior"
    elif prior_type == "Sceptical Prior(懷疑性事前分佈)":
        prior_a, prior_b = calc_sceptical_prior(r0, r1)
        prior_desc = f"平均值 = R₀ = {r0*100:.0f}%,P(θ>R₁) = 5%(偏向不相信藥效)"
        prior_name_en = "Sceptical Prior"
    elif prior_type == "Enthusiastic Prior(樂觀性事前分佈)":
        prior_a, prior_b = calc_enthusiastic_prior(r0, r1)
        prior_desc = f"平均值 = R₁ = {r1*100:.0f}%,P(θ<R₀) = 5%(偏向相信藥效)"
        prior_name_en = "Enthusiastic Prior"
    else:  # 自訂
        prior_a, prior_b = custom_a, custom_b
        prior_desc = "使用者自訂參數"
        prior_name_en = "Custom Prior"
    
    # === 計算事後分佈 ===
    # Stage 1 後
    post1_a = prior_a + s1_success
    post1_b = prior_b + (s1_total - s1_success)
    
    # Stage 2 後(最終)
    post2_a = prior_a + total_success
    post2_b = prior_b + (total_n - total_success)
    
    # === 計算機率 ===
    # 事前
    prior_prob_lt_r0 = stats.beta.cdf(r0, prior_a, prior_b)
    prior_prob_r0_r1 = stats.beta.cdf(r1, prior_a, prior_b) - prior_prob_lt_r0
    prior_prob_gt_r1 = 1 - stats.beta.cdf(r1, prior_a, prior_b)
    
    # Stage 1 後
    post1_prob_lt_r0 = stats.beta.cdf(r0, post1_a, post1_b)
    post1_prob_r0_r1 = stats.beta.cdf(r1, post1_a, post1_b) - post1_prob_lt_r0
    post1_prob_gt_r1 = 1 - stats.beta.cdf(r1, post1_a, post1_b)
    
    # Stage 2 後
    post2_prob_lt_r0 = stats.beta.cdf(r0, post2_a, post2_b)
    post2_prob_r0_r1 = stats.beta.cdf(r1, post2_a, post2_b) - post2_prob_lt_r0
    post2_prob_gt_r1 = 1 - stats.beta.cdf(r1, post2_a, post2_b)
    
    # === 頻率學派判定 ===
    if trial_type == "Trial C (初治組)":
        stage1_threshold = 2  # 至少 2 人有反應
        final_threshold = 6   # 至少 6 人有反應
    else:
        stage1_threshold = 1  # 至少 1 人有反應
        final_threshold = 4   # 至少 4 人有反應
    
    stage1_pass = s1_success >= stage1_threshold
    final_pass = total_success >= final_threshold
    
    # === 輸出結果 ===
    result = f"""
## 📋 試驗資訊:{trial_name}

### 設計參數
- **R₀(無興趣反應率)**: {r0*100:.0f}%
- **R₁(理想反應率)**: {r1*100:.0f}%

---

## 📊 頻率學派分析

### 試驗結果
| 階段 | 成功 (R) | 失敗 (N-R) | 總數 (N) | 反應率 |
|------|----------|------------|----------|--------|
| Stage I | {s1_success} | {s1_total - s1_success} | {s1_total} | {s1_success/s1_total*100:.1f}% |
| Stage II | {s2_success} | {s2_total - s2_success} | {s2_total} | {s2_success/s2_total*100:.1f}% |
| **合計** | **{total_success}** | **{total_n - total_success}** | **{total_n}** | **{total_success/total_n*100:.1f}%** |

### Simon Minimax Design 判定
- Stage I:需 ≥ {stage1_threshold} 人有反應 → {'✅ 通過' if stage1_pass else '❌ 未通過'}(實際 {s1_success} 人)
- 最終:需 ≥ {final_threshold} 人有反應 → {'✅ 有效' if final_pass else '❌ 無效'}(實際 {total_success} 人)

---

## 🔮 貝氏分析

### 事前分佈:{prior_type}
{prior_desc}

**Beta({prior_a:.1f}, {prior_b:.1f})**,平均值 = {beta_mean(prior_a, prior_b)*100:.1f}%

### 機率表(對應論文 Table 1 & 2)

| 階段 | Beta 參數 | π(平均值) | θ ≤ R₀ | R₀ < θ ≤ R₁ | θ > R₁ |
|------|-----------|-------------|--------|-------------|--------|
| Prior | Beta({prior_a:.1f}, {prior_b:.1f}) | **{beta_mean(prior_a, prior_b):.3f}** | {prior_prob_lt_r0:.3f} | {prior_prob_r0_r1:.3f} | {prior_prob_gt_r1:.3f} |
| Stage 1 posterior | Beta({post1_a:.1f}, {post1_b:.1f}) | **{beta_mean(post1_a, post1_b):.3f}** | {post1_prob_lt_r0:.3f} | {post1_prob_r0_r1:.3f} | {post1_prob_gt_r1:.3f} |
| Stage 2 posterior | Beta({post2_a:.1f}, {post2_b:.1f}) | **{beta_mean(post2_a, post2_b):.3f}** | {post2_prob_lt_r0:.3f} | {post2_prob_r0_r1:.3f} | {post2_prob_gt_r1:.3f} |

### 貝氏更新過程(練習用)

| 事前分佈 | Stage I 結果 | Stage I 後分佈 | Stage II 結果 | Stage II 後分佈 |
|----------|-------------|---------------|--------------|----------------|
| Beta({prior_a:.1f},{prior_b:.1f}), π={beta_mean(prior_a, prior_b):.2f} | 成功:{s1_success}, 失敗:{s1_total - s1_success} | Beta({post1_a:.1f},{post1_b:.1f}), π={beta_mean(post1_a, post1_b):.2f} | 成功:{s2_success}, 失敗:{s2_total - s2_success} | Beta({post2_a:.1f},{post2_b:.1f}), π={beta_mean(post2_a, post2_b):.3f} |

### 結果解讀
- **P(θ > R₁) = {post2_prob_gt_r1:.1%}**:藥物反應率超過理想值的機率
- **P(θ > R₀) = {(1-post2_prob_lt_r0):.1%}**:藥物值得繼續研究的機率

"""
    
    if post2_prob_gt_r1 > 0.5:
        result += "📌 **結論**:強烈支持進入 Phase III 試驗\n"
    elif post2_prob_gt_r1 > 0.25:
        result += "📌 **結論**:中度支持,可考慮進一步研究\n"
    else:
        result += "📌 **結論**:證據不足,需謹慎評估\n"
    
    # === 繪圖(英文標籤)===
    fig, axes = plt.subplots(1, 2, figsize=(14, 5))
    x = np.linspace(0, 1, 500)
    
    # 左圖:三條分佈曲線
    ax1 = axes[0]
    ax1.plot(x, stats.beta.pdf(x, prior_a, prior_b), 'b--', lw=2, 
             label=f'Prior Beta({prior_a:.1f},{prior_b:.1f})')
    ax1.plot(x, stats.beta.pdf(x, post1_a, post1_b), 'g-.', lw=2,
             label=f'Stage 1 Post Beta({post1_a:.1f},{post1_b:.1f})')
    ax1.plot(x, stats.beta.pdf(x, post2_a, post2_b), 'r-', lw=2.5,
             label=f'Stage 2 Post Beta({post2_a:.1f},{post2_b:.1f})')
    ax1.axvline(r0, color='gray', ls=':', alpha=0.7, label=f'R0={r0}')
    ax1.axvline(r1, color='orange', ls=':', alpha=0.7, label=f'R1={r1}')
    ax1.set_xlabel('Response Rate (theta)', fontsize=12)
    ax1.set_ylabel('Density', fontsize=12)
    ax1.set_title(f'{prior_name_en} - Bayesian Update\n{trial_name_en}', fontsize=12)
    ax1.legend(fontsize=9)
    ax1.grid(alpha=0.3)
    ax1.set_xlim(0, 0.8)
    
    # 右圖:4 種事前分佈比較
    ax2 = axes[1]
    
    # 計算所有 4 種事前分佈
    clin_a, clin_b = calc_clinical_prior(r0, r1)
    ref_a, ref_b = 1, 1
    scep_a, scep_b = calc_sceptical_prior(r0, r1)
    enth_a, enth_b = calc_enthusiastic_prior(r0, r1)
    
    ax2.plot(x, stats.beta.pdf(x, clin_a, clin_b), 'b-', lw=1.5, alpha=0.7,
             label=f'Clinical ({clin_a:.1f},{clin_b:.1f})')
    ax2.plot(x, stats.beta.pdf(x, ref_a, ref_b), 'gray', ls='--', lw=1.5, alpha=0.7,
             label=f'Reference ({ref_a},{ref_b})')
    ax2.plot(x, stats.beta.pdf(x, scep_a, scep_b), 'r-', lw=1.5, alpha=0.7,
             label=f'Sceptical ({scep_a:.1f},{scep_b:.1f})')
    ax2.plot(x, stats.beta.pdf(x, enth_a, enth_b), 'g-', lw=1.5, alpha=0.7,
             label=f'Enthusiastic ({enth_a:.1f},{enth_b:.1f})')
    ax2.axvline(r0, color='gray', ls=':', alpha=0.5)
    ax2.axvline(r1, color='orange', ls=':', alpha=0.5)
    ax2.set_xlabel('Response Rate (theta)', fontsize=12)
    ax2.set_ylabel('Density', fontsize=12)
    ax2.set_title(f'Comparison of 4 Prior Types\n{trial_name_en} (Figure 1 & 4 style)', fontsize=12)
    ax2.legend(fontsize=9)
    ax2.grid(alpha=0.3)
    ax2.set_xlim(0, 0.8)
    
    plt.tight_layout()
    
    # 建立摘要給 LLM
    summary = f"""
Trial: {trial_name}
R0={r0}, R1={r1}
Stage1: {s1_success}/{s1_total}, Stage2: {s2_success}/{s2_total}, Total: {total_success}/{total_n}
Prior: {prior_type}, Beta({prior_a:.1f},{prior_b:.1f})
Posterior: Beta({post2_a:.1f},{post2_b:.1f})
P(theta>R0)={1-post2_prob_lt_r0:.3f}, P(theta>R1)={post2_prob_gt_r1:.3f}
Frequentist: Stage1={'Pass' if stage1_pass else 'Fail'}, Final={'Effective' if final_pass else 'Ineffective'}
"""
    
    return result, fig, summary

# ============== LLM 解釋功能(OpenAI ChatGPT)==============

def get_llm_explanation(summary_str, question, api_key):
    """呼叫 OpenAI API (ChatGPT) 獲得 LLM 解釋"""
    
    if not api_key or api_key.strip() == "":
        return "⚠️ 請先輸入 OpenAI API Key\n\n(到 https://platform.openai.com/api-keys 取得)"
    
    if not summary_str:
        return "⚠️ 請先執行分析,再請 AI 解釋"
    
    try:
        from openai import OpenAI
        
        client = OpenAI(api_key=api_key)
        
        system_prompt = """你是一位專業的生物統計學家和臨床試驗專家,專門教導學生理解 Simon Two-stage Design 和貝氏分析。

你的教學基於 Tan et al. (2002) 發表在 British Journal of Cancer 的論文:
"A Bayesian re-assessment of two Phase II trials of gemcitabine in metastatic nasopharyngeal cancer"

請用繁體中文回答,語氣友善且易懂,像是老師在跟學生解釋。回答時請:
1. 用簡單的語言解釋統計結果的意義
2. 說明頻率學派和貝氏方法的差異
3. 解釋不同事前分佈(Clinical, Reference, Sceptical, Enthusiastic)的意義
4. 給出是否建議進入下一期試驗的建議"""

        user_prompt = f"""以下是一個 Simon Two-stage Design 臨床試驗的分析結果:

{summary_str}

學生的問題:{question if question else "請幫我解釋這個結果代表什麼意思?不同的事前分佈會如何影響結論?"}

請用簡單易懂的方式回答。"""

        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt}
            ],
            max_tokens=1500,
            temperature=0.7
        )
        
        return response.choices[0].message.content
        
    except Exception as e:
        return f"❌ API 呼叫失敗:{str(e)}\n\n請確認:\n1. API Key 是否正確\n2. 帳戶是否有餘額"

# ============== Gradio 介面 ==============

with gr.Blocks(
    title="Simon Two-stage Design 教學工具",
    theme=gr.themes.Soft(primary_hue="blue")
) as demo:
    
    # 儲存分析結果
    analysis_summary = gr.State("")
    
    # 標題
    gr.Markdown("""
    # 🔬 Simon Two-stage Design 互動式教學工具
    ## 基於 Tan et al. (2002) British Journal of Cancer 論文
    **A Bayesian re-assessment of two Phase II trials of gemcitabine in metastatic nasopharyngeal cancer**
    """)
    
    # 故事背景
    with gr.Accordion("📖 教學故事線", open=True):
        gr.Markdown("""
        > 🎯 **核心問題**:新藥開發很燒錢,怎麼「早點知道該不該繼續」?
        
        - 💰 一個新藥開發平均要 **10-20 億美元**
        - ⏰ 耗時 **10-15 年**
        - 📉 Phase II 失敗率高達 **70%**
        

        
        「如果藥沒效,能不能早點停?」
        「如果有希望,再多收病人確認」
        

        
        > **1989 年 Richard Simon 提出解決方案:**
        > 👉 **Simon Two-stage Design**
        
        ---
        
        ### 🏥 今天的案例:Gemcitabine 治療鼻咽癌
        
        新加坡國家癌症中心要測試 **gemcitabine(健擇)** 對轉移性鼻咽癌的療效,設計了兩個試驗:
        
        | 試驗 | 對象 | R₀(沒興趣) | R₁(理想) |
        |------|------|-------------|-----------|
        | **Trial C** | 初治患者 | 10% | 30% |
        | **Trial P** | 曾治療患者 | 5% | 20% |
        
        - 若反應率 ≤ R₀ → 藥物不值得繼續研究
        - 若反應率 ≥ R₁ → 值得進入 Phase III
        
        👇 **請選擇試驗、輸入數據,看看結果如何!**
        """)
    
    with gr.Accordion("📐 方法說明:四種事前分佈", open=False):
        gr.Markdown("""
        貝氏分析需要設定「事前分佈」,代表看到數據**之前**的信念:
        
        | 類型 | 定義 | 立場 |
        |------|------|------|
        | **Clinical** | P(θ<R₀) = P(R₀<θ<R₁) = P(θ>R₁) = 1/3 | 均衡看法 |
        | **Reference** | Uniform (1,1) | 完全不確定 |
        | **Sceptical** | 平均=R₀,P(θ>R₁)=5% | 懷疑,不太信 |
        | **Enthusiastic** | 平均=R₁,P(θ<R₀)=5% | 樂觀,相信藥效 |
        
        **Beta-Binomial 共軛更新**:
        ```
        事前:Beta(α, β) + 數據:n人中r人成功 → 事後:Beta(α+r, β+n-r)
        ```
        
        📚 *Tan SB et al. Br J Cancer. 2002;86(6):843-850.*
        """)
    
    gr.Markdown("---")
    
    # 主要設定區
    with gr.Row():
        with gr.Column(scale=1):
            gr.Markdown("### ⚙️ 試驗設定")
            
            trial_type = gr.Radio(
                choices=["Trial C (初治組)", "Trial P (曾治療組)"],
                value="Trial C (初治組)",
                label="選擇試驗類型"
            )
            
            gr.Markdown("### 📝 輸入試驗數據")
            gr.Markdown("*請根據論文填入數據,或點下方按鈕載入*")
            
            with gr.Row():
                s1_success = gr.Number(value=None, label="Stage I 成功人數", precision=0)
                s1_total = gr.Number(value=None, label="Stage I 總人數", precision=0)
            
            with gr.Row():
                s2_success = gr.Number(value=None, label="Stage II 成功人數", precision=0)
                s2_total = gr.Number(value=None, label="Stage II 總人數", precision=0)
            
            gr.Markdown("### 🔮 選擇事前分佈")
            
            prior_type = gr.Radio(
                choices=[
                    "Clinical Prior(臨床事前分佈)",
                    "Reference Prior(參考事前分佈)",
                    "Sceptical Prior(懷疑性事前分佈)",
                    "Enthusiastic Prior(樂觀性事前分佈)",
                    "Custom(自訂)"
                ],
                value="Clinical Prior(臨床事前分佈)",
                label="事前分佈類型"
            )
            
            with gr.Row(visible=False) as custom_row:
                custom_a = gr.Number(value=1, label="自訂 α", precision=1)
                custom_b = gr.Number(value=1, label="自訂 β", precision=1)
            
            # 載入預設值按鈕
            gr.Markdown("### 📚 載入論文數據(或自己填完後對答案)")
            with gr.Row():
                load_c_btn = gr.Button("📥 載入 Trial C 數據", size="sm")
                load_p_btn = gr.Button("📥 載入 Trial P 數據", size="sm")
            
            btn = gr.Button("🚀 執行分析", variant="primary", size="lg")
        
        with gr.Column(scale=2):
            gr.Markdown("### 📊 分析結果")
            output_text = gr.Markdown()
            output_plot = gr.Plot()
    
    # 載入預設值函數
    def load_trial_c():
        return "Trial C (初治組)", 3, 15, 4, 10
    
    def load_trial_p():
        return "Trial P (曾治療組)", 7, 13, 6, 14
    
    load_c_btn.click(load_trial_c, outputs=[trial_type, s1_success, s1_total, s2_success, s2_total])
    load_p_btn.click(load_trial_p, outputs=[trial_type, s1_success, s1_total, s2_success, s2_total])
    
    # 顯示/隱藏自訂參數
    def toggle_custom(prior):
        return gr.Row(visible=(prior == "Custom(自訂)"))
    
    prior_type.change(toggle_custom, prior_type, custom_row)
    
    # 執行分析
    btn.click(
        fn=analyze,
        inputs=[trial_type, s1_success, s1_total, s2_success, s2_total, 
                prior_type, custom_a, custom_b],
        outputs=[output_text, output_plot, analysis_summary]
    )
    
    # ========== LLM 解釋區 ==========
    gr.Markdown("---")
    gr.Markdown("### 🤖 AI 助教解釋")
    
    with gr.Row():
        with gr.Column(scale=2):
            user_question = gr.Textbox(
                label="你的問題(選填)",
                placeholder="例如:為什麼 Sceptical Prior 和 Enthusiastic Prior 的結論會不同?",
                lines=2
            )
        with gr.Column(scale=1):
            api_key = gr.Textbox(
                label="OpenAI API Key",
                placeholder="sk-...",
                type="password"
            )
    
    ask_ai_btn = gr.Button("🧠 請 AI 助教解釋", variant="secondary")
    ai_response = gr.Markdown()
    
    ask_ai_btn.click(
        fn=get_llm_explanation,
        inputs=[analysis_summary, user_question, api_key],
        outputs=[ai_response]
    )
    
    # 習題
    gr.Markdown("---")
    with gr.Accordion("✏️ 論文導讀問題", open=False):
        gr.Markdown("""
        ### 問題 1:頻率學派 vs 貝氏方法
        論文中提到「The conclusions drawn using the Bayesian approach were in general agreement with those obtained from the frequentist analysis」,但貝氏方法有什麼額外的優點?
        
        ### 問題 2:事前分佈的影響
        比較 Trial C 使用 Sceptical Prior 和 Enthusiastic Prior 的結果(論文 Table 1),為什麼 P(θ>R₁) 的值差異很大?
        
        ### 問題 3:Trial C vs Trial P
        Trial P 的結果(48% 反應率)比 Trial C(28%)好很多,論文作者如何解釋這個現象?
        
        ### 問題 4:實務應用
        如果你是藥廠的統計師,你會選擇哪種事前分佈來分析結果?為什麼?
        """)
    
    # 頁尾
    gr.Markdown("""
    ---
    <center>
    🔬 Simon Two-stage Design 教學工具<br>
    Based on Tan et al. (2002) British Journal of Cancer<br>
    <small>Powered by Gradio + OpenAI</small>
    </center>
    """)

if __name__ == "__main__":
    demo.launch()