Kim-el commited on
Commit
c2c64d8
Β·
verified Β·
1 Parent(s): 0e4284f

Upload significance_test.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. significance_test.py +150 -0
significance_test.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Significance testing for Hadith vs Dense NDCG@10 differences.
3
+ Reads query_deltas.csv, runs paired t-test and randomization test."""
4
+ import csv, math, random, sys
5
+ import numpy as np
6
+ from scipy import stats as scipy_stats
7
+
8
+ CSV_PATH = "/workspace/rankings/query_deltas.csv" if len(sys.argv) < 2 else sys.argv[1]
9
+
10
+ def paired_t_test(a, b):
11
+ """Paired two-sided t-test. Returns t-statistic, p-value."""
12
+ diffs = np.array(a) - np.array(b)
13
+ n = len(diffs)
14
+ mean_d = np.mean(diffs)
15
+ std_d = np.std(diffs, ddof=1)
16
+ t_stat = mean_d / (std_d / math.sqrt(n)) if std_d > 0 else 0.0
17
+ # approximate p-value from t-distribution
18
+ from scipy.stats import t
19
+ p = 2 * t.sf(abs(t_stat), df=n-1)
20
+ return t_stat, p
21
+
22
+ def randomization_test(a, b, n_permutations=100_000):
23
+ """Paired randomization test. Returns p-value (fraction of permutations with |delta| >= observed)."""
24
+ diffs = np.array(a) - np.array(b)
25
+ observed = np.abs(np.mean(diffs))
26
+ n = len(diffs)
27
+ count_extreme = 0
28
+ for _ in range(n_permutations):
29
+ # Randomly flip each pair's sign
30
+ signs = np.random.choice([-1, 1], size=n)
31
+ perm_mean = np.abs(np.mean(diffs * signs))
32
+ if perm_mean >= observed:
33
+ count_extreme += 1
34
+ return (count_extreme + 1) / (n_permutations + 1) # +1 for continuity
35
+
36
+ def cohens_d(a, b):
37
+ """Cohen's d effect size."""
38
+ diffs = np.array(a) - np.array(b)
39
+ mean_d = np.mean(diffs)
40
+ std_d = np.std(diffs, ddof=1)
41
+ return mean_d / std_d if std_d > 0 else 0.0
42
+
43
+ # ── Load ──
44
+ print(f"Reading {CSV_PATH} ...")
45
+ rows = []
46
+ with open(CSV_PATH) as f:
47
+ reader = csv.DictReader(f)
48
+ for row in reader:
49
+ rows.append(row)
50
+
51
+ print(f"Loaded {len(rows)} queries\n")
52
+
53
+ # Extract named variants
54
+ variants = ["dense+T", "dense+S", "dense+M", "dense+TS", "dense+MT", "dense+MS", "dense+MTS"]
55
+ dense_col = "dense"
56
+
57
+ dense_ndcg = np.array([float(r[dense_col]) for r in rows])
58
+
59
+ print("=" * 80)
60
+ print("SIGNIFICANCE TEST REPORT β€” Hadith vs Dense (paired test)")
61
+ print("=" * 80)
62
+ print(f"{'Variant':<12} {'Mean Ξ”':>8} {'Std Ξ”':>8} {'t':>8} {'p(t-test)':>10} {'p(random)':>10} {'Cohen d':>8} {'Signif':>8}")
63
+ print("-" * 80)
64
+
65
+ results = []
66
+ for v in variants:
67
+ v_ndcg = np.array([float(r[v]) for r in rows])
68
+ deltas = v_ndcg - dense_ndcg
69
+
70
+ mean_d = np.mean(deltas)
71
+ std_d = np.std(deltas, ddof=1)
72
+
73
+ # Paired t-test
74
+ t_stat, p_ttest = paired_t_test(v_ndcg, dense_ndcg)
75
+
76
+ # Randomization test (10K permutations for speed; increase for final)
77
+ p_rand = randomization_test(v_ndcg, dense_ndcg, n_permutations=10_000)
78
+
79
+ # Cohen's d
80
+ d_cohen = cohens_d(v_ndcg, dense_ndcg)
81
+
82
+ # Significance label
83
+ if p_ttest < 0.001:
84
+ sig = "***"
85
+ elif p_ttest < 0.01:
86
+ sig = "**"
87
+ elif p_ttest < 0.05:
88
+ sig = "*"
89
+ else:
90
+ sig = "n.s."
91
+
92
+ results.append((v, mean_d, std_d, t_stat, p_ttest, p_rand, d_cohen, sig))
93
+ print(f"{v:<12} {mean_d:>+8.6f} {std_d:>8.6f} {t_stat:>+8.3f} {p_ttest:>10.6f} {p_rand:>10.6f} {d_cohen:>+8.4f} {sig:>8}")
94
+
95
+ print("-" * 80)
96
+ print("\nSignificance codes: *** p<0.001 ** p<0.01 * p<0.05 n.s. = not significant")
97
+ print()
98
+
99
+ # ── Summary statistics ──
100
+ print("\n" + "=" * 80)
101
+ print("SUMMARY STATISTICS β€” Per-Query Deltas")
102
+ print("=" * 80)
103
+ print(f"{'Variant':<12} {'Mean':>8} {'Median':>8} {'Std':>8} {'Min':>8} {'Max':>8} {'Q1':>8} {'Q3':>8} {'%Neg':>8} {'%Zero':>8}")
104
+ print("-" * 80)
105
+
106
+ for v, md, std_d, _, _, _, _, _ in results:
107
+ deltas = np.array([float(r[v]) for r in rows]) - dense_ndcg
108
+ median = np.median(deltas)
109
+ mn = np.min(deltas)
110
+ mx = np.max(deltas)
111
+ q1 = np.percentile(deltas, 25)
112
+ q3 = np.percentile(deltas, 75)
113
+ pct_neg = 100 * np.mean(deltas < 0)
114
+ pct_zero = 100 * np.mean(deltas == 0)
115
+ print(f"{v:<12} {md:>+8.6f} {median:>+8.6f} {std_d:>8.6f} {mn:>+8.4f} {mx:>+8.4f} {q1:>+8.6f} {q3:>+8.6f} {pct_neg:>7.1f}% {pct_zero:>7.1f}%")
116
+
117
+ print("-" * 80)
118
+
119
+ # ── Best sweep ──
120
+ print("\n" + "=" * 80)
121
+ print("BEST SWEEP β€” Tawatur threshold/tau sweep vs Dense")
122
+ print("=" * 80)
123
+ # Find best sweep variant
124
+ sweep_keys = [k for k in rows[0].keys() if k.startswith("tawatur_ct=")]
125
+ sweep_results = []
126
+ for k in sweep_keys:
127
+ v = np.array([float(r[k]) for r in rows])
128
+ deltas = v - dense_ndcg
129
+ mean_d = np.mean(deltas)
130
+ sweep_results.append((k, mean_d, np.std(deltas, ddof=1)))
131
+
132
+ sweep_results.sort(key=lambda x: -x[1])
133
+ print(f"{'Sweep':<30} {'Mean Ξ”':>10} {'Std Ξ”':>10}")
134
+ print("-" * 50)
135
+ for k, md, sd in sweep_results[:5]:
136
+ print(f"{k:<30} {md:>+10.6f} {sd:>10.6f}")
137
+ print(f"{'...':<30}")
138
+ for k, md, sd in sweep_results[-3:]:
139
+ print(f"{k:<30} {md:>+10.6f} {sd:>10.6f}")
140
+
141
+ # ── N queries helped/hurt ──
142
+ print("\n" + "=" * 80)
143
+ print("HELPED vs HURT β€” Query counts")
144
+ print("=" * 80)
145
+ for v in variants:
146
+ deltas = np.array([float(r[v]) for r in rows]) - dense_ndcg
147
+ helped = np.sum(deltas > 0.001) # >0.001 to avoid floating-point noise
148
+ hurt = np.sum(deltas < -0.001)
149
+ neutral = len(deltas) - helped - hurt
150
+ print(f"{v:<12}: helped={helped:>4} ({100*helped/len(deltas):>5.1f}%) hurt={hurt:>4} ({100*hurt/len(deltas):>5.1f}%) neutral={neutral:>4} ({100*neutral/len(deltas):>5.1f}%)")