Ana içeriğe geç
Kesit Python
İleri · Veri analizi ve bilimi

İstatistik ve hipotez testi

Betimsel istatistik, normal/binom/Poisson dağılımları, merkezi limit teoremi, güven aralığı, t-testi, ki-kare, korelasyon, Anadolu Market A/B testi, p-değeri yanlışları, etki büyüklüğü ve statsmodels ile doğrusal regresyon.

Uygulamada aç, 28 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

Veriden karara: neden istatistik?

Anadolu Market yeni bir kampanya sayfası denedi: ziyaretçilerin yarısı eski sayfayı (A), yarısı yeni sayfayı (B) gördü. A'da dönüşüm %12, B'de %15. Yönetim soruyor: "B'yi herkese açalım mı?" Üç puanlık fark gerçek bir iyileşme mi, yoksa şans eseri mi? Bin ziyaretçiyle mi ölçtük, yüz ziyaretçiyle mi? Bu sorunun cevabı tahmin değil, istatistiksel çıkarımdır (statistical inference).

Bu derste betimsel istatistikten başlayıp dağılımlara, örneklemeye, güven aralığına, hipotez testlerine (t, ki-kare, korelasyon), A/B testine, etki büyüklüğüne ve doğrusal regresyona ilerleyeceğiz. Araçlar: numpy (bunu numpy: sayısal diziler ve vektörleştirme dersinde gördün), pandas, scipy.stats ve statsmodels.

Betimsel istatistik: veriyi özetlemek

Her analiz veriye bakarak başlar. Merkez (ortalama, medyan), yayılım (standart sapma, çeyrekler arası açıklık) ve şekil (çarpıklık) üç temel sorudur.

import numpy as np
import pandas as pd
from scipy import stats

sepet = pd.Series([120, 95, 410, 260, 180, 80, 150, 210, 175, 140, 190, 2400])
print("ortalama:", round(sepet.mean(), 1), "| medyan:", sepet.median())
print("std (örneklem, ddof=1):", round(sepet.std(), 1), "| numpy varsayılanı ddof=0:", round(np.std(sepet), 1))
q1, q3 = sepet.quantile([0.25, 0.75])
print("Q1:", q1, "Q3:", q3, "IQR:", q3 - q1)
print("çarpıklık:", round(stats.skew(sepet), 2))
print(sepet.describe().round(1))

Tek bir 2400 TL'lik sipariş ortalamayı 367 TL'ye çekti, medyan 177,5 TL'de kaldı. Gelir, sepet tutarı, bekleme süresi gibi sağa çarpık verilerde "tipik değer" için medyan, yayılım için IQR daha dürüsttür.

Ölçüpandas / numpyNot
Ortalama, medyanmean(), median()Aykırı değer ortalamayı çeker
Standart sapmas.std() (ddof=1), np.std(x) (ddof=0)Örneklemde ddof=1 kullan
Çeyrekler, IQRquantile([.25, .75])Aykırı değere dayanıklı
Çarpıklıkstats.skew> 0: sağ kuyruk uzun
Mods.mode()Kategorik veride anlamlı
ddof tuzağınp.std varsayılan olarak nüfus (population) formülünü (n'e böler), pandas.Series.std örneklem formülünü (n-1'e böler) kullanır. Küçük veride fark büyüktür. Örneklemden çıkarım yapıyorsan ddof=1.

Olasılık dağılımları: scipy.stats

Gerçek süreçler bilinen dağılımlarla iyi modellenir. Normal dağılım ölçüm hatası ve ortalamalar için, binom "n denemede kaç başarı" (100 siparişten kaçı iade?), Poisson "birim zamanda kaç olay" (çağrı merkezine saatte kaç arama?) için kullanılır. scipy'de her dağılımın aynı yöntemleri vardır.

YöntemSoruÖrnek
pmf(k) / pdf(x)Tam olarak k olasılığı (yoğunluk)binom.pmf(2, 10, 0.1)
cdf(x)x veya daha aznorm.cdf(1.96) ≈ 0.975
sf(x)x'ten büyük (1 - cdf)poisson.sf(4, 2.5): 4'ten fazla
ppf(q)q yüzdelik dilimin değerinorm.ppf(0.975) ≈ 1.96
rvs(size, random_state)Rastgele örnek üretSimülasyon
from scipy import stats

