IlmHamroh
Data Science va sun'iy intellekt/Gipoteza testlari10/10-dars22 daqiqa
Mundarija (22)

11.10-dars: Amaliyot — A/B test loyihasi

11-QISM — GIPOTEZA TESTLARI · 10-dars


1. Kirish va motivatsiya

11-qismda testlar tizimini 11.1-bob, quvvat va namuna hajmini 11.2-bob, t-testlarni 11.3-bob, proporsiyalarni 11.4-bob, xi-kvadratni 11.5-bob, ANOVA ni 11.6-bob, noparametrik testlarni 11.7-bob, bootstrap va permutatsiyani 11.8-bob hamda ko'p taqqoslashni 11.9-bob o'rgandik. Endi hammasini bitta A/B testda birlashtiramiz — boshidan oxirigacha: loyihalash → sanity-check → asosiy metrika → ikkilamchi metrikalar → segmentlar → hisobot.

A/B test — Data Science'dagi eng ko'p uchraydigan sababiy tahlil vositasi 4.10-bob: tasodifiy taqsimot tufayli farqni sababiy talqin qilish mumkin. Lekin uni buzish ham oson: quvvat yetmasligi, noto'g'ri birlik, erta to'xtatish, ko'p metrikani tuzatishsiz tekshirish, segmentlarni "qidirish". Bu dars — to'g'ri ish tartibining namunasi.

Real vaziyat. Mahsulot jamoasi yangi mahsulot sahifasini sinaydi. Data Scientist testni oldindan loyihalaydi: asosiy metrika — buyurtma konversiyasi (hozir 5.0%), amaliy chegara — nisbiy +12% (5.6% ga), quvvat 80%, alfa 0.05 → har guruhga ~21 900 foydalanuvchi, kunlik trafik 4 000 → 11 kun, to'liq haftalarga yaxlitlab 12 kun. Ikkilamchi metrikalar (o'rtacha chek, sahifada qolish vaqti, savatga qo'shish) va sath metrikalari (qaytish, xatolar) oldindan ro'yxatga olinadi. Test tugagach: A/A sanity-check, asosiy metrika (z-test + CI), ikkilamchilar (Holm), segmentlar (oldindan belgilangan 4 ta), yakuniy hisobot.

Bu darsda 11-qismning barcha bilimlarini bitta loyihada qo'llaymiz.

Bu darsda:

  • Testni loyihalash (metrika, MDE, n, davomiylik)
  • Sanity-check (taqsimot, A/A, SRM)
  • Asosiy metrika tahlili
  • Ikkilamchi metrikalar va tuzatish
  • Segment tahlili (oldindan belgilangan)
  • Qiyshiq metrikalar (bootstrap)
  • Erta to'xtatish va tuzoqlar
  • Yakuniy hisobot va qaror

ℹ Misollar real numpy/scipy/statsmodels bilan (Python 3.14). Barcha misollar bir xil yarat() generatoridan foydalanadi.


2. Nazariya — chuqur tushuntirish

2.1. Testni loyihalash

text
1. GIPOTEZA 8.8-bob: "Yangi sahifa mahsulot afzalliklarini aniqroq ko'rsatadi →
                    buyurtma konversiyasi oshadi"
2. ASOSIY METRIKA: buyurtma konversiyasi (bitta!)
3. AMALIY CHEGARA: nisbiy +12% (5.0% → 5.6%)
4. QUVVAT: 80%, alfa = 0.05 (ikki tomonlama)
5. NAMUNA: har guruhga ~21 900 → 11 kun (12 kunga yaxlitlanadi)
6. IKKILAMCHI metrikalar va SATH (guardrail) metrikalari — oldindan ro'yxat
7. TO'XTASH QOIDASI: belgilangan muddat; oraliq tekshiruvlar faqat sath uchun

