A Northstar kísérlet lezárult: 300 ügyfél randomizálva a
treatment és control karra, a primer kimenet a
randomizálás után 7 nappal mért conversion_7d (0/1). A
bemenet a lab/assignments.csv és a
lab/outcomes.csv; a megfigyelési egység egy ügyfél, ezért
az egyesítés kulcsa customer_id. A cél most nem új becslő
építése, hanem a kísérlet tudományos közlése: egy rövid
jelentés, amelyben a becsült hatás, a bizonytalanság, a design problémái
és a döntési javaslat külön szakaszban szerepel.
Induló becslés: a szimuláció generáló hatása 10 százalékpont volt, de a 300 elemű minta ettől véletlenül eltérhet. A jelentésnek ezért nem a generáló értéket, hanem a mintából számolt becslést és annak intervallumát kell tárgyalnia. A döntési kérdés: érdemes-e a kezelést a teljes ügyfélkörre bevezetni?
A kísérleti hatást az átlagos kezelési hatás (average treatment effect, ATE) becslőjével adjuk meg. Legyen (Y_i \in {0, 1}) az (i)-edik ügyfél konverziója, (Z_i \in {0, 1}) a véletlen kezeléskiosztás (0 = kontroll, 1 = kezelés), (n_1) és (n_0) a két kar létszáma. A becslő
[ \hat{\tau} = \bar{y}_1 - \bar{y}_0, \qquad \bar{y}j = \frac{1}{n_j} \sum{i: Z_i = j} Y_i, ]
ahol (\bar{y}_j) dimenzió nélküli arány, értéke 0 és 1 közé esik, így (\hat{\tau}) tartománya ([-1, 1]). 100-zal szorozva a hatást százalékpontban kapjuk: ha (\hat{\tau} = 0{,}08), akkor a kezelés 8 százalékponttal növeli a becsült konverziót.
A standard hiba bináris kimenet esetén, független csoportokra:
[ \widehat{SE}(\hat{\tau}) = \sqrt{ \frac{\bar{y}_1 (1 - \bar{y}_1)}{n_1} + \frac{\bar{y}_0 (1 - \bar{y}_0)}{n_0} }. ]
A számlálóbeli (\bar{y}(1-\bar{y})) a csoporton belüli variancia becslése; a nevező az adott kar mintanagysága. A standard hiba ugyanabban az egységben van, mint (\hat{\tau}), tehát arány, illetve 100-zal szorozva százalékpont. A Nagy számok és centrális határeloszlás alfejezetben látott gondolat ismétlődik: az átlag ingadozása (\sigma/\sqrt{n}) nagyságrendű, itt csak mindkét kar varianciáját összeadjuk, mert a két mintaátlag különbségének varianciája a varianciák összege.
Közelítő 95%-os konfidenciaintervallum:
[ \left[ \hat{\tau} - z_{0{,}975} \widehat{SE}, ; \hat{\tau} + z_{0{,}975} \widehat{SE} \right], \qquad z_{0{,}975} \approx 1{,}96. ]
Az intervallum gyakorisági értelmezésű: ha az eljárást sok független kísérleten megismételnénk, az így számolt intervallumok körülbelül 95%-a fedné a valódi hatást. A konkrét intervallumra nem mondjuk, hogy „95% valószínűséggel tartalmazza a hatást”, mert a valódi hatás paraméter, nem véletlen változó ebben a keretben. A pontos értelmezéshez és a különbséghez lásd Standard error és konfidenciaintervallum és a Bayesi alternatívát Credible interval és posterior predictive check.
Kis mintán a fenti Wald-intervallum kiléphet a ([-1, 1]) tartományból. Kézi ellenőrzés: ha (n_0 = n_1 = 4), (\bar{y}_0 = 1/4), (\bar{y}_1 = 3/4), akkor
[ \hat{\tau} = 0{,}5, \quad \widehat{SE} = \sqrt{\frac{0{,}25 \cdot 0{,}75}{4} + \frac{0{,}75 \cdot 0{,}25}{4}} = \sqrt{0{,}09375} \approx 0{,}306, ]
tehát a 95%-os intervallum (0{,}500 \pm 1{,}96 \cdot 0{,}306 = [-0{,}100; 1{,}100]). A felső határ nagyobb 1-nél, ami mutatja, hogy ilyen kis minta esetén a normál közelítés nem megbízható. A labor jelentésében ezért a fő következtetést a 300 ügyfélre számolt intervallummal, a kis mintás alternatívát pedig ügyfélszintű bootstrap segítségével adjuk meg.
A külső érvényesség (external validity) azt kérdezi: a kísérletben becsült hatás milyen populációra, időszakra és megvalósításra vihető át. A belső érvényesség a kísérleten belüli torzítatlanságot jelenti; a kettő független.
A Northstar esetben négy korlátot kell a jelentésben megnevezni:
segment és region eloszlást követ. Más
összetételű ügyfélkörre a hatás nagysága változhat.treatment a szimuláció
egyetlen működési formája. Más üzenet, új csatorna vagy rosszabb
kivitelezés nem ugyanazt a hatást garantálja.A statisztikai szignifikancia nem pótolja ezt a kérdést. Egy eredmény lehet belsőleg torzítatlan, szignifikáns, mégsem általánosítható a célpopulációra.
A megerősítő (confirmatory) elemzés az előre
rögzített primer kérdés: a conversion_7d aránykülönbsége a
két kar között. Ehhez tartozik az előre választott ITT-definíció (Randomizálás és kísérleti protokoll): a nevezőben
mind a 300 randomizált ügyfél szerepel, függetlenül attól, hogy a
kezelést megkapta-e vagy megnyitotta-e. Az erre számolt intervallumot
használjuk döntési bemenetként.
A feltáró (exploratory) elemzés minden, ami utólag
merül fel: a revenue_7d_minor összehasonlítása,
szegmensenkénti bontás, időbeli görbék, alcsoport-eltérések. Ezek fontos
hipotéziseket adhatnak a következő kísérlethez, de nem
ugyanazzal a megerősítő erővel bírnak. A jelentésben ezért külön cím
alatt, „Feltáró megfigyelések” néven szerepeljenek, a primer becsléstől
vizuálisan is elválasztva.
A megkülönböztetés oka a többszörös próba és a szelektív közlés: ha sok utólagos metszetet nézünk, a véletlen kiugrások közül válogatunk. A primer elemzés előre rögzítése védi a p-érték értelmezhetőségét; a feltáró eredmény csak következő kísérleti tervet indokolhat.
A döntési veszteség (decision loss) a becslést a döntési kontextushoz köti. A statisztikai kérdés „van-e hatás?”, a döntési kérdés „érdemes-e bevezetni?”. A kettő közötti híd a minimális gyakorlati hatás (minimum practical effect) és a tévedés költsége.
Legyen (\theta) a valódi hatás százalékpontban, és (\theta_{\min} = 3) százalékpont az a küszöb, amely alatt a bevezetés üzletileg nem éri meg. A döntési szabály egyszerű formája:
A harmadik eset a bizonytalanság explicit elismerése: a minta nem
elég a döntéshez, nem „majdnem szignifikáns” eredmény. A veszteség itt
kétféle lehet, és mindkettőt érdemes megnevezni: a téves bevezetés
költsége (ha a valódi hatás negatív vagy (\theta_{\min}) alatti), és az
elmaradt haszon költsége (ha a valódi hatás (\theta_{\min}) felett van,
de elutasítjuk). A veszteség skálája pénzben vagy más üzleti célban
fejezhető ki; a kísérleti adatban a revenue_7d_minor
EUR-centben van, de a konverziós döntéshez itt nem adunk meg vállalati
árazást, mert a labor szintetikus.
A következő kód a két laborfájlt egyesíti, kiszámolja a hatásbecslést, a normál közelítésű intervallumot és egy ügyfélszintű bootstrap intervallumot. A kódot ebben a generálásban nem futtattuk; a megjegyzett kimenet szemléltető, a tényleges számok a te futásodtól függenek. A kód Linux, macOS és Windows PowerShell alatt is futtatható, a munkakönyvtár a projektgyökér.
import numpy as np
import pandas as pd
from pathlib import Path
root = Path("lab")
assign = pd.read_csv(root / "assignments.csv")
out = pd.read_csv(root / "outcomes.csv")
df = (
assign.merge(out, on="customer_id", validate="one_to_one")
.sort_values("customer_id")
)
# Ellenőrző kapu: ITT-nevező és séma
assert df["customer_id"].is_unique
assert df.shape[0] == 300
assert df["conversion_7d"].isin([0, 1]).all()
assert set(df["variant"]) == {"control", "treatment"}
by = df.groupby("variant")["conversion_7d"]
means = by.mean() # shape: (2,)
ns = by.size() # shape: (2,)
diff = means["treatment"] - means["control"]
se = float(np.sqrt(
(means["treatment"] * (1 - means["treatment"]) / ns["treatment"])
+ (means["control"] * (1 - means["control"]) / ns["control"])
))
z = 1.959963984540054 # normális 0.975 kvantilis
lo = diff - z * se
hi = diff + z * se
# Ügyfélszintű percentile bootstrap
rng = np.random.default_rng(20261020)
B = 2000
c = df.loc[df["variant"] == "control", "conversion_7d"].to_numpy()
t = df.loc[df["variant"] == "treatment", "conversion_7d"].to_numpy()
boot_diffs = np.empty(B)
for b in range(B):
cb = rng.choice(c, size=c.size, replace=True)
tb = rng.choice(t, size=t.size, replace=True)
boot_diffs[b] = tb.mean() - cb.mean()
boot_lo, boot_hi = np.quantile(boot_diffs, [0.025, 0.975])
print(f"n_control={ns['control']}, n_treatment={ns['treatment']}")
print(f"mean_control={means['control']:.4f}, mean_treatment={means['treatment']:.4f}")
print(f"diff={diff:.4f}, SE={se:.4f}")
print(f"normal_CI=[{lo:.4f}, {hi:.4f}]")
print(f"bootstrap_CI=[{boot_lo:.4f}, {boot_hi:.4f}]")
# Döntési szabály
theta_min = 0.03
if lo > theta_min:
decision = "bevezetés"
elif hi < 0:
decision = "elutasítás"
else:
decision = "további adat"
print(f"decision_with_theta_min={decision} (theta_min={theta_min})")Egy lehetséges szemléltető futás kimenete:
n_control=150, n_treatment=150
mean_control=0.1867, mean_treatment=0.2867
diff=0.1000, SE=0.0472
normal_CI=[0.0074, 0.1926]
bootstrap_CI=[0.0067, 0.1933]
decision_with_theta_min=további adat (theta_min=0.03)
A pontos számok a véletlen generálás miatt eltérhetnek; a fenti kimenet csak a formátumot mutatja. A laborban a saját futást tüntesd fel a jelentésben.
A kód előbb szerződés-kapuval ellenőrzi a bemenetet: a
validate="one_to_one" garantálja, hogy egy ügyfélhez
pontosan egy kimenet tartozik, a shape == (300, ...) pedig
az ITT-nevezőt. A bootstrap az ügyfélszintű független mintavételi
egységet tartja meg: a karokon belül újramintavételezünk egész
ügyfeleket, nem sorokat, így az order_items nem
sokszorozhatja a nevezőt.
Kézi kontroll a normál intervallumra: a fenti szemléltető adatokkal (\bar{y}_1 = 0{,}2867), (\bar{y}_0 = 0{,}1867), (n_1 = n_0 = 150), így
[ \widehat{SE} = \sqrt{\frac{0{,}2867 \cdot 0{,}7133}{150} + \frac{0{,}1867 \cdot 0{,}8133}{150}} \approx 0{,}0472, ]
és a (0{,}1000 \pm 1{,}96 \cdot 0{,}0472) intervallum ([0{,}0074; 0{,}1926]), egyezik a kimenettel. A bootstrap intervallum itt közel azonos, mert a mintanagyság elég nagy; kis mintán a Wald-intervallum kilépése ellen a bootstrap jobb védelmet ad.
Az értelmezésben három állítást különítsünk el:
A revenue_7d_minor elemzése ugyanilyen szerkezettel, de
feltáró cím alatt jelenik meg; a döntést nem arra alapozzuk.
Jelentési minimum: közölni kell a randomizált, megfigyelt és elemzett egységek számát karonként, a hiányzó végpontok kezelését, a becslési célt, a pontbecslést és bizonytalanságot; az előre rögzített és az utólagos elemzést egyértelműen különítsük el.
Értelmezés. Tegyük fel, hogy a laborfutásod ezt
adta: diff=0.084, normal_CI=[0.012, 0.156],
(n_0 = n_1 = 150). Fogalmazd meg egy mondatban a becslést, egy mondatban
a bizonytalanságot, és dönts a (\theta_{\min} = 0{,}03) küszöbbel.
Indokold, miért nem elég azt mondani, hogy „szignifikáns, tehát
bevezetjük”.
Implementálás. Írj
write_report(df, out_path) függvényt, amely a fenti
számításokból a report.md fájlba négy szakaszt ír:
## Becslés, ## Bizonytalanság,
## Design és korlátok, ## Döntési javaslat. A
design szakaszban három, a külső érvényességet érintő korlát is
szerepeljen; a döntési szakasz a (\theta_{\min}) szabályt alkalmazza. A
függvény visszatérési értéke a négy szakasz szövege.
Hibadiagnózis. Egy kolléga jelentése ezt írja: „A kezelést megnyitó 102 ügyfél 37%-a konvertált, a kontroll 24%-a konvertált; a kezelés 13 százalékpontot javít.” Nevezd meg a protokollsértést, és add meg a helyes ITT-nevezőt és a helyes becslési elvet. Milyen torzítást okoz a megnyitásra szűrés?
Megváltozott feltétel. Cseréld a primer
kimenetet revenue_7d_minor-ra, és számold ki a két kar
átlagkülönbségét, a standard hibát és a 95%-os intervallumot. A kapott
intervallumot miért szabad csak feltáró eredményként közölni? Milyen
további design-elemet kellene rögzíteni egy következő kísérletben ahhoz,
hogy a bevételi cél megerősítő legyen?
A becslés: a kezelés 8,4 százalékponttal növeli a konverziót a mintában. A bizonytalanság: a 95%-os intervallum 1,2 és 15,6 százalékpont közé esik. A döntés (\theta_{\min} = 0{,}03) mellett „további adat”: az alsó határ 0,012, ami kisebb 0,03-nál, tehát nem zárható ki, hogy a hatás kisebb a gyakorlati küszöbnél. A p < 0,05 önmagában csak a nulla hatást zárja ki; nem állítja, hogy a hatás hasznos, és nem pótolja a külső érvényességet.
Egy lehetséges váz:
def write_report(df, out_path):
# ... ugyanaz a diff, se, lo, hi számítás ...
theta_min = 0.03
if lo > theta_min:
decision = "bevezetés"
elif hi < 0:
decision = "elutasítás"
else:
decision = "további adat"
text = (
"## Becslés\n"
f"A kezelés becsült hatása {diff:.3f} százalékpont.\n\n"
"## Bizonytalanság\n"
f"A 95%-os intervallum [{lo:.3f}, {hi:.3f}] százalékpont.\n\n"
"## Design és korlátok\n"
"- A minta szintetikus, adott szegmens- és régióeloszlással.\n"
"- A megfigyelési ablak fix 7 napos.\n"
"- A kezelés egyetlen megvalósítási formáját teszteltük.\n\n"
"## Döntési javaslat\n"
f"A {theta_min} százalékpontos minimális gyakorlati hatás mellett: {decision}.\n"
)
Path(out_path).write_text(text, encoding="utf-8")
return textEllenőrző invariant: a fájl négy ## szakaszt tartalmaz,
a döntési szakasz pontosan egyet a három lehetséges érték közül, és a
becslési szakasz számértéke egyezik a korábban kiszámolt
diff értékkel (10^{-3}) tűrésen belül.
A kolléga megsértette az ITT-elvet (Randomizálás és kísérleti protokoll): a kezelési kar nevezőjét a kezelés megnyitására szűrte, ami a kezelés expozíciójának utólagos feltételét jelenti. A megnyitás maga is kimenet, ezért a szűrés kiválasztási torzítást okoz: a megnyitó ügyfelek eleve aktívabbak lehetnek, így a 13 százalékpontos különbség a kiválasztás és a kezelés keveréke. A helyes nevező mind a 150 randomizált ügyfél a kezelési karban, tehát a becslés a kezelési kar összes ügyfelének átlagos konverziója mínusz a kontroll összes ügyfelének átlagos konverziója. A megnyitási arány csak mint folyamatmutató (guardrail) elemezhető, nem mint a hatásbecslés nevezője.
A bevételi elemzés:
rev = df.groupby("variant")["revenue_7d_minor"]
rev_means = rev.mean()
rev_vars = rev.var(ddof=1)
rev_diff = rev_means["treatment"] - rev_means["control"]
rev_se = float(np.sqrt(
rev_vars["treatment"] / ns["treatment"]
+ rev_vars["control"] / ns["control"]
))A pénzegység EUR-cent; az átlagok, a különbség és a standard hiba is
centben van. Az intervallum ugyanazzal a normál kvantilissel számolható,
de az eloszlás ferdesége miatt bootstrap ajánlott. Feltáró a bevétel,
mert a primer elemzés csak a conversion_7d volt; a bevételt
utólag választottuk, ezért a becsült intervallum nem hordozza a
megerősítő kísérlet garanciáit. Egy következő kísérletben előre
rögzíteni kell: a bevételi ablakot (napok, pénznem, visszatérítés
kezelése), a primer elemzést (átlag vagy medián, nyertességi arány), a
szükséges minimális hatást centben, és a többszörös tesztelésre
vonatkozó szabályt, ha a konverziót is megtartjuk másodlagos
kimenetként.
A leggyakoribb tévedés: a statisztikai szignifikancia azonosítása a hasznossággal vagy a külső érvényességgel. Ellenpélda: két kar, (n = 50,000) ügyfél, a becsült hatás 0,5 százalékpont, a standard hiba 0,2 százalékpont. A p-érték 0,012, tehát szignifikáns, de a hatás a 3 százalékpontos gyakorlati küszöb alatt van, és a teljes intervallum is a küszöb alatt marad. A döntési szabály helyesen „elutasítás” lehet, miközben a p-érték kicsi. A nagy minta itt sem javítja a külső érvényességet: ha a kísérlet csak egy régióban futott, az 50 000 fős eredmény sem vihető át automatikusan más régióra.
Visszautalás: a standard hiba itt a Nagy számok és centrális határeloszlás alfejezetben tanult mintavételi eloszlás kétmintás változata; a GLM: bináris és számlálási cél logisztikus modellje ugyanezt a bináris kimenetet link-skálán modellezné, de a döntési jelentéshez az abszolút aránykülönbség érthetőbb; a Randomizálás és kísérleti protokoll ITT-definíciója rögzíti a nevezőt; a Dátumok, időzónák és kategóriák időtípusai pedig a 7 napos ablak konzisztens kezelését biztosítják.
Önellenőrzés a leadás előtt: a jelentésemben külön szerepel-e a becslés, a bizonytalanság, a design és a döntés? Megadtam-e az intervallum szintjét és a mintanagyságot? Megneveztem-e legalább három külső érvényességi korlátot? A döntési javaslatban szerepel-e a gyakorlati küszöb, és különbözik-e a „szignifikáns” állítástól?
A kísérleti jelentés négy, egymástól elkülönített részből áll. A becslés a mintából számolt aránykülönbség, például 10,0 százalékpont; a bizonytalanság a standard hibából és a 95%-os konfidenciaintervallumból áll, amelynek gyakorisági értelmezését pontosan meg kell tartani. A design szakasz a külső érvényesség korlátjait nevezi meg: populáció, időablak, megvalósítás és megfigyelési hossz. A döntési javaslat a minimális gyakorlati hatáshoz köti a becslést, ezért a „további adat” éppúgy érvényes eredmény, mint a bevezetés vagy az elutasítás. A következő alkalmazásban ezt a jelentést egy teljes kísérleti becslési és érzékenységi munkamenetté bővítjük a Becslés, intervallum és érzékenység záróprojekt-feladatában.