# Teslimat süresi: ortalama 48 saat, std 8 saat (normal)
teslim = stats.norm(loc=48, scale=8)
print("72 saati aşma olasılığı:", round(teslim.sf(72), 4))
print("siparişlerin %95'i kaç saatte teslim:", round(teslim.ppf(0.95), 1))

# İade oranı %10, günde 10 sipariş (binom): tam 2 iade, en az 1 iade
print("tam 2 iade:", round(stats.binom.pmf(2, 10, 0.10), 4))
print("en az 1 iade:", round(1 - stats.binom.pmf(0, 10, 0.10), 4))

# Ilgaz Bank çağrı merkezi: saatte ortalama 2.5 şikâyet (Poisson)
print("bir saatte 4'ten fazla şikâyet:", round(stats.poisson.sf(4, 2.5), 4))
cdf mi sf mi?Kesikli dağılımlarda (binom, Poisson) "en az k" için sf(k - 1), "k'dan fazla" için sf(k) yaz. Bir eksik/fazla yazılan k, en sık yapılan olasılık hatasıdır.

Örnekleme ve merkezi limit teoremi

Tüm müşterileri ölçemezsin; bir örneklem (sample) alırsın. Merkezi limit teoremi (central limit theorem) der ki: popülasyon dağılımı ne olursa olsun (çarpık bile), yeterince büyük örneklemlerin ortalamaları yaklaşık normal dağılır ve bu ortalamaların standart sapması (standart hata) σ / √n olur. Güven aralıklarının ve t-testlerinin çalışmasının sebebi budur.

import numpy as np

rng = np.random.default_rng(42)
nufus = rng.exponential(scale=200, size=100_000)       # çok çarpık: sepet tutarı
print("nüfus ortalaması:", round(nufus.mean(), 1), "| std:", round(nufus.std(), 1))

for n in (5, 30, 200):
    ortalamalar = rng.choice(nufus, size=(2000, n)).mean(axis=1)
    print(f"n={n:>3}: ortalamaların ortalaması={ortalamalar.mean():6.1f}  "
          f"std hata={ortalamalar.std():5.1f}  teorik={nufus.std() / np.sqrt(n):5.1f}")

Örneklem büyüdükçe ortalamalar gerçek değerin etrafında daralır; 4 kat büyük örneklem hatayı yarıya indirir. "Neden 30 değil 3000 kişiyle test ettik?" sorusunun cevabı bu √n'dir.

Güven aralığı

Örneklem ortalaması tek bir sayıdır; güven aralığı (confidence interval) bu tahminin ne kadar kesin olduğunu söyler. Küçük örneklemde ve σ bilinmediğinde t dağılımı kullanılır: ortalama ± t* · s / √n.

import numpy as np
from scipy import stats

bekleme = np.array([4.2, 6.1, 3.8, 7.5, 5.0, 4.9, 6.3, 5.5, 4.1, 5.8])   # dakika, şube kuyruğu
n = len(bekleme)
ort = bekleme.mean()
se = bekleme.std(ddof=1) / np.sqrt(n)
t_kritik = stats.t.ppf(0.975, df=n - 1)
print(f"elle: {ort - t_kritik * se:.2f} – {ort + t_kritik * se:.2f}")
alt, ust = stats.t.interval(0.95, df=n - 1, loc=ort, scale=se)
print(f"scipy: {alt:.2f} – {ust:.2f}   (t*={t_kritik:.3f}, z ile 1.96 olurdu)")
Güven aralığını doğru oku"%95 güven aralığı 4.6–6.0" demek, "gerçek ortalama %95 olasılıkla bu aralıkta" demek değildir. Doğru yorum: bu yöntemi tekrar tekrar uygulasaydık, üretilen aralıkların %95'i gerçek ortalamayı kapsardı. Pratikte: aralık genişse elinde kesin bir bilgi yoktur, daha çok veri topla.

Hipotez testi çerçevesi

Her test aynı iskeleti izler: (1) sıfır hipotezi H0 "fark yok", alternatif H1 "fark var"; (2) anlamlılık düzeyi α'yı önceden seç (genellikle 0.05); (3) test istatistiğini ve p-değerini hesapla; (4) p < α ise H0'ı reddet. p-değeri: H0 doğru olsaydı, gözlediğin kadar ya da daha uç bir sonuç görme olasılığı.

