Előzetesen rögzített vizsgálat: a permutációs teszt az éles nullhipotézis alatt, az eredeti randomizációs séma reprodukálásával igazolható. A percentilis bootstrap és a Wald-intervallum kis mintán vagy szélső arányok mellett pontatlanul fedhet; az alkalmazott intervallumeljárásnak szerepelnie kell az előzetes tervben.
A Kísérleti kérdés és előzetes elemzési terv
alfejezetben befagyasztott kísérleti terv szerint 300 szintetikus
ügyfelet randomizáltunk control és treatment
ágra. Az assignments.csv egy sor = egy ügyfél, a
customer_id egyedi, a variant oszlop a
randomizált beavatkozás. Az outcomes.csv ugyanezen ügyfelek
teljes, 7 napos kimenetét tartalmazza; a primer végpont a
conversion_7d bináris változó. A
revenue_7d_minor másodlagos, folytonos végpont, itt csak
érzékenységi szempontból jelenik meg. Az elemzési egység az ügyfél, nem
a rendelés vagy a rendelési tétel: a kísérleti nevező 300, és nem szabad
az order_items sorainak számával felnagyítani.
Bemenet: lab/assignments.csv,
lab/outcomes.csv. Közbenső tábla: customer_id
kulcsú join, egy-egy reláció. Kimenet: abszolút hatásbecslés (difference
in proportions), 95%-os konfidenciaintervallum, előre rögzített
permutációs teszt p-értéke, valamint érzékenységi ellenőrzés. A
módszerválasztás indoklása és legalább egy érvényességi korlát szöveges
megnevezése szintén átadandó.
Induló becslés: a generáló folyamatot nem ismerjük, de hasonló beavatkozásoknál 5–15 százalékpontos abszolút konverziónövekedés várható. A 300 fős mintán ilyen hatás észlelhető, de az intervallum széles marad: nagyságrendileg ±6–7 százalékpont a 95%-os fél-szélesség. A feladat nem az, hogy „szignifikáns legyen”, hanem hogy a bizonytalanság korrektül, a döntési célhoz illő skálán szerepeljen.
Difference in proportions. Legyenek a karok:
Az abszolút különbség mértékegysége konverzió/ügyfél, tehát 0,10 értéke azt jelenti: 100 randomizált ügyfélből várhatóan 10-zel többen konvertálnak a kezelt ágon. Ez az értelmezés közvetlenül használható költség–haszon döntésben.
Standard error és Wald-intervallum. Ha a két minta független binomiális megfigyelés, a különbség becsült szórása
Ez az ún. unpooled vagy Neyman-típusú képlet. Randomizált kísérletben a valódi variancia kismértékben eltér, mert a kimenetelek rögzítettek, és a véletlen a hozzárendelés; a binomiális közelítés azonban a gyakorlatban elfogadható, ha a cella-várható esetszámok nem extrém kicsik. A közelítő 95%-os intervallum
Ez aszimptotikus (nagy mintás) eljárás. A
confidence level a mintavételi eljárás hosszú távú
lefedettségéhez kötött: sok ismételt kísérletben a 95%-os intervallumok
körülbelül 95%-a tartalmazná a valódi hatást. Azt nem
állítja, hogy az aktuális intervallum 95% valószínűséggel tartalmazza a
paramétert; a paraméter rögzített, a véletlen az intervallumban van.
Kézi ellenőrzés: legyen , , . Ekkor , , . A standard error:
A 95%-os intervallum: . A nulla benne van, tehát ekkora mintán ez a hatás nem elkülöníthető a zajtól.
Effect scale. A bináris végpontnál három gyakori skála létezik:
A relatív és abszolút skála közötti választás üzleti kérdés: ha a konverzió alaparánya 2%, akkor a 2% → 3% relatíve 50%-os növekedés, abszolút értékben 1 százalékpont. A jelentésben mindkét skálát érdemes feltüntetni, de a következtetéshez előre rögzítsük az elsődlegeset — a terv szerint az abszolút különbséget.
Bootstrap. A bootstrap a mintavételezési bizonytalanságot újramintavételezéssel közelíti. Az elemzési egység az ügyfél, ezért a resamplingnak is ügyfélszintűnek kell lennie. Karokon belül, visszatevéssel húzunk és megfigyelést, újraszámoljuk -t, ezt -szer ismételjük. A bootstrap replicák -os és -os kvantilise adja a percentile-intervallumot. A módszer nem igényli a binomiális közelítést, de továbbra is feltételezi, hogy a megfigyelt minta reprezentatív a sokaságra, és a megfigyelések a resampling egység szintjén függetlenek.
Permutation test. A nullhipotézis: éles nulla — a beavatkozás egyetlen ügyfél kimenetére sem hat. Ekkor a kezelési címkék felcserélhetők. A permutációs teszt a megfigyelt kimeneteleket megtartja, a hozzárendelést véletlenül újraosztja, és minden permutációnál kiszámítja a két kar átlagos konverziókülönbségét. A p-érték a null-eloszlásból számított feltételes szélsőség:
A -es korrekció a Monte Carlo-hibát simítja; mellett a p-érték numerikus hibája néhány ezred. A permutációs teszt pontosabb kis mintán, mint a Wald-féle normalitás, mert nem támaszkodik aszimptotikára, csak a randomizációra.
Assumptions. Mindhárom eljárás érvényességéhez szükséges:
assignments.csv
szerint randomizált; ezt a terv rögzíti.outcomes.csv mind a 300
ügyfélre tartalmazza a 7 napos kimenetet, observed_until
azonos. Nincs lemorzsolódás. Ha hiányzó kimenet lenne, az ITT-elemzéshez
előre definiált imputáció vagy alsó/legrosszabb eset kell; az
exclusion nem lehet adatvezérelt.conversion_7d és a
permutációs teszt a megerősítő elemzés.A kód a lab/ gyökérből futtatható. Az alábbi szkript a
két CSV-t egyesíti, ellenőrzi a kulcs egyediségét, kiszámítja a
pontbecslést, a Wald- és bootstrap-intervallumot, valamint a permutációs
tesztet.
# lab/s158_estimate.py
from pathlib import Path
import numpy as np
import pandas as pd
from scipy import stats
base = Path("lab")
assign = pd.read_csv(base / "assignments.csv")
outcome = pd.read_csv(base / "outcomes.csv")
# Kötelező ellenőrzés: egy ügyfél pontosan egyszer szerepel.
assert assign["customer_id"].is_unique
assert outcome["customer_id"].is_unique
df = assign.merge(outcome, on="customer_id", validate="one_to_one")
assert df.shape[0] == 300
control = df.loc[df["variant"] == "control", "conversion_7d"].to_numpy()
treat = df.loc[df["variant"] == "treatment", "conversion_7d"].to_numpy()
n_c, n_t = len(control), len(treat)
p_c, p_t = control.mean(), treat.mean()
diff_hat = p_t - p_c
# Wald: két független arány különbségének standard hibája.
se = np.sqrt(p_c * (1 - p_c) / n_c + p_t * (1 - p_t) / n_t)
ci_wald = diff_hat + np.array([-1, 1]) * stats.norm.ppf(0.975) * se
rng = np.random.default_rng(20261020)
B = 10_000
# Ügyfélszintű bootstrap, karokon belül visszatevéssel.
boot_diffs = np.empty(B)
for b in range(B):
c_res = rng.choice(control, size=n_c, replace=True)
t_res = rng.choice(treat, size=n_t, replace=True)
boot_diffs[b] = t_res.mean() - c_res.mean()
ci_boot = np.quantile(boot_diffs, [0.025, 0.975])
# Permutációs teszt: éles nulla mellett a címkék felcserélhetők.
all_y = np.concatenate([control, treat])
n_total = len(all_y)
perm_diffs = np.empty(B)
for b in range(B):
perm = rng.permutation(n_total)
perm_control = all_y[perm[:n_c]]
perm_treat = all_y[perm[n_c:]]
perm_diffs[b] = perm_treat.mean() - perm_control.mean()
p_value = (np.sum(np.abs(perm_diffs) >= np.abs(diff_hat)) + 1) / (B + 1)
print(f"n_control={n_c}, n_treatment={n_t}")
print(f"p_control={p_c:.4f}, p_treatment={p_t:.4f}")
print(f"Abszolút hatás (risk difference) = {diff_hat:.4f}")
print(f"Relatív kockázat RR = {p_t / p_c:.2f}")
print(f"Wald SE = {se:.4f}, 95% CI = ({ci_wald[0]:.4f}, {ci_wald[1]:.4f})")
print(f"Bootstrap 95% CI = ({ci_boot[0]:.4f}, {ci_boot[1]:.4f})")
print(f"Permutációs p-érték = {p_value:.4f}")A blokk kimenete a helyi adattól függ. Az alábbi szemléltető kimenet a generáló paraméterek nagyságrendjét mutatja, és nem állítjuk, hogy éppen ezt a futást láttuk:
Szemléltető kimenet — a tényleges értékek a lab adatain futáskor képződnek:
n_control=150, n_treatment=150
p_control=0.1733, p_treatment=0.2867
Abszolút hatás (risk difference) = 0.1133
Relatív kockázat RR = 1.65
Wald SE = 0.0485, 95% CI = (0.0183, 0.2083)
Bootstrap 95% CI = (0.0200, 0.2067)
Permutációs p-érték = 0.0095
Ha a tényleges fájlokból más értékek jönnek ki, az a véletlen
mintavétel következménye; az elemzés logikája változatlan. A
one_to_one join-ellenőrzés megakadályozza, hogy véletlenül
egy ügyfelet többször számoljunk, ami az ITT-nevezőt hamisítaná.
A fenti szemléltető számok alapján: a kezelt ágon a konverziós arány körülbelül 28,7%, a kontrollon 17,3%, az abszolút különbség 11,3 százalékpont. A relatív kockázat : a kezelés 65%-os relatív konverziónövekedéssel jár. A 95%-os intervallum mindkét közelítésben kizárja a nullát, tehát a hatás a véletlentől elkülöníthető. A p-érték kerekítve 0,010: ilyen vagy szélsőségesebb különbség az éles nulla mellett a permutációk körülbelül 1%-ában fordul elő.
Fontos, hogy a p-érték nem a nullhipotézis igazságának valószínűsége, és nem a hatás nagyságát méri. A hatás nagyságát a pontbecslés és az intervallum hordozza: a legvalószínűbb érték 11 százalékpont körül van, de a 95%-os bizonytalansági sáv nagyjából 2–21 százalékpont. Ha a döntési küszöb például 5 százalékpont, akkor a legkisebb érdemi hatást (MDE) a terv szerint 10 százalékpontra állítottuk; ez a minta az alsó határon mozog, a következtetést tehát óvatosan, a teljes intervallum együttes bemutatásával kell megfogalmazni.
A Wald- és a bootstrap-intervallum hasonló, mert a mintaméret elég nagy a binomiális normalitáshoz. A bootstrap akkor is használható, ha a normális közelítés kérdéses; itt a két módszer egyezése önmagában diagnosztikai jel, nem „több szignifikancia”.
Az érvényességi korlát: a szintetikus adatban nincs lemorzsolódás,
nincs késleltetett címke, és az ügyfelek nem lépnek interakcióba. Valós
éles környezetben a Késő címke, teljesítmény és
újratanítás alfejezetben tárgyalt késő címke vagy visszacsatolási
hurok az ITT-elemzést csak akkor nem torzítja, ha a kimenet a teljes
ablakban megfigyelt. Itt a observed_until minden sorban
azonos, ezért ez a feltétel ellenőrzött.
1. Értelmezés. A fenti szemléltető kimenet alapján írjon egy pontos mondatot a hatásról és bizonytalanságáról, amely nem állítja, hogy „a kezelés 95% valószínűséggel növeli a konverziót”, és nem kezeli a p-értéket a nullhipotézis valószínűségeként. Indokolja, miért hibás a „95%-os valószínűség” megfogalmazás.
2. Megvalósítás kézi kontrollal. A következő kis
adaton ellenőrizze a permutációs tesztet úgy, hogy az összes lehetséges
permutációt végignézi (exhaustive), ne Monte Carlo-mintát használjon.
Kontroll: [0, 0, 1, 0, 1, 1, 1, 0, 0, 1], kezelt:
[1, 0, 1, 1, 0, 1, 1, 1, 0, 1]. Számítsa ki
-t,
a Wald-SE-t és a pontos permutációs p-értéket. Hány lehetséges
hozzárendelés van, ha a mintaelemszámok rögzítettek?
3. Hibadiagnózis. Egy kolléga a következő kóddal
próbál bootstrap intervallumot számolni, de véletlenül az
order_items táblát is bejoinolja, mielőtt a konverziót
aggregálná:
df_bad = (assign.merge(outcome, on="customer_id")
.merge(order_items, on="order_id", how="left"))
# ... majd df_bad-ból bootstrap replikákat húz soronkéntMagyarázza el, miért torzítja ez a konverziós arányok bootstrap-eloszlását, és hogyan javítaná a kódot az elemzési egység megtartásával.
4. Megváltozott feltétel. Tegyük fel, hogy a kimenet 5%-a hiányzik, és a hiányzás a kezelt ágon gyakoribb, mert a kezelés mobilalkalmazás-értesítést küld, és néhány ügyfél törli az alkalmazást a 7 napos ablak előtt. Hogyan változtatná az elemzést? Nevezzen meg legalább két lehetséges, előre rögzíthető kezelési módot, és indokolja, miért nem elfogadható a hiányzó sorok egyszerű elhagyása ITT-elemzésben.
1. megoldás. Helyes mondat: „A kezelt ágon a konverziós arány 11,3 százalékponttal magasabb (95% CI: 1,8–20,8 százalékpont); a permutációs teszt p = 0,010, tehát az éles nulla mellett a megfigyelt különbség a permutációs eloszlás szélső 1%-ában van.” A „95% valószínűséggel növeli” megfogalmazás azért hibás, mert a konfidenciaszint az eljárás ismételt mintavételi lefedettségére vonatkozik, nem az aktuális paraméterre vonatkozó szubjektív hitre. A paraméter rögzített; a véletlen az intervallum helyzetében van.
2. megoldás. A minták: kontroll , összeg , tehát ; kezelt , összeg , tehát ; . Wald-SE:
A lehetséges hozzárendelések száma, ha a karok mérete rögzített és : . Az összes permutációt végigjárni még számítógép nélkül is sok, de Pythonnal generálható az összes kombináció:
from itertools import combinations
import numpy as np
y = np.array([0,0,1,0,1,1,1,0,0,1, 1,0,1,1,0,1,1,1,0,1])
n_total = len(y)
n_c = 10
obs_diff = y[10:].mean() - y[:10].mean() # 0.2
all_diffs = []
for c_idx in combinations(range(n_total), n_c):
c_mask = np.zeros(n_total, dtype=bool)
c_mask[list(c_idx)] = True
c = y[c_mask]
t = y[~c_mask]
all_diffs.append(t.mean() - c.mean())
all_diffs = np.array(all_diffs)
p_exact = (np.sum(np.abs(all_diffs) >= np.abs(obs_diff)) + 1) / (len(all_diffs) + 1)
print(f"Pontos permutációs p-érték: {p_exact:.4f}")A pontos p-érték a fenti adaton -hez közeli érték lesz, mert ilyen kis mintán a különbség a nulla körüli permutációs eloszlás jelentős részében előfordul. Az ellenőrzés: a Monte Carlo-változat -rel hasonló nagyságrendet ad; az eltérés a Monte Carlo-hiba.
3. megoldás. Az order_items join után
egy ügyfél annyi sorban jelenik meg, ahány rendelési tétele van. A
konverzió bináris értékét minden sorban megismétli a join, így a
soronkénti bootstrap az ügyfelek tételszám szerinti súlyozását vezeti
be: egy több tételt vásárló ügyfél nagyobb eséllyel kerül a replikába. A
bootstrap-eloszlás ezért az ilyen ügyfelek konverzióját
felülreprezentálja, a szórást pedig torzítja. Javítás: előbb
ügyfélszintű táblát készítsünk, a join előtt aggregáljunk, vagy a
bootstrapet kizárólag az assignments + outcomes egy-egy
relációjú dataframe-en futtassuk. A validate="one_to_one"
már a merge-nél hibát dob, ha nem egyedi a kulcs.
4. megoldás. A hiányzó sorok egyszerű elhagyása sértheti a randomizáció által biztosított összehasonlíthatóságot: ha a hiányzás a kezeléstől függ, a megmaradó minta már nem éles nulla mellett sem cserélhető fel. Két előre rögzíthető lehetőség: (a) Worst-case imputáció: a hiányzó konverziót a kezelt ágon 0-nak, a kontroll ágon 1-nek kódoljuk, így a hatás alsó korlátját konzervatívan becsüljük; (b) Többszörös imputáció vagy stratifikált elemzés a hiányzási mechanizmus feltételezett okait (például app verzió, értesítési beállítás) bevonva. Az ITT-elv szerint a nevező továbbra is minden randomizált ügyfél; a kizárás csak akkor megengedett, ha a kizárási kritérium a randomizáció előtt mérhető és előre rögzített.
Ugyanazt a mintát sok módszerrel megvizsgálni, majd a legkedvezőbb eredményt kiválasztani, exploratív szelekció. A Wald-, bootstrap- és permutációs eljárás nem független teszt, de ha például húsz különböző kiértékelési döntést próbálunk ki (más végpont, más ablak, más szűrés), és a legkisebb p-értéket jelentjük, a hamis pozitív esély összeadódik. Éles nulla mellett minden 5%-os szintű teszt önmagában 5% eséllyel téved; húsz független tesztnél annak valószínűsége, hogy legalább egy szignifikáns lesz, .
Szemléltető szimuláció:
import numpy as np
from scipy import stats
rng = np.random.default_rng(42)
m = 20
reps = 1000
min_p = np.empty(reps)
for r in range(reps):
pvals = np.empty(m)
for j in range(m):
x = rng.normal(0, 1, 50)
y = rng.normal(0, 1, 50)
pvals[j] = stats.ttest_ind(x, y).pvalue
min_p[r] = pvals.min()
fp_rate = (min_p < 0.05).mean()
print(f"Húsz párhuzamos teszt után a legkisebb p < 0.05 aránya: {fp_rate:.2f}")A várható kimenet körülbelül 0.64, nem
0.05. Ezért a Kísérleti kérdés és
előzetes elemzési terv alfejezetben a primer végpontot, a tesztet és
az elemzési egységet előre rögzítettük. Az itt elvégzett Wald- és
bootstrap-intervallum ugyanazt a becslési célt ellenőrzi; a kettő
együttes bemutatása diagnosztikai, nem többszörös tesztelés. Ha további
elemzéseket (szegmens szerint, másodlagos végpont) is futtatunk, azokat
külön, exploratív címkével kell közölni a Tudományos
jelentés és reprodukálható bemutató alfejezetbeli jelentésben.
Az abszolút konverziókülönbség, a hozzá tartozó standard error, bootstrap-intervallum és permutációs p-érték együtt ad korrekt kísérleti következtetést. Az elemzési egység az ügyfél, a resampling is ügyfélszintű; a Wald-közelítés a nagyobb, a permutációs teszt a kisebb mintán pontos. Az intervallum a hatás bizonytalanságát, a p-érték pedig az éles nulla alatti szélsőséget méri — nem a nullhipotézis igazságát. Az érvényességi feltételek (randomizáció, interferencia hiánya, teljes megfigyelés) kimondandók; hiányzó kimenetnél az ITT-elvet konzervatív vagy előre rögzített imputáció őrzi. A módszerek közötti válogatás adatvezérelten exploratív szelekció, és felfújja a hamis pozitív arányt. A következő Tudományos jelentés és reprodukálható bemutató alfejezetben a tudományos jelentés és reprodukálható bemutató foglalja egységbe ezt az elemzést.