Záró audit: az elfogadási jegyzőkönyv mutassa be a split és a rendelkezésre állási idő ellenőrzését, az adatminőségi hibapályát, a környezetből újrafuttatást, az előzetes statisztikai tervhez való megfelelést és az eredménykorlátok közlését. A sikeres futtatás önmagában nem helyettesíti a módszertani auditot.
Az X. ellenőrző pont azt méri, hogy a könyv tíz állomásán tanult munkamenetet egyetlen, új feladaton is önállóan végig tudod vinni: a kérdés megfogalmazásától a döntési jelentésig. A korábbi Tudományos jelentés és reprodukálható bemutató tudományos közlésre, Átadás és új környezetes ismétlés átadásra, IX. ellenőrző pont: működő rendszer és bizonyítékai működő rendszerre, VII. ellenőrző pont: szakosodási döntés kontrollal módszerválasztásra készített fel; itt ezeknek együtt kell működniük.
A példafeladat egy teljesen új, fiktív szintetikus kísérlet: a MicroGrove nevű képzeletbeli bolt 800 véletlenszerűen kiválasztott ügyfele közül 400-400 kap kontrol-, illetve treatment-állapotot. A treatment egy e-mailes kupon, a kontrol egy semleges hírlevél. A döntési kérdés: növeli-e a kupon a rendeléskor megfigyelhető 7 napos konverziót (conversion_7d)? Az adatok nem valós ügyfelektől származnak; a generátor kódja a lentiekben található.
Bemenetek, megfigyelési egység és eredmény. A labor
a lab/experiments/microgrove/ könyvtárban él:
| Fájl | Szemcsézettség | Tartalom |
|---|---|---|
assignments.csv |
egy randomizált ügyfél | customer_id, variant,
assigned_at |
outcomes.csv |
ugyanaz az ügyfél | conversion_7d, revenue_7d_minor,
currency, observed_until |
manifest.json |
fájlszintű | seed, elemszám, CSV SHA-256 lenyomatok |
result.json |
kísérleti szintű | becslés, CI, p-érték |
Minden randomizált ügyfél teljes 7 napos ablakon megfigyelt:
observed_until = 2025-11-10T00:00:00+00:00, cenzúra nincs.
A primer végpont a conversion_7d, értéke 0 vagy 1. Az
intention-to-treat (ITT) nevező mind a 800 randomizált
ügyfél, akkor is, ha valaki nem nyitja meg az e-mailt. A
revenue_7d_minor egész EUR-cent; pénznemkonverzió
nincs.
Induló becslés a kód futtatása előtt. Tegyük fel, hogy korábbi kampányok alapján a kontrolkonverzió , a kezelés hatására pedig várható. mellett a Wald-féle standard error tervezett értéke
Így a 95%-os konfidenciaintervallum várható szélessége , a pontnyi hatás mellett a tesztstatisztika várhatóan körül lesz. Ez a kézi kontroll lesz az összehasonlítási alapunk; a tényleges minta a véletlen miatt ettől eltér.
Az ellenőrző pont nem új módszert vezet be, hanem a tanult fogalmakat rendezi hét fázisba. Mindegyik fázisnak van egy konkrét, ellenőrizhető kimenete.
ahol valószínűségek, csoportméretek, aránykülönbség, szintén -ben. A Wald-standard error a fenti képlet, a 95%-os intervallum
ahol
.
A p-érték a nullmodell szerinti feltételes szélsőség: ha
,
mekkora valószínűséggel kapnánk legalább ekkora abszolút eltérést? A
p-érték nem annak a valószínűsége, hogy nincs hatás. 3.
Modeling — a statisztikai modell. Itt nem gépi tanulási
modell kell, hanem egy kétmintás Bernoulli-modell:
,
ahol
csoport,
ügyfél. A modell a csoportátlagok különbségét becsüli. A bootstrap ezt a
modellt lazítja: nem követeli meg a normális közelítést, de megtartja a
független ügyfélszintű mintavételt. 4. Code — reprodukálható
kód. A seed rögzített, a fájlok hash-manifesttel védettek, a
kód a projektgyökérből uv run paranccsal futtatható. Az
analysis.py nem kér hálózatot, és a bootstrap random state
rögzítésével determinisztikus kimenetet ad. 5. Runtime —
végrehajtási közeg. A feladat CPU-s, 800 sorral és 9999
bootstrap-újramintázással kicsi. A tényleges futási időt a tanuló gépén
kell mérni; ebben a szövegben nem állítunk mért másodpercet. 6.
Communication — döntési jelentés. A kimenet nem csak
egy p-érték: pontbecslés, mindkét CI, hatásméret, döntési küszöb és a
korlátok is bekerülnek a jelentésbe. 7. Learning plan —
továbbfejlődési célok. Az ellenőrző pont végén a portfólió
alapján három konkrét fejlődési célt kell kijelölni. A céloknak
mérhetőnek kell lenniük: például „a következő héten egy saját
szintetikus A/B-feladatot a Bootstrap a megfelelő
mintavételi egységgel bootstrap-egységének helyes megnevezésével
oldok meg”.
A hét fázisból a statisztikai fázis hordozza a legnagyobb értelmezési kockázatot. A konfidenciaintervallum a paraméter bizonytalanságát fejezi ki; az új ügyfél kimenetére vonatkozó predikciós intervallum más, ahogy a credible interval értelmezése is. A MicroGrove-elemzésben végig paraméter-intervallumról beszélünk.
Hozzuk létre a laborfájlokat. Első a szintetikus generátor; a seed
20270213 szándékosan más, mint a Northstar
20261020 seedje. A generátorban a lognormális kosár
paraméterezésekor a
összefüggést használjuk, ezért
,
ahol
a megcélzott kosárátlag centben.
lab/experiments/microgrove/generate_microgrove.py:
import hashlib
import json
from pathlib import Path
import numpy as np
import pandas as pd
BASE = Path(__file__).resolve().parent
SEED = 20270213
N = 800
ASSIGNED_AT = "2025-11-03T09:00:00+00:00"
OBSERVED_UNTIL = "2025-11-10T00:00:00+00:00"
rng = np.random.default_rng(SEED)
ids = [f"mg-{i:04d}" for i in range(N)]
treatment = np.zeros(N, dtype=int)
treatment[N // 2:] = 1
rng.shuffle(treatment)
p_control = 0.20
p_treatment = 0.28
p = np.where(treatment == 1, p_treatment, p_control)
conversion = rng.binomial(1, p).astype(int)
target_basket = np.where(treatment == 1, 7200, 6400)
sigma = 0.45
mu = np.log(target_basket) - 0.5 * sigma**2
basket = np.round(rng.lognormal(mean=mu, sigma=sigma)).astype(int)
basket = np.where(conversion == 1, basket, 0)
assignments = pd.DataFrame(
{
"customer_id": ids,
"variant": np.where(treatment == 1, "treatment", "control"),
"assigned_at": ASSIGNED_AT,
}
)
outcomes = pd.DataFrame(
{
"customer_id": ids,
"conversion_7d": conversion,
"revenue_7d_minor": basket,
"currency": "EUR",
"observed_until": OBSERVED_UNTIL,
}
)
assignments.to_csv(BASE / "assignments.csv", index=False)
outcomes.to_csv(BASE / "outcomes.csv", index=False)
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
manifest = {
"seed": SEED,
"n": N,
"files": {
"assignments.csv": sha256(BASE / "assignments.csv"),
"outcomes.csv": sha256(BASE / "outcomes.csv"),
},
}
(BASE / "manifest.json").write_text(json.dumps(manifest, indent=2))
print("microgrove data generated")Második az elemzés. A verify_manifest a CSV-k
integritását ellenőrzi, mielőtt bármilyen becslést számolnánk. A
bootstrap az ügyfél szintjén újramintáz, nem a sorok
tetszőleges keverésével; ez a független mintavételi egység.
lab/experiments/microgrove/analysis.py:
import hashlib
import json
from pathlib import Path
import numpy as np
import pandas as pd
from scipy import stats
BASE = Path(__file__).resolve().parent
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def verify_manifest(base: Path) -> None:
manifest = json.loads((base / "manifest.json").read_text())
for name, expected in manifest["files"].items():
actual = sha256(base / name)
if actual != expected:
raise RuntimeError(f"hash mismatch: {name}")
print("manifest ok")
def diff_mean(t: np.ndarray, c: np.ndarray) -> float:
return float(np.mean(t) - np.mean(c))
def main() -> None:
verify_manifest(BASE)
assignments = pd.read_csv(
BASE / "assignments.csv",
dtype={"customer_id": "string", "variant": "string"},
)
outcomes = pd.read_csv(
BASE / "outcomes.csv",
dtype={"customer_id": "string", "conversion_7d": "int64",
"revenue_7d_minor": "int64"},
)
df = assignments.merge(
outcomes, on="customer_id", validate="one_to_one"
)
if len(df) != len(assignments):
raise ValueError("missing or duplicate outcome row")
if not df["conversion_7d"].isin([0, 1]).all():
raise ValueError("conversion_7d must be 0 or 1")
y = df["conversion_7d"].to_numpy(dtype=float)
treat = (df["variant"].to_numpy() == "treatment")
y_t, y_c = y[treat], y[~treat]
n_t, n_c = y_t.size, y_c.size
p_t, p_c = y_t.mean(), y_c.mean()
tau = p_t - p_c
se = np.sqrt(p_t * (1 - p_t) / n_t + p_c * (1 - p_c) / n_c)
ci_wald = tau + np.array([-1.0, 1.0]) * stats.norm.ppf(0.975) * se
z = tau / se
p_value = 2 * (1 - stats.norm.cdf(abs(z)))
boot = stats.bootstrap(
(y_t, y_c),
statistic=diff_mean,
paired=False,
n_resamples=9999,
random_state=20270213,
method="BCa",
confidence_level=0.95,
)
result = {
"n_treatment": int(n_t),
"n_control": int(n_c),
"p_treatment": float(p_t),
"p_control": float(p_c),
"tau_hat": float(tau),
"se": float(se),
"ci_95_wald": [float(x) for x in ci_wald],
"z": float(z),
"p_value_two_sided": float(p_value),
"ci_95_bca": [float(x) for x in boot.confidence_interval],
}
(BASE / "result.json").write_text(json.dumps(result, indent=2))
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()Futtatás a projektgyökérből, Linux/macOS terminálon; Windows PowerShellben ugyanez a két sor használható:
uv run python lab/experiments/microgrove/generate_microgrove.py
uv run python lab/experiments/microgrove/analysis.pyA Python 3.12, NumPy 2.x, pandas 2.2.x és SciPy 1.15 körüli környezet a cél; a pontos feloldást a saját uv lock rögzíti. A kód nem igényel hálózatot, GPU-t vagy külső szolgáltatást.
Az alábbi összefoglaló szemléltető, az elméleti paraméterekből levezetett várt érték, nem ennek a könyvgenerátornak a futtatási jegyzéke. A tanuló gépén a seed miatt kis mértékben más számok jelennek meg:
manifest ok
n_treatment=400, n_control=400
p_treatment≈0.28, p_control≈0.20
tau_hat≈0.08
se≈0.030
wald CI≈[0.021, 0.139]
bca CI hasonló, kis aszimmetriával
z≈2.66, p≈0.008
Az ellenőrző invariantok: a beolvasott df shape
(800, 7); a customer_id string dtype, a
variant string, a conversion_7d és
revenue_7d_minor egész; a merge
validate="one_to_one" sikeres, tehát nincs hiányzó vagy
duplikált kimenet. A tau_hat aránykülönbség, pp-re váltva
100-szoros. A p-érték helyes feltételes mondata: ha a valódi hatás
nulla lenne, a megfigyelt vagy szélsőségesebb különbség valószínűsége
körülbelül 0.008 a nullmodell szerint.
A döntési értelmezés a küszöbbel együtt történik. Az 5 pp bevezetési küszöb mellett a szemléltető CI alsó határa körülbelül 2.1 pp, tehát a minta nem erősíti meg, hogy a hatás biztosan eléri a küszöböt; a pontbecslés igen, de a bizonytalanság nagy. Ha a küszöb 2 pp lenne, akkor a 2.1 pp alsó határ éppen elegendő. Ez a különbségtétel a tudományos jelentés magja: a szignifikancia a nullával, a döntés a küszöbbel hasonlít össze.
scipy.stats.bootstrap
hívásában a data=(y_t, y_c) tömbök ügyfélszintű 0/1
értékeket tartalmaznak. Magyarázd el, miért helyes az ügyfél a
resampling egység, és miért lenne hibás, ha a bootstrapet például a
tranzakciós revenue_7d_minor sorokból képzett hosszabb
tömbön futtatnánk olyan adatbázisból, ahol egy ügyfélnek több sora is
lehet.analysis.py
kódot a revenue_7d_minor ITT-elemzésével: csoportátlagok
különbsége, standard error, bootstrap percentilis CI. A kimeneti
JSON-ban revenue_tau_hat, revenue_ci_95_bca
kulcsok alatt szerepeljen az eredmény. Ellenőrzésként írd ki a nem
konvertálók arányát csoportonként.p_value = 2 * (1 - stats.norm.cdf(z)), ahol z
az előjeles tesztstatisztika. Adj olyan szituációt, amikor ez hibás
eredményt ad, és magyarázd meg a javítást.1. feladat. A randomizálás egysége az ügyfél: a
treatment sorsolás ügyfélenként független, ezért a becslés
bizonytalanságát az ügyfélszintű véletlen ingadozás határozza meg. Ha
egy ügyfél több tranzakciós sorral szerepelne, és a bootstrap sorokból
húzna, akkor egy sokvásárlós ügyfél többszörösen jelenne meg, a
függetlenség sérülne, a standard error pedig jellemzően alulbecsült
lenne. A MicroGrove outcomes.csv egy ügyfélhez pontosan egy
sort rendel, ezért a hiba itt nem lép fel; a feladat arra figyelmeztet,
hogy a resampling egységet a kísérleti tervből kell levezetni, nem a
fájl sorszámából.
2. feladat. Az outcomes.csv
revenue_7d_minor értékei erősen ferde eloszlást követnek: a
nem konvertálók értéke 0, a konvertálók kosara lognormális. A
normálközelítés itt gyenge lehet, ezért a bootstrap indokolt. Egy
lehetséges bővítés:
rev = df["revenue_7d_minor"].to_numpy(dtype=float)
rev_t, rev_c = rev[treat], rev[~treat]
rev_tau = rev_t.mean() - rev_c.mean()
rev_boot = stats.bootstrap(
(rev_t, rev_c),
statistic=diff_mean,
paired=False,
n_resamples=9999,
random_state=20270213,
method="BCa",
)
result["revenue_tau_hat"] = float(rev_tau)
result["revenue_ci_95_bca"] = [
float(x) for x in rev_boot.confidence_interval
]Ellenőrző invariant: a groupby-átlagok újraszámolva megegyeznek a
diff_mean kimenetével 1 centen belül; a nem konvertálók
aránya a mintabeli 1 - p_t és 1 - p_c, ami a
szemléltető paraméterekkel 0.72 és 0.80 körül várható.
3. feladat. Ha a mintából számított hatás negatív,
például z = -2.0, akkor a hibás kifejezés
2 * (1 - cdf(-2.0)) = 2 * (1 - 0.0228) = 1.9544-et adna,
ami p-értékként lehetetlen. A kétoldali p-értékhez az abszolút eltérést
kell nézni: 2 * (1 - stats.norm.cdf(abs(z))). A javítás
után z = -2.0 esetén a p-érték körülbelül 0.0455. Az
ellenőrzés módja: mesterségesen előjelet váltva a p-értéknek
változatlannak kell maradnia.
4. feladat. Ha a felajánlott kezelés csak 90%-ban jut el, és a hatás homogén, akkor az ITT-becslés várható értéke , ahol a ténylegesen kezeltek átlagos hatása. A 0.08 pontos tervezett hatás így körülbelül 0.072 pontra hígul. A protokoll szerinti ITT továbbra is a randomizálásból következő torzítatlan becslés marad, de a döntési jelentésben érzékenységvizsgálatot kell közölni: adjunk felső becslést a ténylegesen kezeltek hatására a felajánlás/elérés aránnyal osztva, és jelezzük, hogy ez erős homogenitási feltétel mellett érvényes. A csoportméret növelése nem pótolja ezt az elemzést.
A leggyakoribb tévedés ezen az ellenőrző ponton az, hogy a tanuló a könyv elvégzését vagy egy sikeres demót munkaköri minősítésként kezeli. A tanúsítvány csak a tanulási utat igazolja; a tényleges teljesítményt az új feladaton mutatott önálló következtetés bizonyítja. Két jelölt közül az egyik hibátlan portfóliót mutat, de a MicroGrove p-értékét így közli: „3% az esélye, hogy nincs hatás”; a másik nem végzett el minden opcionális modult, de a p-értéket feltételes állításként fogalmazza, és a CI alsó határát a döntési küszöbhöz méri. Az utóbbi teljesítménye az erősebb.
Egy másik csapda a korábbi Northstar-kódból való kritikátlan másolás: a prediktív projekt időbeli splitje nem alkalmazható egy randomizált kísérletre. A kísérletben nincs train/test split; a randomizálás és a teljes megfigyelés adja az érvényességet. Visszautalva Tanítás, validáció és érintetlen teszt alfejezetre: a split a predikciós szivárgást, a randomizálás a confoundingot kezeli; a kettő nem felcserélhető eszköz.
A leadás előtt ellenőrizd ezt a listát:
Az X. ellenőrző pont azt igazolja, hogy egy új szintetikus kísérleten a kérdéstől a döntési jelentésig önállóan végig tudod vinni a data scientist munkamenetet. A MicroGrove-példa ügyfélszintű randomizálást, teljes megfigyelésű 7 napos konverziót, ITT-becslést, Wald- és bootstrap konfidenciaintervallumot, p-értéket, manifest-hash ellenőrzést, döntési küszöböt és érzékenységvizsgálatot kapcsolt egybe. A teljesítményt nem a könyv elvégzése, hanem az új feladaton mutatott korrekt következtetés bizonyítja. Következő alkalmazásként válassz a saját portfóliódból három konkrét fejlődési célt, és egy újabb, saját tervezésű szintetikus esettanulmányon mérd meg mindegyiket.
A felsorolt tételek válogatott, visszakereshető kiinduló források, nem a könyv minden állításának tételes hivatkozásai. A szoftverfüggvények pontos viselkedését mindig a példaprojektben rögzített verzióval együtt kell ellenőrizni; az itt megadott dokumentációk „stable” ága idővel változhat.
[ISLR] James et al.: An Introduction to Statistical Learning (2021), https://www.statlearning.com/ (hozzáférés: 2026-10-21).
[Python] Python Packaging User Guide, https://packaging.python.org/ (hozzáférés: 2026-10-21).
[Pandas] pandas User Guide, https://pandas.pydata.org/docs/user_guide/ (hozzáférés: 2026-10-21).
[DuckDB] DuckDB SQL Documentation, https://duckdb.org/docs/stable/sql/introduction (hozzáférés: 2026-10-21).
[NumPy] NumPy Linear Algebra, https://numpy.org/doc/stable/reference/routines.linalg.html (hozzáférés: 2026-10-21).
[SciPy] scipy.stats Reference, https://docs.scipy.org/doc/scipy/reference/stats.html (hozzáférés: 2026-10-21).
[ASA] Wasserstein & Lazar (2016): The ASA Statement on p-Values, DOI: https://doi.org/10.1080/00031305.2016.1154108.
[Hernán] Hernán & Robins: Causal Inference: What If, https://www.hsph.harvard.edu/miguel-hernan/causal-inference-book/ (hozzáférés: 2026-10-21).
[PyMC] PyMC Documentation, https://www.pymc.io/projects/docs/en/stable/ (hozzáférés: 2026-10-21).
[Sklearn] scikit-learn User Guide, https://scikit-learn.org/stable/user_guide.html (hozzáférés: 2026-10-21).
[SklearnHGB] scikit-learn: Gradient Boosting and Categorical Features, https://scikit-learn.org/stable/modules/ensemble.html (hozzáférés: 2026-10-21).
[SklearnMetrics] scikit-learn Model Evaluation, https://scikit-learn.org/stable/modules/model_evaluation.html (hozzáférés: 2026-10-21).
[SklearnCalibration] scikit-learn Calibration, https://scikit-learn.org/stable/modules/calibration.html (hozzáférés: 2026-10-21).
[PyTorch] PyTorch Autograd Tutorial, https://docs.pytorch.org/tutorials/beginner/basics/autogradqs_tutorial.html (hozzáférés: 2026-10-21).
[FastAPI] FastAPI Documentation, https://fastapi.tiangolo.com/ (hozzáférés: 2026-10-21).
[R] R Manuals, https://cran.r-project.org/manuals.html (hozzáférés: 2026-10-21).