SoruVeriTest
İki bağımsız grubun ortalaması farklı mı?Sayısal, iki grupttest_ind(a, b, equal_var=False) (Welch)
Aynı birimlerde önce/sonra farkı var mı?Sayısal, eşlittest_rel(once, sonra)
İki kategorik değişken ilişkili mi?Sayım tablosuchi2_contingency(tablo)
İki oran farklı mı? (A/B)Başarı / denemeproportions_ztest ya da ki-kare
İki sayısal değişken birlikte mi hareket ediyor?Sayısal çiftlerpearsonr (doğrusal), spearmanr (sıralı)
import numpy as np
from scipy import stats

rng = np.random.default_rng(7)
istanbul = rng.normal(310, 60, 40)      # sepet tutarı
ankara = rng.normal(285, 75, 35)
t, p = stats.ttest_ind(istanbul, ankara, equal_var=False)
print(f"Welch t={t:.2f}, p={p:.4f}")

# Eşli: aynı 8 kasiyerin eğitim öncesi/sonrası işlem süresi (sn)
once = np.array([62, 58, 71, 66, 59, 73, 68, 64])
sonra = np.array([57, 55, 66, 63, 58, 67, 62, 61])
print("eşli t-testi p =", round(stats.ttest_rel(once, sonra).pvalue, 5))
print("yanlış (bağımsız) t-testi p =", round(stats.ttest_ind(once, sonra).pvalue, 4))

Eşli veride bağımsız test kullanmak, kasiyerler arasındaki doğal farkı gürültü olarak sayar ve gerçek etkiyi gizler: aynı veriyle p birkaç binde birden 0,05'in üstüne çıkar.

import pandas as pd
from scipy import stats

# Şehir ile ödeme yöntemi ilişkili mi? (sipariş sayıları)
tablo = pd.DataFrame({"Kart": [320, 210, 150], "Kapıda": [80, 90, 110]},
                     index=["İstanbul", "Ankara", "İzmir"])
chi2, p, dof, beklenen = stats.chi2_contingency(tablo)
print(f"ki-kare={chi2:.1f}, sd={dof}, p={p:.6f}")
print(pd.DataFrame(beklenen, index=tablo.index, columns=tablo.columns).round(1))

x = [1, 2, 3, 4, 5, 6]
y = [2, 4, 9, 15, 30, 70]                       # artan ama doğrusal değil
print("pearson:", round(stats.pearsonr(x, y)[0], 3), "| spearman:", round(stats.spearmanr(x, y)[0], 3))

Ki-kare testi gözlenen sayımları "ilişki olmasaydı" beklenecek sayımlarla kıyaslar. Spearman sıralar üzerinden çalıştığı için tekdüze (monoton) ama doğrusal olmayan ilişkide 1 verir; Pearson yalnızca doğrusallığı ölçer. Korelasyon nedensellik değildir: dondurma satışı ile klima satışı birlikte artar, ikisinin de sebebi sıcaklıktır.

A/B testi: Anadolu Market kampanyası

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

donusum = np.array([120, 150])        # A, B
ziyaret = np.array([1000, 1000])
oran = donusum / ziyaret
z, p = proportions_ztest(donusum, ziyaret)
lift = (oran[1] / oran[0] - 1) * 100
alt, ust = confint_proportions_2indep(donusum[1], ziyaret[1], donusum[0], ziyaret[0])
print(f"A={oran[0]:.1%}  B={oran[1]:.1%}  göreli artış={lift:.1f}%")
print(f"z={z:.3f}  p={p:.4f}")
print(f"B - A farkı için %95 GA: {alt:.3%} … {ust:.3%}")

p ≈ 0.0496: sınırda. Güven aralığı farkın neredeyse 0'dan %6'ya kadar olabileceğini söylüyor. Doğru karar cümlesi: "B muhtemelen daha iyi, ama etkinin büyüklüğü belirsiz; daha büyük örneklemle doğrula." Testi her gün kontrol edip p 0.05'in altına ilk düştüğünde durdurmak (peeking) yanlış pozitif oranını katlar: örneklem büyüklüğünü önceden hesapla ve sonuna kadar bekle.

p-değeri hakkında sık yanlışlar ve etki büyüklüğü

YanlışDoğrusu
"p = 0.03: H0'ın doğru olma olasılığı %3"p, H0 doğruysa bu kadar uç veri görme olasılığıdır; H0'ın olasılığı değildir
"p > 0.05: fark yok, kanıtlandı"Kanıt yetersiz demektir; örneklem küçükse gerçek fark kaçabilir (II. tip hata)
"p çok küçük: etki çok büyük"Milyon satırda önemsiz farklar bile anlamlı çıkar; etki büyüklüğüne bak
"20 metrik test ettim, biri anlamlı"α=0.05 ile 20 testte 1 yanlış pozitif beklenir; çoklu karşılaştırma düzeltmesi yap