Testning muvaffaqiyati oldindan hal qilinadi: bitta asosiy metrika 11.9-bob, amaliy chegara 11.1-bob, quvvat va namuna hajmi 11.2-bob, davomiylik (to'liq haftalar — 8.7 dagi hafta kuni effekti), to'xtash qoidasi (11.2 — ketma-ket tekshirish taqiqlanadi). Bularning hammasi hujjat sifatida yoziladi (8.8 — pre-registration).

2.2. Sanity-check

text
1. SRM (Sample Ratio Mismatch): guruhlar nisbati kutilganday (50/50) mi?
   xi-kvadrat moslik testi 11.5-bob; p < 0.001 → texnik muammo, tahlilni to'xtating

2. A/A tekshiruvi: testdan oldingi davrda guruhlar farq qilmasligi kerak

3. Ma'lumot sifati: dublikatlar, yo'qolgan yozuvlar, bir foydalanuvchi ikki guruhda

4. Taqsimotlar: metrikaning shakli (qiyshiqmi?), outlierlar (8.6)

Sanity-check — tahlilning birinchi qadami: agar guruhlarga taqsimot buzilgan bo'lsa (SRM), barcha keyingi natijalar ishonchsiz. SRM ning odatiy sabablari: bot trafigi, kesh, yuklanish xatolari, noto'g'ri yorliqlash. Bir foydalanuvchi ikki guruhda — birlik muammosi 11.3-bob. Bu bosqichda testni bekor qilish — yomon xabar, lekin noto'g'ri qarordan yaxshi.

2.3. Asosiy metrika tahlili

text
Konversiya — ulush 11.4-bob:
  farq (foiz punkt) + nisbiy farq + 95% CI + p (z-test)
  amaliy chegara bilan solishtirish

Qaror matritsasi:
  CI butunlay chegaradan yuqori     → ishga tushirish
  CI nolni o'z ichiga oladi          → dalil yetarli emas
  CI butunlay manfiy                 → rad etish
  CI keng (noaniq)                   → ma'lumot yetarli emas / davom ettirish

Qaror faqat p ga emas, CI va amaliy chegaraga tayanadi 11.1-bob: "p < 0.05" — statistik signal; "CI ning pastki chegarasi amaliy chegaradan yuqori" — biznes qarori. Bu yondashuv "muhim, lekin arzimas" va "muhim emas, lekin istiqbolli" holatlarini to'g'ri ajratadi.

2.4. Ikkilamchi metrikalar va tuzatish

Ikkilamchi metrikalar (chek, vaqt, savatga qo'shish) — tuzatish bilan (Holm, 11.9); sath metrikalari (xatolar, qaytish, shikoyatlar) — yomonlashuvni aniqlash uchun (bu yerda odatda tuzatilmaydi, chunki maqsad — xavfni o'tkazib yubormaslik; aksincha, sezgirroq alfa qo'yiladi). Barcha tekshirilgan metrikalar hisobotda ko'rsatiladi — nechtasi va qanday tuzatish bilan.

2.5. Segment tahlili

Segmentlar (qurilma, yangi/qaytgan, mintaqa) oldindan belgilanadi — 20 ta segmentni "qidirish" yolg'on kashfiyot mashinasi 11.9-bob. Segment tahlili ikki maqsadda: (1) zarar tekshiruvi — biror segmentda keskin yomonlashuv bormi; (2) gipoteza — effekt qayerda kuchliroq (keyingi test uchun). Segment natijalari hech qachon yakuniy qaror uchun yagona asos bo'lmaydi (8.8 — kashfiyot ≠ tasdiq).

2.6. Qiyshiq metrikalar

Pul va vaqt metrikalari kuchli qiyshiq (9.8, 11.7): o'rtacha chek uchun t-test noto'g'ri ishlashi mumkin. Yechimlar: bootstrap CI (11.8 — o'rtacha uchun ham, median uchun ham), log o'zgartirish, noparametrik test 11.7-bob, yoki winsorizatsiya (yuqori 0.5% ni kesish — oldindan e'lon qilingan qoida bilan). Muhim: o'rtacha biznes uchun muhim bo'lsa (umumiy daromad), medianga o'tib ketmang — bootstrap ishlating. Texnik nuans: scipy.stats.bootstrap ning standart BCa usuli katta namunada (o'n minglab qator) jackknife tufayli juda ko'p xotira talab qiladi — bunday holatda method="percentile" ishlating.

2.7. Erta to'xtatish va tuzoqlar

Asosiy tuzoqlar: erta to'xtatish ("p < 0.05 bo'ldi — to'xtatamiz" — I tur xato 2-4 barobar oshadi, 11.2); noto'g'ri birlik (sessiya emas, foydalanuvchi — 11.3); SRM ni tekshirmaslik; ko'p metrikani tuzatishsiz 11.9-bob; segment qidirish; novelty effekti (yangilik ta'siri — birinchi kunlar farqi keyin yo'qoladi); bayram/aksiya davri; test guruhlari orasidagi ta'sir (ijtimoiy tarmoqlarda, marketplace'da — SUTVA buzilishi); faqat g'alaba qidirish (neytral natija ham qimmatli).

2.8. A/B test — sababiy xulosaning amaliyoti

