#!/usr/bin/env python3
"""专业课笔记 · 可验证部分的自检

⚠️ 重要说明：
  认知科学的内容大多是**概念性**的（如"N400 反映语义整合"），
  这类断言**无法用代码验证**，只能靠对照文献。
  本脚本只验证笔记里那些**涉及统计学/概率**的、可以用计算检验的断言。

  所以：本脚本通过 ≠ 笔记全部正确。它只覆盖可计算的部分。
"""

import numpy as np
from scipy import stats

ok = lambda b: "✅" if b else "❌"

print("=" * 60)
print("专业课笔记 · 可验证部分自检")
print("=" * 60)
print()

print("【统计功效】")
print("-" * 60)

# 1. 样本量对检出效应的影响
def power_ttest(n, d=0.5, alpha=0.05):
    """双侧独立样本 t 检验的近似功效（用正态近似）。"""
    from scipy.stats import norm
    ncp = d * np.sqrt(n / 2)          # 非中心参数
    crit = norm.ppf(1 - alpha / 2)
    return (1 - norm.cdf(crit - ncp)) + norm.cdf(-crit - ncp)

p20 = power_ttest(20)   # 每组 20，中等效应
p64 = power_ttest(64)   # 每组 64
p128 = power_ttest(128)
print(f"  中等效应(d=0.5) 每组20人  功效 = {p20:.2f}  {ok(p20 < 0.5)}")
print(f"                  每组64人  功效 = {p64:.2f}  {ok(0.75 < p64 < 0.85)}")
print(f"                  每组128人 功效 = {p128:.2f}  {ok(p128 > 0.9)}")
print(f"  → 验证笔记'小样本功效不足'：N=20 时功效仅 {p20:.0%}")

# 2. 小样本导致效应量高估（赢家诅咒）
print()
np.random.seed(42)
true_d = 0.0   # 真实无效应
false_pos_small, false_pos_big, effects = 0, 0, []
N_TRIALS = 4000
for _ in range(N_TRIALS):
    g1 = np.random.randn(20); g2 = np.random.randn(20)
    t, p = stats.ttest_ind(g1, g2)
    if p < 0.05:
        false_pos_small += 1
        d = (g1.mean() - g2.mean()) / np.sqrt((g1.var(ddof=1) + g2.var(ddof=1)) / 2)
        effects.append(abs(d))
print(f"  真实无效应时，N=20/组 的假阳性率 = {false_pos_small/N_TRIALS:.3f}  (名义 α=0.05)")
print(f"  → 与名义 0.05 {'接近' if abs(false_pos_small/N_TRIALS-0.05)<0.02 else '偏离'} ✅")

# 3. 多重比较
print()
print("【多重比较】")
print("-" * 60)
for k in [1, 5, 20]:
    p_any = 1 - 0.95 ** k
    print(f"  {k:2d} 次独立检验，至少 1 次假阳性 = {p_any:.4f}")
print(f"  → 验证笔记：20 次检验假阳性率约 {1-0.95**20:.0%}  {ok(abs(1-0.95**20-0.64)<0.01)}")

# Bonferroni 校正效果
alpha_raw, k = 0.05, 20
alpha_bonf = alpha_raw / k
print(f"  Bonferroni 校正：{alpha_raw} → {alpha_bonf:.4f}  {ok(alpha_bonf < alpha_raw)}")

print()
print("【效应量估计的偏差】")
print("-" * 60)
# 只在显著的实验里看效应量，会系统性高估
np.random.seed(1)
true_d = 0.4
sig_effects = []
all_effects = []
for _ in range(3000):
    g1 = np.random.randn(30) + true_d / 2
    g2 = np.random.randn(30) - true_d / 2
    d = (g1.mean() - g2.mean()) / np.sqrt((g1.var(ddof=1) + g2.var(ddof=1)) / 2)
    all_effects.append(d)
    t, p = stats.ttest_ind(g1, g2)
    if p < 0.05:
        sig_effects.append(d)
print(f"  真实 d = {true_d}")
print(f"  全部实验的平均 d   = {np.mean(all_effects):.3f}  {ok(abs(np.mean(all_effects)-true_d)<0.05)}")
print(f"  仅显著实验的平均 d = {np.mean(sig_effects):.3f}  {ok(np.mean(sig_effects) > true_d)}")
print(f"  → 验证笔记'赢家诅咒'：只看显著结果会高估效应量")

print()
print("=" * 60)
print("⚠️ 本脚本只覆盖了统计学部分。")
print("   以下内容无法用代码验证，必须对照文献：")
print("   · 认知心理学：工作记忆容量、注意理论、决策偏差")
print("   · 神经科学：ERP 成分、脑区功能、方法分辨率")
print("   · 心理语言学：花园路径句、句法自主之争")
print("   · 实验方法：实验设计原则")
print("   这些请以教材和原始文献为准。")
print("=" * 60)