Etki büyüklüğü (effect size) farkın pratikte ne kadar önemli olduğunu ölçer. Oranlarda göreli artış (lift), ortalamalarda Cohen's d: iki ortalama farkının ortak standart sapmaya bölümü. Kabaca 0.2 küçük, 0.5 orta, 0.8 büyük sayılır. İstatistiksel anlamlılık "fark gerçek mi?", etki büyüklüğü "fark önemli mi?" sorusunu cevaplar; karar için ikisi birlikte gerekir.

statsmodels ile doğrusal regresyon

Regresyon, bir sonucu (ciro) birden çok açıklayıcı değişkenle (reklam bütçesi, indirim oranı) ilişkilendirir ve her birinin etkisini diğerleri sabitken tahmin eder. statsmodels'in formül arayüzü R'a benzer: "ciro ~ reklam + indirim".

import numpy as np
import pandas as pd
import statsmodels.formula.api as smf

rng = np.random.default_rng(3)
n = 60
df = pd.DataFrame({"reklam": rng.uniform(5, 50, n), "indirim": rng.uniform(0, 20, n)})
df["ciro"] = 120 + 2.5 * df["reklam"] + 1.2 * df["indirim"] + rng.normal(0, 15, n)

model = smf.ols("ciro ~ reklam + indirim", data=df).fit()
print(model.summary().tables[1])
print("R²:", round(model.rsquared, 3), "| düzeltilmiş R²:", round(model.rsquared_adj, 3))
print("reklam katsayısı GA:", model.conf_int().loc["reklam"].round(2).tolist())
print("tahmin:", model.predict(pd.DataFrame({"reklam": [30], "indirim": [10]})).round(1).tolist())
Özet tablosu sütunuAnlamı
coefDiğerleri sabitken değişkendeki 1 birim artışın sonuca etkisi (reklama +1 bin TL → ciro +2.5 bin TL)
std errKatsayı tahmininin belirsizliği
t, P>|t|"Katsayı 0" hipotezinin testi; küçük p, etkinin sıfırdan farklı olduğunu gösterir
[0.025 0.975]Katsayının %95 güven aralığı
R-squaredSonuçtaki değişkenliğin modelce açıklanan oranı; değişken eklendikçe hep artar, bu yüzden düzeltilmiş R²'ye de bak
Gerçek işteE-ticaret ve bankacılıkta deney ekipleri her A/B testi için önceden bir plan yazar: birincil metrik (dönüşüm), en küçük anlamlı etki (ör. %1 puan), gereken örneklem büyüklüğü (güç analiziyle), test süresi (en az bir tam hafta, haftanın günü etkisi için) ve durdurma kuralı. Sonuç raporunda p-değeri tek başına durmaz: oranlar, fark, güven aralığı ve iş etkisi (yıllık ek ciro tahmini) birlikte sunulur. Regresyon ise "fiyat esnekliği", "kredi notunu etkileyen faktörler" gibi açıklama sorularının temel aracıdır.
Derinleş

Derinleş: gerçek bir A/B testini uçtan uca okumak

Derste A/B testinin temel mantığını gördün. Gerçek bir deneyi raporlamadan önce kıdemli analistlerin sırayla sorduğu dört soru vardır: (1) Gruplar doğru bölündü mü? (SRM), (2) Dönüşüm farkı anlamlı mı ve ne kadar büyük? (oranlar için z-testi ve güven aralığı), (3) Sonuca erken mi baktık? (peeking), (4) Daha az kullanıcıyla aynı kesinliğe ulaşabilir miydik? (CUPED). Hepsini Anadolu Market'in gerçek deney tablosuyla yapacağız: anadolu_market/ab_test, 2.000 kullanıcı; variant A (eski tasarım) ya da B (yeni tasarım), converted 0/1, revenue kullanıcının getirdiği gelir.

import veri

ab = veri.oku("anadolu_market", "ab_test")
ozet = ab.groupby("variant").agg(kullanici=("user_id", "size"), donusum=("converted", "mean"),
                                 gelir_kisi=("revenue", "mean"))
print(ozet.round(4))

1. Önce SRM: örneklem oranı uyumsuzluğu