To'liq sikl: loyihalash (gipoteza, bitta asosiy metrika, amaliy chegara, quvvat va n, davomiylik, to'xtash qoidasi — 8.8, 11.1, 11.2) → sanity-check (SRM, A/A, ma'lumot sifati, taqsimotlar — 11.5, 8.6) → asosiy metrika (farq + CI + p, amaliy chegara bilan qaror — 11.4) → ikkilamchilar (tuzatish — 11.9) va sath metrikalari → segmentlar (oldindan, gipoteza sifatida) → qiyshiq metrikalar (bootstrap — 11.8) → hisobot (8.9: xulosa birinchi, cheklovlar, keyingi qadam). Bu — sanoat standarti ish tartibi.


3. Tez ma'lumotnoma

python
import numpy as np
from scipy import stats
from statsmodels.stats.proportion import proportions_ztest, confint_proportions_2indep
from statsmodels.stats.multitest import multipletests
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize

# 1) LOYIHALASH
h = proportion_effectsize(0.054, 0.05)
n = NormalIndPower().solve_power(effect_size=h, alpha=0.05, power=0.8)

# 2) SANITY: SRM
stats.chisquare([n_a, n_b])                       # p < 0.001 → muammo

# 3) ASOSIY METRIKA
stat, p = proportions_ztest([x_b, x_a], [n_b, n_a])
lo, hi = confint_proportions_2indep(x_b, n_b, x_a, n_a, method="wald")

# 4) IKKILAMCHI (tuzatish)
rad, p_tuz, _, _ = multipletests(p_lar, alpha=0.05, method="holm")

# 5) QIYSHIQ METRIKA (bootstrap)
stats.bootstrap((chek_b, chek_a), lambda u, v, axis=-1: u.mean(axis) - v.mean(axis),
                vectorized=True, n_resamples=10_000,
                method="percentile", random_state=0)   # katta n da BCa qimmat
QOIDA: oldindan reja · SRM · asosiy metrika bitta · tuzatish · CI bilan qaror

A/B loyiha xulosasi

Loyihalash: gipoteza, asosiy metrika, amaliy chegara, quvvat, n, muddat
Sanity: SRM (chisquare), A/A, dublikat, taqsimot
Asosiy: farq (pp va nisbiy) + CI + p; chegara bilan qaror
Ikkilamchi: Holm; sath metrikalari alohida
Segment: oldindan, gipoteza sifatida
Qiyshiq metrika: bootstrap CI
Hisobot: xulosa birinchi, cheklovlar, keyingi qadam

4. Batafsil misollar

Misollar real numpy/scipy/statsmodels bilan (Python 3.14). Har misol boshida bir xil yarat() generatori.

Misol 1 — Loyihalash va sanity-check

python
"""A/B loyiha: namuna hajmi, davomiylik va sanity-check (real numpy/scipy/statsmodels)."""

import numpy as np
from scipy import stats
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize


def yarat(seed: int = 7):
    """Test ma'lumoti: 24 000 + 24 000 foydalanuvchi, 12 kun."""
    rng = np.random.default_rng(seed)
    n = 24_000
    guruh = np.repeat(["A", "B"], n)
    kun = rng.integers(0, 12, 2 * n)
    qurilma = rng.choice(["mobil", "kompyuter"], 2 * n, p=[0.7, 0.3])
    yangi = rng.random(2 * n) < 0.4
    p_baza = np.where(kun % 7 >= 5, 0.062, 0.050)          # dam olish kunlari yuqoriroq
    p = np.where(guruh == "B", p_baza * 1.14, p_baza)      # haqiqiy effekt: +14% nisbiy
    konversiya = rng.random(2 * n) < p
    chek = np.where(konversiya, rng.lognormal(np.log(180_000), 0.7, 2 * n), 0.0)
    vaqt = rng.lognormal(np.log(95), 0.5, 2 * n) * np.where(guruh == "B", 1.02, 1.0)
    qaytish = rng.random(2 * n) < 0.021
    return {"guruh": guruh, "kun": kun, "qurilma": qurilma, "yangi": yangi,
            "konversiya": konversiya, "chek": chek, "vaqt": vaqt, "qaytish": qaytish}


def main() -> None:
    print("=== 1. Loyihalash ===")
    baza, maqsad = 0.05, 0.056
    h = proportion_effectsize(maqsad, baza)
    n = NormalIndPower().solve_power(effect_size=h, alpha=0.05, power=0.8)
    print(f"  baza {baza:.1%}, amaliy chegara +12% nisbiy ({maqsad:.1%})")
    print(f"  har guruhga n = {np.ceil(n):,.0f}; kunlik trafik 4000 → "
          f"{2 * np.ceil(n) / 4000:.1f} kun (12 kunga yaxlitlanadi)")

    d = yarat()
    print("\n=== 2. SRM (guruhlar nisbati) ===")
    nA = (d["guruh"] == "A").sum(); nB = (d["guruh"] == "B").sum()
    res = stats.chisquare([nA, nB])
    print(f"  A: {nA:,}, B: {nB:,}, p = {res.pvalue:.3f} → "
          f"{'muammo!' if res.pvalue < 0.001 else 'normal'}")

    print("\n=== 3. Ma'lumot sifati ===")
    print(f"  yozuvlar: {len(d['guruh']):,}, konversiyalar: {d['konversiya'].sum():,}")
    print(f"  chek > 0 bo'lganlar soni konversiyaga teng: "
          f"{(d['chek'] > 0).sum() == d['konversiya'].sum()}")

    print("\n=== 4. Metrika taqsimoti (chek, konversiya qilganlar) ===")
    chek = d["chek"][d["konversiya"]]
    print(f"  o'rtacha {chek.mean():,.0f}, median {np.median(chek):,.0f}, "
          f"skew {stats.skew(chek):.2f} → qiyshiq (bootstrap kerak)")
    print("  ⭐ Sanity-check — tahlilning birinchi qadami")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Loyihalash ===
  baza 5.0%, amaliy chegara +12% nisbiy (5.6%)
  har guruhga n = 21,869; kunlik trafik 4000 → 10.9 kun (12 kunga yaxlitlanadi)

=== 2. SRM (guruhlar nisbati) ===
  A: 24,000, B: 24,000, p = 1.000 → normal

=== 3. Ma'lumot sifati ===
  yozuvlar: 48,000, konversiyalar: 2,717
  chek > 0 bo'lganlar soni konversiyaga teng: True

=== 4. Metrika taqsimoti (chek, konversiya qilganlar) ===
  o'rtacha 225,325, median 178,436, skew 2.80 → qiyshiq (bootstrap kerak)
  ⭐ Sanity-check — tahlilning birinchi qadami

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — Asosiy metrika va qaror

python
"""Asosiy metrika: farq, CI, p va amaliy chegara bilan qaror (real numpy/scipy/statsmodels)."""

import numpy as np
from scipy import stats
from statsmodels.stats.proportion import confint_proportions_2indep, proportions_ztest


def yarat(seed: int = 7):
    """Test ma'lumoti: 24 000 + 24 000 foydalanuvchi, 12 kun."""
    rng = np.random.default_rng(seed)
    n = 24_000
    guruh = np.repeat(["A", "B"], n)
    kun = rng.integers(0, 12, 2 * n)
    qurilma = rng.choice(["mobil", "kompyuter"], 2 * n, p=[0.7, 0.3])
    yangi = rng.random(2 * n) < 0.4
    p_baza = np.where(kun % 7 >= 5, 0.062, 0.050)          # dam olish kunlari yuqoriroq
    p = np.where(guruh == "B", p_baza * 1.14, p_baza)      # haqiqiy effekt: +14% nisbiy
    konversiya = rng.random(2 * n) < p
    chek = np.where(konversiya, rng.lognormal(np.log(180_000), 0.7, 2 * n), 0.0)
    vaqt = rng.lognormal(np.log(95), 0.5, 2 * n) * np.where(guruh == "B", 1.02, 1.0)
    qaytish = rng.random(2 * n) < 0.021
    return {"guruh": guruh, "kun": kun, "qurilma": qurilma, "yangi": yangi,
            "konversiya": konversiya, "chek": chek, "vaqt": vaqt, "qaytish": qaytish}


def main() -> None:
    d = yarat()
    A = d["guruh"] == "A"
    nA, nB = A.sum(), (~A).sum()
    xA, xB = d["konversiya"][A].sum(), d["konversiya"][~A].sum()
    pA, pB = xA / nA, xB / nB

    print("=== 1. Kuzatilgan ===")
    print(f"  A: {pA:.3%} ({xA:,}/{nA:,}), B: {pB:.3%} ({xB:,}/{nB:,})")
    print(f"  absolyut farq {100 * (pB - pA):+.2f} pp, nisbiy {(pB - pA) / pA:+.1%}")

    print("\n=== 2. Test va CI ===")
    stat, p = proportions_ztest([xB, xA], [nB, nA])
    lo, hi = confint_proportions_2indep(xB, nB, xA, nA, method="wald")
    print(f"  z = {stat:.3f}, p = {p:.5f}")
    print(f"  95% CI: [{100 * lo:+.2f}, {100 * hi:+.2f}] pp")
    print(f"  nisbiy CI (taxminan): [{lo / pA:+.1%}, {hi / pA:+.1%}]")

    print("\n=== 3. Amaliy chegara bilan qaror ===")
    chegara_pp = 0.05 * 0.12                      # +12% nisbiy = +0.6 pp
    print(f"  amaliy chegara: +{100 * chegara_pp:.2f} pp")
    if lo > chegara_pp:
        qaror = "ISHGA TUSHIRISH (CI butunlay chegaradan yuqori)"
    elif hi < 0:
        qaror = "RAD ETISH (zarar)"
    elif lo > 0:
        qaror = "IJOBIY, lekin chegaraga yetmasligi mumkin — biznes qarori"
    else:
        qaror = "DALIL YETARLI EMAS"
    print(f"  qaror: {qaror}")

    print("\n=== 4. Kunlar bo'yicha barqarorlik ===")
    for k in [3, 6, 9, 12]:
        m = d["kun"] < k
        a = d["konversiya"][A & m]; b = d["konversiya"][(~A) & m]
        _, pk = proportions_ztest([b.sum(), a.sum()], [len(b), len(a)])
        print(f"  {k:>2} kun: A {a.mean():.3%}, B {b.mean():.3%}, p = {pk:.4f}")
    print("  ⭐ Qaror — CI va amaliy chegara bilan (faqat p emas)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Kuzatilgan ===
  A: 5.250% (1,260/24,000), B: 6.071% (1,457/24,000)
  absolyut farq +0.82 pp, nisbiy +15.6%

=== 2. Test va CI ===
  z = 3.891, p = 0.00010
  95% CI: [+0.41, +1.23] pp
  nisbiy CI (taxminan): [+7.8%, +23.5%]

=== 3. Amaliy chegara bilan qaror ===
  amaliy chegara: +0.60 pp
  qaror: IJOBIY, lekin chegaraga yetmasligi mumkin — biznes qarori

=== 4. Kunlar bo'yicha barqarorlik ===
   3 kun: A 4.961%, B 6.101%, p = 0.0063
   6 kun: A 5.101%, B 6.202%, p = 0.0002
   9 kun: A 5.276%, B 6.354%, p = 0.0000
  12 kun: A 5.250%, B 6.071%, p = 0.0001
  ⭐ Qaror — CI va amaliy chegara bilan (faqat p emas)

Nima ko'rsatdi: 2.3, 2.7-bo'limlar.

Misol 3 — Ikkilamchi metrikalar va bootstrap

python
"""Ikkilamchi metrikalar (Holm) va qiyshiq chek uchun bootstrap CI (real numpy/scipy/statsmodels)."""

import numpy as np
from scipy import stats
from statsmodels.stats.multitest import multipletests
from statsmodels.stats.proportion import proportions_ztest


def yarat(seed: int = 7):
    """Test ma'lumoti: 24 000 + 24 000 foydalanuvchi, 12 kun."""
    rng = np.random.default_rng(seed)
    n = 24_000
    guruh = np.repeat(["A", "B"], n)
    kun = rng.integers(0, 12, 2 * n)
    qurilma = rng.choice(["mobil", "kompyuter"], 2 * n, p=[0.7, 0.3])
    yangi = rng.random(2 * n) < 0.4
    p_baza = np.where(kun % 7 >= 5, 0.062, 0.050)          # dam olish kunlari yuqoriroq
    p = np.where(guruh == "B", p_baza * 1.14, p_baza)      # haqiqiy effekt: +14% nisbiy
    konversiya = rng.random(2 * n) < p
    chek = np.where(konversiya, rng.lognormal(np.log(180_000), 0.7, 2 * n), 0.0)
    vaqt = rng.lognormal(np.log(95), 0.5, 2 * n) * np.where(guruh == "B", 1.02, 1.0)
    qaytish = rng.random(2 * n) < 0.021
    return {"guruh": guruh, "kun": kun, "qurilma": qurilma, "yangi": yangi,
            "konversiya": konversiya, "chek": chek, "vaqt": vaqt, "qaytish": qaytish}


def main() -> None:
    d = yarat()
    A = d["guruh"] == "A"

    print("=== 1. Ikkilamchi metrikalar ===")
    p_lar, nomlar = [], []

    vaqt_res = stats.ttest_ind(d["vaqt"][~A], d["vaqt"][A], equal_var=False)
    p_lar.append(vaqt_res.pvalue); nomlar.append("sahifada vaqt")

    chek_a = d["chek"][A & d["konversiya"]]; chek_b = d["chek"][(~A) & d["konversiya"]]
    chek_res = stats.ttest_ind(chek_b, chek_a, equal_var=False)
    p_lar.append(chek_res.pvalue); nomlar.append("o'rtacha chek")

    _, p_qayt = proportions_ztest([d["qaytish"][~A].sum(), d["qaytish"][A].sum()],
                                  [(~A).sum(), A.sum()])
    p_lar.append(p_qayt); nomlar.append("qaytish (sath)")

    for nom, p in zip(nomlar, p_lar):
        print(f"  {nom:<16}: xom p = {p:.4f}")

    print("\n=== 2. Holm tuzatishi (sath metrikasisiz) ===")
    rad, p_tuz, _, _ = multipletests(p_lar[:2], alpha=0.05, method="holm")
    for nom, p, pt, r in zip(nomlar[:2], p_lar[:2], p_tuz, rad):
        print(f"  {nom:<16}: tuzatilgan p = {pt:.4f}{' ← muhim' if r else ''}")

    print("\n=== 3. O'rtacha chek: bootstrap CI (qiyshiq metrika) ===")
    res = stats.bootstrap((chek_b, chek_a),
                          lambda u, v, axis=-1: u.mean(axis=axis) - v.mean(axis=axis),
                          vectorized=True, n_resamples=5_000,
                          method="percentile", random_state=0)
    print(f"  o'rtacha chek: A {chek_a.mean():,.0f}, B {chek_b.mean():,.0f}")
    print(f"  farq CI: [{res.confidence_interval.low:,.0f}, "
          f"{res.confidence_interval.high:,.0f}] so'm")

    print("\n=== 4. Mijoz boshiga daromad (konversiya × chek) ===")
    rpu_a = d["chek"][A].mean(); rpu_b = d["chek"][~A].mean()
    res2 = stats.bootstrap((d["chek"][~A], d["chek"][A]),
                           lambda u, v, axis=-1: u.mean(axis=axis) - v.mean(axis=axis),
                           vectorized=True, n_resamples=5_000,
                           method="percentile", random_state=0)
    print(f"  A {rpu_a:,.0f}, B {rpu_b:,.0f}, farq CI "
          f"[{res2.confidence_interval.low:,.0f}, {res2.confidence_interval.high:,.0f}]")
    print("  ⭐ Qiyshiq metrikalar — bootstrap; ikkilamchilar — tuzatish bilan")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ikkilamchi metrikalar ===
  sahifada vaqt   : xom p = 0.0000
  o'rtacha chek   : xom p = 0.2591
  qaytish (sath)  : xom p = 0.8243

=== 2. Holm tuzatishi (sath metrikasisiz) ===
  sahifada vaqt   : tuzatilgan p = 0.0000 ← muhim
  o'rtacha chek   : tuzatilgan p = 0.2591

=== 3. O'rtacha chek: bootstrap CI (qiyshiq metrika) ===
  o'rtacha chek: A 221,166, B 228,921
  farq CI: [-5,748, 21,185] so'm

=== 4. Mijoz boshiga daromad (konversiya × chek) ===
  A 11,611, B 13,897, farq CI [1,077, 3,493]
  ⭐ Qiyshiq metrikalar — bootstrap; ikkilamchilar — tuzatish bilan

Nima ko'rsatdi: 2.4, 2.6-bo'limlar.

Misol 4 — Segmentlar, erta to'xtatish va hisobot

python
"""Segment tahlili, erta to'xtatish xavfi va yakuniy hisobot (real numpy/scipy/statsmodels)."""

import numpy as np
from scipy import stats
from statsmodels.stats.multitest import multipletests
from statsmodels.stats.proportion import proportions_ztest


def yarat(seed: int = 7):
    """Test ma'lumoti: 24 000 + 24 000 foydalanuvchi, 12 kun."""
    rng = np.random.default_rng(seed)
    n = 24_000
    guruh = np.repeat(["A", "B"], n)
    kun = rng.integers(0, 12, 2 * n)
    qurilma = rng.choice(["mobil", "kompyuter"], 2 * n, p=[0.7, 0.3])
    yangi = rng.random(2 * n) < 0.4
    p_baza = np.where(kun % 7 >= 5, 0.062, 0.050)          # dam olish kunlari yuqoriroq
    p = np.where(guruh == "B", p_baza * 1.14, p_baza)      # haqiqiy effekt: +14% nisbiy
    konversiya = rng.random(2 * n) < p
    chek = np.where(konversiya, rng.lognormal(np.log(180_000), 0.7, 2 * n), 0.0)
    vaqt = rng.lognormal(np.log(95), 0.5, 2 * n) * np.where(guruh == "B", 1.02, 1.0)
    qaytish = rng.random(2 * n) < 0.021
    return {"guruh": guruh, "kun": kun, "qurilma": qurilma, "yangi": yangi,
            "konversiya": konversiya, "chek": chek, "vaqt": vaqt, "qaytish": qaytish}


def main() -> None:
    d = yarat()
    A = d["guruh"] == "A"

    print("=== 1. Oldindan belgilangan 4 segment ===")
    segmentlar = {
        "mobil": d["qurilma"] == "mobil",
        "kompyuter": d["qurilma"] == "kompyuter",
        "yangi mijoz": d["yangi"],
        "qaytgan mijoz": ~d["yangi"],
    }
    p_lar = []
    for nom, m in segmentlar.items():
        a = d["konversiya"][A & m]; b = d["konversiya"][(~A) & m]
        _, p = proportions_ztest([b.sum(), a.sum()], [len(b), len(a)])
        p_lar.append(p)
        print(f"  {nom:<14}: A {a.mean():.2%}, B {b.mean():.2%}, "
              f"nisbiy {(b.mean() - a.mean()) / a.mean():+.1%}, p = {p:.4f}")

    rad, p_tuz, _, _ = multipletests(p_lar, alpha=0.05, method="holm")
    print(f"  Holm tuzatilgan p: {np.round(p_tuz, 4).tolist()}")

    print("\n=== 2. Erta to'xtatish xavfi (simulyatsiya, H0 rost) ===")
    rng = np.random.default_rng(1)
    yolgon_erta = yolgon_oxir = 0
    N = 2000
    for _ in range(N):
        a = rng.random((12, 2000)) < 0.05
        b = rng.random((12, 2000)) < 0.05
        erta = False
        for k in range(1, 13):
            _, p = proportions_ztest([b[:k].sum(), a[:k].sum()], [k * 2000, k * 2000])
            if p < 0.05:
                erta = True
                break
        yolgon_erta += erta
        _, p_oxir = proportions_ztest([b.sum(), a.sum()], [24_000, 24_000])
        yolgon_oxir += p_oxir < 0.05
    print(f"  har kuni tekshirish: {yolgon_erta / N:.3f}")
    print(f"  faqat oxirida:       {yolgon_oxir / N:.3f}  (kutilgan 0.05)")

    print("\n=== 3. Yakuniy hisobot ===")
    xA = d["konversiya"][A].sum(); xB = d["konversiya"][~A].sum()
    nA = A.sum(); nB = (~A).sum()
    pA, pB = xA / nA, xB / nB
    _, p_asosiy = proportions_ztest([xB, xA], [nB, nA])
    print(f"  Asosiy metrika: konversiya {pA:.2%} → {pB:.2%} "
          f"({(pB - pA) / pA:+.1%} nisbiy), p = {p_asosiy:.4f}")
    print("  Ikkilamchilar: tuzatishdan keyingi natija — Misol 3 da")
    yomon = [nom for nom, p, m in zip(segmentlar, p_tuz, segmentlar.values())
             if p < 0.05 and d["konversiya"][(~A) & m].mean() < d["konversiya"][A & m].mean()]
    print(f"  Segmentlar: zarar ko'rsatgan segment {'yo\'q' if not yomon else yomon}")
    print("  Cheklov: 12 kun, bitta mavsum; uzoq muddatli ta'sir o'lchanmagan")
    print("  Tavsiya: ishga tushirish va 4 hafta monitoring")
    print("  ⭐ Hisobot — qaror, cheklovlar va keyingi qadam bilan")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Oldindan belgilangan 4 segment ===
  mobil         : A 5.22%, B 6.23%, nisbiy +19.3%, p = 0.0001
  kompyuter     : A 5.33%, B 5.70%, nisbiy +7.0%, p = 0.3265
  yangi mijoz   : A 5.37%, B 6.05%, nisbiy +12.6%, p = 0.0436
  qaytgan mijoz : A 5.17%, B 6.09%, nisbiy +17.7%, p = 0.0007
  Holm tuzatilgan p: [0.0003, 0.3265, 0.0872, 0.0022]

=== 2. Erta to'xtatish xavfi (simulyatsiya, H0 rost) ===
  har kuni tekshirish: 0.207
  faqat oxirida:       0.056  (kutilgan 0.05)

=== 3. Yakuniy hisobot ===
  Asosiy metrika: konversiya 5.25% → 6.07% (+15.6% nisbiy), p = 0.0001
  Ikkilamchilar: tuzatishdan keyingi natija — Misol 3 da
  Segmentlar: zarar ko'rsatgan segment yo'q
  Cheklov: 12 kun, bitta mavsum; uzoq muddatli ta'sir o'lchanmagan
  Tavsiya: ishga tushirish va 4 hafta monitoring
  ⭐ Hisobot — qaror, cheklovlar va keyingi qadam bilan

Nima ko'rsatdi: 2.5, 2.7, 2.8-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"p < 0.05 — ishga tushiramiz" CI va amaliy chegara
"Natija tez chiqsa to'xtatamiz" Erta to'xtatish — I tur xato oshadi
"Ko'p metrika — ko'p imkoniyat" Tuzatish kerak (11.9)
"Segmentda topilgan effekt — natija" Gipoteza (alohida test)
"SRM tekshirish shart emas" Birinchi qadam
"O'rtacha chek — t-test" Qiyshiq: bootstrap
"Neytral natija — muvaffaqiyatsizlik" Qimmatli bilim
"Test tugadi — ish tugadi" Monitoring va takroriy tekshiruv

6. Keng tarqalgan xatolar va yechimlari

1. Rejasiz test

python
# "ishga tushiramiz, keyin ko'ramiz"                              # ⚠️
# oldindan: metrika, chegara, n, muddat, to'xtash qoidasi          # ✅

2. Erta to'xtatish

python
if p < 0.05: testni_toxtat()      # har kuni                      # ⚠️
# belgilangan muddatgacha kutish                                  # ✅

3. SRM ni tekshirmaslik

python
# to'g'ridan-to'g'ri konversiyani solishtirish                    # ⚠️
stats.chisquare([n_a, n_b])   # avval                             # ✅

4. Sessiya darajasi

python
proportions_ztest([klik_b, klik_a], [sessiya_b, sessiya_a])       # ⚠️
# foydalanuvchi darajasiga agregatsiya                             # ✅

5. Tuzatishsiz ikkilamchilar

python
for m in metrikalar: test(m)                                      # ⚠️
multipletests(p_lar, alpha=0.05, method="holm")                   # ✅

6. Qiyshiq metrikaga t-test

python
stats.ttest_ind(chek_b, chek_a)                                   # ⚠️
stats.bootstrap((chek_b, chek_a), farq_funksiyasi, ...)           # ✅

7. Segment qidirish

python
eng_yaxshi_segment = min(segmentlar, key=lambda s: p[s])          # ⚠️
# oldindan belgilangan segmentlar + tuzatish                       # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 11.1-11.9-darslar (o'tilgan): Butun qism
  • 8.8-8.9-darslar (o'tilgan): Gipoteza, hisobot
  • 4.10-dars (o'tilgan): Sababiylik va eksperiment
  • 9.9-dars (o'tilgan): Qaror va xato narxi
  • Keyingi qismlar: ML modellarini A/B test bilan baholash

8. Eng yaxshi amaliyotlar

  1. Testni yozma reja bilan boshlang.

  2. Bitta asosiy metrika.

  3. SRM va sanity-checkdan boshlang.

  4. CI va amaliy chegara bilan qaror.

  5. Ikkilamchilarga tuzatish.

  6. Segmentlarni oldindan belgilang.

  7. Qiyshiq metrikalarga bootstrap.

  8. Neytral natijani ham hisobot qiling.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # asosiy metrika nechta?
2.  # amaliy chegara nima uchun?
3.  # SRM nima?
4.  # SRM p < 0.001 bo'lsa?
5.  # erta to'xtatish ta'siri?
6.  # ikkilamchilarga tuzatish?
7.  # segmentlar qachon belgilanadi?
8.  # qiyshiq metrikaga?
9.  # sath (guardrail) metrikasi nima?
10. # CI nolni o'z ichiga olsa?
11. # test davomiyligi nega to'liq hafta?
12. # neytral natija — nima?
Javoblar
  1. Bitta
  2. Biznes ahamiyati uchun
  3. Guruhlar nisbati buzilishi
  4. Tahlilni to'xtatib, sababni topish
  5. I tur xato oshadi
  6. Holm/BH
  7. Oldindan
  8. Bootstrap
  9. Zararni aniqlash metrikasi
  10. Dalil yetarli emas
  11. Hafta kuni effekti
  12. Qimmatli bilim

Vazifa 2: Xatolarni tuzating

python
1.  # har kuni p ni tekshirib, p < 0.05 da to'xtatish

2.  proportions_ztest([klik_b, klik_a], [sessiya_b, sessiya_a])

3.  for m in 12_metrika: print(m, p[m] < 0.05)

4.  stats.ttest_ind(chek_b, chek_a)   # kuchli qiyshiq

5.  # eng yaxshi natijali segmentni hisobotda ko'rsatish
Javoblar
python
1.  # oldindan belgilangan muddatgacha kutish (11.2)

2.  # foydalanuvchi darajasida agregatsiya

3.  rad, p_tuz, _, _ = multipletests(p_lar, alpha=0.05, method="holm")

4.  stats.bootstrap((chek_b, chek_a), farq, vectorized=True, random_state=0)

5.  # oldindan belgilangan segmentlar; natijani gipoteza deb belgilash

Vazifa 3: To'liq test

Modellang:

  1. O'z A/B testingiz (metrika, chegara, n)
  2. Sanity-check
  3. Asosiy va ikkilamchi tahlil
  4. Hisobot

Vazifa 4: Erta to'xtatish

Modellang:

  1. H0 rost, 21 kunlik test
  2. Har kuni tekshirish
  3. I tur xato o'sishi
  4. Ketma-ket usullar haqida qisqacha

Vazifa 5: Quvvat va MDE

Modellang:

  1. Turli baza konversiya
  2. Kerakli n va davomiylik
  3. MDE jadvali
  4. Qaysi testlarni o'tkazish mantiqiy

Vazifa 6: Integratsiya

Modellang:

  1. Quvvat (11.2)
  2. Proporsiya (11.4)
  3. Bootstrap (11.8)
  4. Tuzatish (11.9)

Vazifa 7: O'ylash

A/B test "oltin standart" deb ataladi, chunki tasodifiy taqsimot sababiy xulosaga imkon beradi. Lekin ko'p muhim savollarni A/B test bilan javoblab bo'lmaydi: brend obro'si, uzoq muddatli sodiqlik, tarmoq effektlari, kamdan-kam hodisalar. Bunday hollarda nima qilish kerak va A/B testning chegaralari qayerda?

Javob

Qisqa javob: A/B test qisqa muddatli, individual darajadagi va tez o'lchanadigan effektlar uchun ideal. Uzoq muddatli, tarmoqli yoki kamdan-kam hodisalar uchun boshqa usullar kerak: geo-testlar, difference-in-differences, kvazi-eksperimentlar, uzoq muddatli kuzatuv (holdout guruhlar).

1. A/B test chegaralari

Muammo Sabab
Uzoq muddatli effekt Test 2 hafta, effekt oylar
Tarmoq effekti Guruhlar bir-biriga ta'sir qiladi (SUTVA)
Kamdan-kam hodisa Quvvat yetmaydi (11.2)
Brend/obro' O'lchash qiyin, sekin
Yangilik effekti Boshlang'ich reaksiya != barqaror

2. Muqobil usullar

  • Geo-test / switchback: shaharlar yoki vaqt bo'laklari darajasida tasodifiylashtirish
  • Holdout guruh: foydalanuvchilarning kichik qismi uzoq vaqt eski tajribada
  • Difference-in-differences: tabiiy eksperiment (4.10)
  • Surrogat metrikalar: uzoq muddatli natija bilan bog'liq qisqa metrika (ehtiyotkorlik bilan)
  • Sifat tadqiqoti: intervyu, kuzatuv

3. Amaliy tavsiyalar

  1. Test davomiyligini effekt tabiatiga moslash
  2. Tarmoq effektlari bo'lsa — klaster darajasida tasodifiylashtirish
  3. Uzoq muddatli monitoring (metrikalar drift qilyaptimi)
  4. Bir necha usulni birlashtirish (triangulyatsiya)

4. Data Scientist qanday

  • "Bu savolga A/B test javob beradimi?" deb so'raydi
  • Cheklovlarni hisobotda ochiq yozadi (8.9)
  • Surrogat metrikalarning xavfini tushuntiradi

5. Xulosa

  1. A/B test — kuchli, lekin universal emas
  2. Uzoq muddat, tarmoq, kam hodisa — muammo
  3. Geo-test, holdout, kvazi-eksperiment — muqobil
  4. Usulni savolga moslash — asosiy mahorat

Nimani mustahkamlaydi: 2.1, 2.7-bo'limlar.


Xulosa

Bu darsda 11-qismning barcha bilimlarini A/B test loyihasida qo'lladik.

Eng muhim uch fikr:

  1. Oldindan loyihalash. Gipoteza 8.8-bob, bitta asosiy metrika, amaliy chegara, quvvat va namuna hajmi 11.2-bob, davomiylik (to'liq haftalar) va to'xtash qoidasi — hammasi testdan oldin yoziladi. Erta to'xtatish I tur xatoni bir necha barobar oshiradi.

  2. Sanity va qaror. Tahlil SRM va ma'lumot sifati tekshiruvidan boshlanadi 11.5-bob. Asosiy metrika: farq (foiz punkt va nisbiy) + ishonch oralig'i + p; qaror CI va amaliy chegara bilan qabul qilinadi, faqat p bilan emas (11.1, 11.4).

  3. Ikkilamchilar, segmentlar, hisobot. Ikkilamchi metrikalarga tuzatish (Holm, 11.9), sath metrikalari alohida; segmentlar oldindan belgilanadi va natijalari gipoteza sifatida qaraladi 8.8-bob; qiyshiq metrikalarga bootstrap 11.8-bob. Hisobot — xulosa birinchi, cheklovlar va keyingi qadam bilan 8.9-bob.

11-qism yakunlandi! Gipoteza testlari — noaniqlik ostida qaror qabul qilishning rasmiy tili: test tizimi va p-qiymat, quvvat va namuna hajmi, t-testlar, proporsiyalar, xi-kvadrat, ANOVA, noparametrik testlar, bootstrap va permutatsiya, ko'p taqqoslash va A/B test amaliyoti. Keyingi qismda Machine Learning asoslari boshlanadi: o'rganish nima, train/test ajratish, overfitting va bias-variance, birinchi modellar va baholash.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
11.10-dars: Amaliyot — A/B test loyihasi — IlmHamroh