T.UZUN_
VERİ ANALİTİĞİ

A/B Testi: Parametrik ve Non-Parametrik Yöntemler

A/B testlerinde istatistiksel yöntem seçimi, verinin normallik dağılımı ve varyans homojenliğine bağlıdır. Veri normal dağılımda T-Testi ve ANOVA gibi parametrik testlerde başarı sunarken, aşırı çarpık ve aykırı değer içeren verilerde Mann-Whitney U ve Kruskal-Wallis gibi non-parametrik yöntemler...

Paylaş
ab-testi.jpg

A/B testleri, dijital ürün ve büyüme analizlerinde kararları sezgilere değil, veriye dayandırmanın bel kemiğidir. Ancak topladığınız veri setine yanlış bir istatistiksel test uygulamak, p-değerlerini (p-value) manipüle ederek hatalı kararlar almanıza ve dönüşüm oranlarınızı artırmak isterken düşürmenize neden olabilir. Bir A/B testinde en kritik yol ayrımı, veri dağılımınıza bağlı olarak Parametrik mi yoksa Non-Parametrik istatistiksel yöntemleri mi seçeceğinizdir.

A/B Testinde Test Seçiminin Metodolojik Temelleri

Parametrik ve Non-Parametrik Yöntemler Nedir? / Neden Önemlidir?

Parametrik yöntemler, verilerin belirli bir teorik dağılıma (genellikle Normal Dağılım) uyduğunu ve belirli parametrelerle (ortalama, varyans) tanımlanabileceğini varsayan istatistiksel testlerdir. Veri setiniz normallik ve varyans homojenliği şartlarını sağladığında, parametrik testler en yüksek istatistiksel gücü (statistical power) sunar.

Non-Parametrik yöntemler ise verilerin herhangi bir dağılım gösterme zorunluluğu olmadığını (dağılımdan bağımsız / distribution-free) kabul eden testlerdir. Veride aşırı aykırı değerler (outliers), çarpıklık (skewness) veya küçük örneklem boyutu olduğunda, parametrik olmayan yöntemler ortalamalar yerine sıralamaları (ranks) ve medyanları karşılaştırarak en doğru sonucu verir.

Parametrik ve Non-Parametrik Testler Arasındaki Farklar

  • T-Testi vs. Mann-Whitney U Testi: T-Testi iki bağımsız grubun ortalamalarını (mean) karşılaştırırken normal dağılım şartı arar. Mann-Whitney U Testi ise sıralama tabanlıdır ve iki grubun medyan değerlerini/dağılımlarını karşılaştırarak normal dağılmayan verilerde T-testinin yerini alır.

  • ANOVA vs. Kruskal-Wallis Testi: İki ya da daha fazla grubun karşılaştırıldığı durumlarda ANOVA varyans analizi yaparak ortalamaları inceler (normallik ve varyans homojenliği gerektirir). Kruskal-Wallis Testi ise ANOVA'nın non-parametrik karşılığıdır; gruplar arasındaki medyan farklarını sıralamalar üzerinden değerlendirir.

Uygulama Senaryoları, Karşılaştırma Kriterleri ve Python Kod Örnekleri