Deney %50/%50 tasarlandıysa gruplar yaklaşık eşit olmalı. A'da 975, B'de 1.025 kullanıcı var. Bu fark şans eseri mi? SRM (sample ratio mismatch) kontrolü, beklenen oranla gözlenen sayıları ki-kare uygunluk testiyle karşılaştırır. SRM varsa (ör. B sayfası bazı tarayıcılarda çöküyor ve o kullanıcılar kaydedilmiyorsa) gruplar artık karşılaştırılabilir değildir ve hiçbir sonuç yorumlanmaz. Bu kontrol için çok küçük bir eşik (ör. p < 0.001) kullanılır; amaç ciddi kırılmaları yakalamaktır.

import veri
from scipy import stats

ab = veri.oku("anadolu_market", "ab_test")
n = ab["variant"].value_counts().reindex(["A", "B"])
beklenen = [n.sum() * 0.5, n.sum() * 0.5]
sonuc = stats.chisquare(n.values, f_exp=beklenen)
print(n.to_dict(), "p =", round(float(sonuc.pvalue), 4))
print("SRM var" if sonuc.pvalue < 0.001 else "SRM yok: analize devam")

2. Oranlar için z-testi ve fark için güven aralığı

İki dönüşüm oranını karşılaştırmanın standart yolu iki oran z-testidir. Test istatistiği birleşik (pooled) oranla hesaplanır: z = (p_B − p_A) / √(p(1−p)(1/n_A + 1/n_B)). Fark için güven aralığında ise her grubun kendi oranı kullanılır (birleştirilmemiş standart hata). Raporda p-değeri tek başına değil, farkın büyüklüğü ve aralığıyla birlikte verilir.

import veri
import numpy as np
from scipy import stats
from statsmodels.stats.proportion import proportions_ztest

ab = veri.oku("anadolu_market", "ab_test")
g = ab.groupby("variant")["converted"].agg(["sum", "count"])
xa, na = g.loc["A"]; xb, nb = g.loc["B"]
pa, pb = xa / na, xb / nb
p = (xa + xb) / (na + nb)
z = (pb - pa) / np.sqrt(p * (1 - p) * (1 / na + 1 / nb))
pdeg = 2 * stats.norm.sf(abs(z))
se = np.sqrt(pa * (1 - pa) / na + pb * (1 - pb) / nb)
print(f"A %{pa*100:.2f}  B %{pb*100:.2f}  fark {100*(pb-pa):.2f} puan")
print(f"z = {z:.3f}, p = {pdeg:.4f}, %95 GA: [{100*(pb-pa-1.96*se):.2f}, {100*(pb-pa+1.96*se):.2f}] puan")
print("statsmodels:", [round(float(v), 4) for v in proportions_ztest([xb, xa], [nb, na])])

