#!/usr/bin/env python3 """Gepaarter Vergleich der HumanEval+-Laeufe (McNemar, exakt). WARUM: Bisher habe ich die Varianten ueber Binomial-Konfidenzintervalle verglichen (+-4,4 bis +-4,9 Punkte) und daraus geschlossen, es sei nichts unterscheidbar. Dieser Vergleich ist zu schwach, denn er behandelt die Laeufe als unabhaengige Stichproben. Sie sind aber GEPAART: jede Variante bearbeitet exakt dieselben 164 Aufgaben. Aufgaben, die alle loesen oder alle verfehlen, tragen keine Information ueber den Unterschied — nur die Faelle, in denen genau eine der beiden Varianten besteht (die "diskordanten Paare"), zaehlen. Der exakte McNemar-Test schaut ausschliesslich auf diese Paare: unter der Nullhypothese "beide gleich gut" ist ein diskordantes Paar ein Muenzwurf. """ import json import glob import math import itertools import os PREP = os.path.dirname(os.path.abspath(__file__)) def lade(pfad): d = json.load(open(pfad)) return d["modell"], {a["task"]: a["ok"] for a in d["aufgaben"]} def binom_zweiseitig(k, n): """Exakter Zweiseitentest, p=0.5. k Erfolge aus n.""" if n == 0: return 1.0 k = min(k, n - k) einseitig = sum(math.comb(n, i) for i in range(k + 1)) / 2 ** n return min(1.0, 2 * einseitig) laeufe = [lade(f) for f in sorted(glob.glob(f"{PREP}/he-humanevalplus/*.json"))] print(f"{len(laeufe)} Laeufe, je {len(laeufe[0][1])} Aufgaben\n") print("Gepaarte Vergleiche — nur diskordante Paare tragen Information:\n") print(f"{'A':38} {'B':38} {'nur A':>6} {'nur B':>6} {'p':>8}") print("-" * 100) zeilen = [] for (na, ra), (nb, rb) in itertools.combinations(laeufe, 2): gemeinsam = set(ra) & set(rb) nur_a = sum(1 for t in gemeinsam if ra[t] and not rb[t]) nur_b = sum(1 for t in gemeinsam if rb[t] and not ra[t]) p = binom_zweiseitig(nur_a, nur_a + nur_b) zeilen.append((p, na, nb, nur_a, nur_b)) for p, na, nb, a, b in sorted(zeilen): stern = " *" if p < 0.05 else "" print(f"{na[:38]:38} {nb[:38]:38} {a:6} {b:6} {p:8.3f}{stern}") print() print("Kleinstes p:", f"{min(z[0] for z in zeilen):.3f}") print("Signifikant bei 0,05:", sum(1 for z in zeilen if z[0] < 0.05), "von", len(zeilen)) # Wie gross muesste ein echter Unterschied sein, damit wir ihn saehen? print() print("Trennschaerfe: welche Aufteilung der diskordanten Paare waere signifikant?") for n in (6, 10, 15, 20, 25, 30, 40): # kleinstes k > n/2, das noch p < 0,05 liefert kand = [k for k in range(n // 2 + 1, n + 1) if binom_zweiseitig(k, n) < 0.05] if kand: k = min(kand) print(f" {n:2} diskordante Paare: ab {k}:{n-k} " f"(p={binom_zweiseitig(k, n):.3f}) — Vorsprung {2*k-n} Aufgaben") else: print(f" {n:2} diskordante Paare: NIE signifikant, selbst bei {n}:0 " f"(p={binom_zweiseitig(n, n):.3f})")