Karşılaştırma Kriterleri

  1. Veri Normalliği: Parametrik testler verinin normal dağılmasını şart koşar (Shapiro-Wilk veya D'Agostino testleri ile kontrol edilir). Non-Parametrik testlerde normallik şartı yoktur.

  2. Örneklem Büyüklüğü: Parametrik testler büyük örneklemlerde (Merkezi Limit Teoremi gereği) daha dayanıklıdır. Non-Parametrik testler küçük örneklemlerde veya aşırı çarpık veri setlerinde daha güvenilirdir.

  3. İstatistiksel Güç: Dağılım varsayımları sağlandığında Parametrik testlerin istatistiksel gücü (Tip II hatayı önleme yeteneği) daha yüksektir. Varsayımlar ihlal edildiğinde ise Non-Parametrik testler daha güçlü hale gelir.

  4. Varyans Homojenliği: Parametrik testler grupların varyanslarının eşit olmasını ister (Levene Testi ile kontrol edilir). Non-Parametrik testler varyans homojenliği bozulduğunda esneklik sağlar.

Python Uygulama Kodları

1. Parametrik Yöntemler: T-Testi (İki Grup Ortalaması)

Tetikleyici / Kullanım Alanı: Sepet tutarı veya oturum süresi gibi normallik varsayımını sağlayan iki farklı A/B varyasyonunun ortalamalarını karşılaştırmak için kullanılır.

import numpy as np
from scipy import stats

# Normal dağılan iki grup verisi (A: Kontrol, B: Varyasyon)
np.random.seed(42)
group_a = np.random.normal(loc=100, scale=15, size=500)
group_b = np.random.normal(loc=103, scale=15, size=500)

# Bağımsız İki Örneklem T-Testi (Two-Sample t-test)
t_stat, p_value = stats.ttest_ind(group_a, group_b)

print(f"T-İstatistiği: {t_stat:.4f}, p-değeri: {p_value:.4f}")
if p_value < 0.05:
    print("Sonuç: Gruplar arasında istatistiksel olarak anlamlı bir fark vardır.")
else:
    print("Sonuç: Gruplar arasında anlamlı bir fark bulunamamıştır.")

2. Non-Parametrik Yöntemler: Mann-Whitney U Testi (İki Grup Medyanı)

Tetikleyici / Kullanım Alanı: Kullanıcıların harcama miktarları gibi aşırı sağa çarpık (sıfır tutarlı çokça sipariş olan) veya aykırı değer içeren A/B testi verilerinde tercih edilir.

import numpy as np
from scipy import stats

# Aşırı çarpık / Normal dağılmayan veri seti (Üstel dağılım)
np.random.seed(42)
group_a = np.random.exponential(scale=10, size=300)
group_b = np.random.exponential(scale=12, size=300)

# Mann-Whitney U Testi
u_stat, p_value = stats.mannwhitneyu(group_a, group_b, alternative='two-sided')

print(f"U-İstatistiği: {u_stat:.4f}, p-değeri: {p_value:.4f}")
if p_value < 0.05:
    print("Sonuç: Medyanlar/Dağılımlar arasında anlamlı bir fark vardır.")
else:
    print("Sonuç: Dağılımlar arasında anlamlı bir fark yoktur.")

3. Parametrik Çoklu Grup: ANOVA (Varyans Analizi)

Tetikleyici / Kullanım Alanı: A/B/C testlerinde (üç veya daha fazla grupta) normallik sağlandığında ortalamaları tek seferde test etmek için kullanılır.

import numpy as np
from scipy import stats

np.random.seed(42)
group_a = np.random.normal(10, 2, 200)
group_b = np.random.normal(10.5, 2, 200)
group_c = np.random.normal(11, 2, 200)

# One-Way ANOVA Testi
f_stat, p_value = stats.f_oneway(group_a, group_b, group_c)

print(f"F-İstatistiği: {f_stat:.4f}, p-değeri: {p_value:.4f}")

4. Non-Parametrik Çoklu Grup: Kruskal-Wallis Testi

Tetikleyici / Kullanım Alanı: Üç veya daha fazla A/B/n test grubunda veri normal dağılmadığında medyan farklılıklarını incelemek için kullanılır.

import numpy as np
from scipy import stats

np.random.seed(42)
group_a = np.random.lognormal(mean=1, sigma=0.8, size=150)
group_b = np.random.lognormal(mean=1.2, sigma=0.8, size=150)
group_c = np.random.lognormal(mean=1.1, sigma=0.8, size=150)

# Kruskal-Wallis H-Testi
h_stat, p_value = stats.kruskal(group_a, group_b, group_c)

print(f"H-İstatistiği: {h_stat:.4f}, p-değeri: {p_value:.4f}")

5.SENIOR YAKLAŞIM ÖRNEĞİ (Bonus)

Production-ready A/B test motoru:

from dataclasses import dataclass
from typing import List, Dict, Union, Tuple, Optional
import numpy as np
import pandas as pd
from scipy import stats


@dataclass
class TestResult:
    """Hipotez testi sonuçlarını standart bir formatta tutan veri sınıfı."""
    test_name: str
    statistic: float
    p_value: float
    is_significant: bool
    effect_size: Optional[float] = None
    effect_size_name: Optional[str] = None
    details: Optional[Dict[str, float]] = None

    def __repr__(self) -> str:
        sig_str = "Anlamlı Fark Var" if self.is_significant else "Anlamlı Fark Yok"
        eff_str = f" | {self.effect_size_name}: {self.effect_size:.4f}" if self.effect_size is not None else ""
        return (f"\n[{self.test_name}]\n"
                f"İstatistik: {self.statistic:.4f} | p-değeri: {self.p_value:.4e}\n"
                f"Karar: {sig_str}{eff_str}")


class ABTestingPipeline:
    """A/B Testleri ve çoklu grup deneysel analizleri için senior düzey test hattı."""

    def __init__(self, alpha: float = 0.05):
        self.alpha = alpha

    def _check_normality(self, *groups: np.ndarray) -> bool:
        """Shapiro-Wilk testi ile grupların normalliğini kontrol eder."""
        for g in groups:
            # Örneklem boyutu çok büyükse (>5000) Shapiro-Wilk hassasiyetini kaybedebilir
            sample = g if len(g) <= 5000 else np.random.choice(g, 5000, replace=False)
            _, p_val = stats.shapiro(sample)
            if p_val < self.alpha:
                return False  # Normallik varsayımı ihlal edildi
        return True

    def _check_homogeneity(self, *groups: np.ndarray) -> bool:
        """Levene testi ile varyans homojenliğini kontrol eder."""
        _, p_val = stats.levene(*groups)
        return p_val >= self.alpha

    @staticmethod
    def _cohens_d(group_a: np.ndarray, group_b: np.ndarray) -> float:
        """İki bağımsız grup için Cohen's d etki büyüklüğünü hesaplar."""
        n1, n2 = len(group_a), len(group_b)
        s1, s2 = np.var(group_a, ddof=1), np.var(group_b, ddof=1)
        s_pooled = np.sqrt(((n1 - 1) * s1 + (n2 - 1) * s2) / (n1 + n2 - 2))
        return (np.mean(group_a) - np.mean(group_b)) / s_pooled if s_pooled != 0 else 0.0

    @staticmethod
    def _eta_squared_anova(groups: List[np.ndarray]) -> float:
        """ANOVA için Eta-Squared (η²) etki büyüklüğü."""
        all_data = np.concatenate(groups)
        grand_mean = np.mean(all_data)
        ss_between = sum(len(g) * (np.mean(g) - grand_mean) ** 2 for g in groups)
        ss_total = sum(np.sum((g - grand_mean) ** 2) for g in groups)
        return ss_between / ss_total if ss_total != 0 else 0.0

    def compare_two_groups(self, group_a: np.ndarray, group_b: np.ndarray) -> TestResult:
        """
        İki grubu otomatik varsayım kontrollerine göre (T-Test / Welch T-Test / Mann-Whitney U) 
        en uygun yöntemle karşılaştırır.
        """
        is_normal = self._check_normality(group_a, group_b)
        
        if is_normal:
            is_equal_var = self._check_homogeneity(group_a, group_b)
            test_name = "Two-Sample Student's t-test" if is_equal_var else "Welch's t-test"
            stat, p_val = stats.ttest_ind(group_a, group_b, equal_var=is_equal_var)
            effect_size = self._cohens_d(group_a, group_b)
            eff_name = "Cohen's d"
        else:
            test_name = "Mann-Whitney U Test"
            stat, p_val = stats.mannwhitneyu(group_a, group_b, alternative='two-sided')
            # Rank Biserial Correlation as Effect Size for Mann-Whitney U
            n1, n2 = len(group_a), len(group_b)
            effect_size = 1 - (2 * stat) / (n1 * n2)
            eff_name = "Rank-Biserial Correlation"

        return TestResult(
            test_name=test_name,
            statistic=stat,
            p_value=p_val,
            is_significant=p_val < self.alpha,
            effect_size=abs(effect_size),
            effect_size_name=eff_name
        )

    def compare_multiple_groups(self, groups: List[np.ndarray]) -> TestResult:
        """
        3+ grubu varsayımlara göre ANOVA veya Kruskal-Wallis ile test eder.
        """
        is_normal = self._check_normality(*groups)
        is_equal_var = self._check_homogeneity(*groups) if is_normal else False

        if is_normal and is_equal_var:
            test_name = "One-Way ANOVA"
            stat, p_val = stats.f_oneway(*groups)
            effect_size = self._eta_squared_anova(groups)
            eff_name = "Eta-Squared (η²)"
        else:
            test_name = "Kruskal-Wallis H-Test"
            stat, p_val = stats.kruskal(*groups)
            # Eta squared for Kruskal-Wallis: (H - k + 1) / (N - k)
            n_total = sum(len(g) for g in groups)
            k = len(groups)
            effect_size = (stat - k + 1) / (n_total - k) if (n_total - k) > 0 else 0.0
            eff_name = "Kruskal-Wallis H Eta-Squared"

        return TestResult(
            test_name=test_name,
            statistic=stat,
            p_value=p_val,
            is_significant=p_val < self.alpha,
            effect_size=max(0.0, effect_size),
            effect_size_name=eff_name
        )
if __name__ == "__main__":
    np.random.seed(42)
    pipeline = ABTestingPipeline(alpha=0.05)

    # 1. Senaryo: Normal Dağılan İki Grup
    a_norm = np.random.normal(100, 15, 500)
    b_norm = np.random.normal(103, 15, 500)
    res_2_group_norm = pipeline.compare_two_groups(a_norm, b_norm)
    print(res_2_group_norm)

    # 2. Senaryo: Çarpık Dağılan İki Grup (Aşırı Sağ Çarpık)
    a_exp = np.random.exponential(10, 300)
    b_exp = np.random.exponential(12, 300)
    res_2_group_exp = pipeline.compare_two_groups(a_exp, b_exp)
    print(res_2_group_exp)

    # 3. Senaryo: Çoklu Grup (3 Varyasyon)
    g1 = np.random.lognormal(1.0, 0.8, 150)
    g2 = np.random.lognormal(1.2, 0.8, 150)
    g3 = np.random.lognormal(1.1, 0.8, 150)
    res_multi = pipeline.compare_multiple_groups([g1, g2, g3])
    print(res_multi)
[Two-Sample Student's t-test]
İstatistik: -3.6314 | p-değeri: 2.9627e-04
Karar: Anlamlı Fark Var | Cohen's d: 0.2297

[Mann-Whitney U Test]
İstatistik: 41561.0000 | p-değeri: 1.0532e-01
Karar: Anlamlı Fark Yok | Rank-Biserial Correlation: 0.0764

[Kruskal-Wallis H-Test]
İstatistik: 18.3497 | p-değeri: 1.0361e-04
Karar: Anlamlı Fark Var | Kruskal-Wallis H Eta-Squared: 0.0366

Çıktıların Analizi

  1. 1. Senaryo (Student's t-test):

    • Analiz: Sistem veri setinde normallik ve varyans homojenliğini sağladığı için parametrik t-testini seçti. p < 0.05 olduğu için A ve B grupları arasında istatistiksel olarak anlamlı bir fark var.

    • Senior Yorumu (Etki Büyüklüğü): Cohen's d değeri 0.2297 çıktı. Bu değer istatistiksel geleneksel skalada "küçük / düşük" düzeyindedir. İki grup arasında matematiksel olarak kanıtlanmış bir fark olsa da iş kararı alırken yani yeni özelliği devreye almanın maliyeti yüksekse bu farkın radikal bir ticari sıçrama yaratmayabileceği göz önünde bulundurulmalıdır.

  2. 2. Senaryo (Mann-Whitney U Testi):

    • Analiz: Veri seti çarpık dağıldığından motor normallik ihlalini tespit edip otomatik olarak non-parametrik Mann-Whitney U testine geçti.

    • Senior Yorumu: p = 0.1053 > 0.05 olduğu için grupların medyanları/dağılımları arasında anlamlı bir fark yoktur H_0 reddedilemez. Dönüşüm veya harcama oranlarında iki varyasyon birbirine denk davranmıştır.

  3. 3. Senaryo (Kruskal-Wallis H-Testi):

    • Analiz: 3 veya daha fazla grup incelendi ve verinin non-parametrik yapısı sebebiyle Kruskal-Wallis seçildi. p < 0.001 ile en az bir grubun diğerlerinden farklı davrandığı doğrulandı.

    • Senior Yorumu: Eta-Squared değeri 0.0366 seviyesindedir. Yani gruplar arasındaki varyansın yaklaşık %3.6'sı uygulanan varyasyon farklılığıyla açıklanmaktadır. Bir sonraki adım olarak Dunn's Post-Hoc testi (Bonferroni düzeltmeli) çalıştırılarak tam olarak hangi varyasyonun (A-B, A-C veya B-C) bu farkı yarattığı izole edilmelidir.

Yapılandırılmış Veri Tablosu (Matrix)

Yöntem / Test Adı

Test Türü

Karşılaştırılan Metrik

Temel Varsayım / Şart

T-Testi

Parametrik (2 Grup)

Ortalamalar

Normal dağılım, Varyans homojenliği

Mann-Whitney U

Non-Parametrik (2 Grup)

Medyanlar / Sıralamalar

Dağılım serbest (Çarpık veri, aykırı değer)

ANOVA

Parametrik (3+ Grup)

Ortalamalar

Normal dağılım, Grup varyanslarının eşitliği

Kruskal-Wallis

Non-Parametrik (3+ Grup)

Medyanlar / Sıralamalar

Dağılım serbest (Sıralı veya çarpık veri)

Sık Sorulan Sorular

A/B testinde parametrik mi yoksa non-parametrik yöntem mi seçeceğime nasıl karar veririm?

İlk olarak Shapiro-Wilk veya Kolmogorov-Smirnov testleri ile verinizin normal dağılıma uyup uymadığını kontrol etmelisiniz; veri normal dağılıyorsa parametrik, dağılmıyorsa veya aykırı değerler baskınsa non-parametrik yöntemi seçmelisiniz.

Normal dağılmayan bir veriye T-Testi uygulanırsa ne olur?

Normallik varsayımı ihlal edildiğinde T-testi yanlış p-değerleri üretebilir; bu durum Tip I hataya (gerçekte fark yokken varmış gibi görme) veya Tip II hataya (olan farkı kaçırma) yol açar.

Bu içerik sana ne hissettirdi?

İlgili Yazılar