Gelir gibi çarpık metriklerde (kullanıcıların %87'si 0 gelir getiriyor) ortalama farkı için Welch t-testi ya da daha güvenli olarak bootstrap güven aralığı kullanılır; derste gördüğün yöntem burada da geçerlidir.

3. Peeking: sonuca her gün bakmak

Deneyi her gün açıp "p < 0.05 oldu mu?" diye bakar ve olduğu gün durdurursan, yanlış pozitif oranın %5 değil çok daha yüksek olur. Aşağıda kullanıcılar geliş sırasına (user_id) göre birikirken p-değerinin nasıl dalgalandığını görüyorsun: 200 kullanıcıda p = 0.014 ile "anlamlı" görünüyor, sonra yüzlerce kullanıcı boyunca 0.05'in üstüne çıkıyor.

import veri
import numpy as np
from scipy import stats

ab = veri.oku("anadolu_market", "ab_test").sort_values("user_id")

def p_deger(s):
    g = s.groupby("variant")["converted"].agg(["sum", "count"])
    xa, na = g.loc["A"]; xb, nb = g.loc["B"]
    p = (xa + xb) / (na + nb)
    z = (xb / nb - xa / na) / np.sqrt(p * (1 - p) * (1 / na + 1 / nb))
    return 2 * stats.norm.sf(abs(z))

yol = [round(float(p_deger(ab.iloc[:k])), 3) for k in range(200, 2001, 200)]
print(yol)

Gerçekte fark olmayan (A/A) binlerce deney simüle edersen, 10 kez bakıp ilk "anlamlı" sonuçta durmak yanlış pozitif oranını yaklaşık %5'ten %15–20'ye çıkarır. Çözümler: örneklem büyüklüğünü güç analiziyle önceden belirleyip yalnızca sonda bakmak; ara bakış gerekiyorsa sıralı test (sequential testing) kullanmak. Sıralı testlerde her ara bakışa α'nın bir kısmı harcanır (alpha spending; O'Brien-Fleming sınırları ilk bakışlarda çok katı, sonda neredeyse 0.05'tir) ya da her an geçerli (always-valid) p-değerleri üreten yöntemler (mSPRT) kullanılır. Ticari deney platformlarının "erken durdurma" düğmeleri bu yöntemlere dayanır.

4. CUPED: deney öncesi veriyle varyans azaltma

Kullanıcıların harcaması deneyden önce de çok farklıdır; bu doğal farklılık gürültü olarak test sonucuna girer. CUPED (Controlled-experiment Using Pre-Experiment Data), deney öncesi metrik X ile sonuç Y arasındaki ilişkiyi kullanarak gürültüyü çıkarır: θ = cov(Y, X) / var(X), Y_cuped = Y − θ (X − ort(X)). X deneyden önce ölçüldüğü için gruplardan bağımsızdır; bu yüzden ortalama fark değişmez (yansızdır), yalnızca varyans yaklaşık ρ² oranında düşer. Aynı kesinlik için daha az kullanıcı, yani daha kısa deney demektir.

Veri notuab_test tablosunda deney öncesi metrik yok. CUPED'i göstermek için aşağıda tohumlu (seed) bir simülasyon kullanıyoruz. Kendi şirketinde X genellikle deneyden önceki 2–4 haftanın aynı metriğidir (harcama, oturum sayısı).
import numpy as np

def deney(seed=21, n=2000, etki=3.0):
    rng = np.random.default_rng(seed)
    onceki = rng.gamma(2.0, 30.0, n)          # deney öncesi 30 günlük harcama
    grup = rng.integers(0, 2, n)              # 0 = A, 1 = B
    sonraki = 0.8 * onceki + rng.normal(0, 20, n) + etki * grup
    return onceki, grup, sonraki

from scipy import stats

x, g, y = deney()
theta = np.cov(y, x)[0, 1] / np.var(x, ddof=1)
y_c = y - theta * (x - x.mean())
for ad, m in [("ham", y), ("CUPED", y_c)]:
    fark = m[g == 1].mean() - m[g == 0].mean()
    p = stats.ttest_ind(m[g == 1], m[g == 0], equal_var=False).pvalue
    print(f"{ad:6} fark={fark:.2f}  varyans={m.var(ddof=1):.0f}  p={p:.4f}")
Gerçek işteDeney raporunun ilk satırı SRM sonucudur; SRM varsa rapor orada biter ve mühendislik ekibiyle kök neden aranır. Sonra birincil metrik (dönüşüm) farkı ve güven aralığı, ardından koruma metrikleri (gelir, iade, sayfa hızı) gelir. Deney süresi ve örneklem başlamadan yazılı olarak sabitlenir; ara bakış yapılacaksa sıralı test planı da önceden yazılır.
Uzmanlaş

Güç analizi: kaç kişi lazım?

Testin gücü (power), gerçek bir etki varken onu yakalama olasılığıdır (genellikle 0.8 hedeflenir). statsmodels.stats.power.NormalIndPower().solve_power(effect_size=..., alpha=0.05, power=0.8) grup başına gereken örneklemi verir; oranlar için etki büyüklüğü proportion_effectsize(0.15, 0.12) ile hesaplanır. %12'den %15'e çıkışı güvenle yakalamak için grup başına yaklaşık 2000 ziyaretçi gerekir; bin kişilik testin sınırda kalması bu yüzdendir.

Çoklu karşılaştırma ve Simpson paradoksu

Birçok segmentte ya da metrikte test yapıyorsan Bonferroni (α / test sayısı) ya da Benjamini-Hochberg (statsmodels.stats.multitest.multipletests) düzeltmesi uygula. Toplamda B kazanırken her şehirde A'nın kazanması mümkündür (Simpson paradoksu): grupların karışımı farklıysa kırılımlara mutlaka bak.

Varsayım yoksa: bootstrap ve permütasyon

Medyan gibi formülü zor istatistikler için bootstrap: veriden yerine koyarak binlerce örneklem çek, istatistiği her birinde hesapla, yüzdelik dilimlerden güven aralığı al (scipy.stats.bootstrap). Dağılım varsayımı olmayan iki grup karşılaştırması için permütasyon testi (scipy.stats.permutation_test) ya da Mann-Whitney U (mannwhitneyu) kullan.

Alıştırmalara geç (28)