সরাসরি পর্বে যান
পর্ব ৬২ · ৭৭-এর মধ্যে · মডিউল ১২

A/B test-এর পরিকল্পনা — power, sample size আর peeking-এর ফাঁদ

Designing A/B Tests — Power, Sample Size and the Peeking Trap

Taught in Bangla, this lesson plans an A/B test before launch: hypothesis, primary and guardrail metrics, randomisation unit, and per-arm sample size from MDE, alpha and power, checked against statsmodels. In-browser simulations draw power curves, show how daily peeking inflates false positives, derive Pocock and O'Brien–Fleming style bounds, and measure design effect, SRM and A/A tests.

এই পর্বে যা শিখবেন

  • একটি experiment-এর পুরো চক্র: hypothesis, primary ও guardrail metric, randomisation-এর একক আর লিখিত test plan
  • MDE, α আর power থেকে sample size হাতে বের করা, statsmodels দিয়ে মেলানো, আর ভারী লেজের revenue metric কেন বেশি n চায় তা বোঝা
  • Simulation দিয়ে power curve আঁকা, আর কম power-এর test কেন বিপজ্জনক তা দেখা
  • Peeking কেন ভুল আবিষ্কার বাড়ায়, আর fixed horizon, Pocock, O'Brien–Fleming ও alpha spending কীভাবে তা ঠেকায়
  • Cluster randomisation-এর design effect, SRM-এর chi-square check, A/A test, novelty প্রভাব আর সাপ্তাহিক ছন্দ

AI-এর ভিত্তি কোর্সের পর্ব ৩৭-এ standard error, confidence interval, p-value, effect size আর একটি সরল A/B test-এর প্রথম পরিচয় হয়েছে; এই কোর্সের পর্ব ২২-এ একাধিক তুলনা আর পর্ব ০৪-এ forking paths. সেগুলো আবার শেখানো হবে না। এই পর্বের প্রশ্ন একটাই: test চালুর আগে কী ঠিক করলে শেষের ফলটি বিশ্বাস করা যায়? শেষ হওয়া test-এর বিশ্লেষণ (Welch t-test, CUPED, delta method) আসবে পরের পর্বে, পর্ব ৬৩ (A/B test-এর বিশ্লেষণ — t-test, chi-square, CUPED আর ভুল ব্যাখ্যা)। চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python: NumPy, SciPy, pandas, statsmodels, matplotlib); প্রথমবার package নামতে কয়েক সেকেন্ড লাগে। সব ডেটা seed দেওয়া simulation, কোনো বাস্তব দোকানের নয়।

১ · Experiment-এর চক্র — hypothesis, metric আর randomisation-এর একক

ধরুন একজন চা-দোকানি জানতে চান, নতুন কাপে চা বেশি বিক্রি হয় কি না। এক সপ্তাহ নতুন কাপ, পরের সপ্তাহ পুরোনো: কিন্তু দ্বিতীয় সপ্তাহে বৃষ্টি হলো, পরীক্ষাও শেষ হলো। পার্থক্যটা কাপের, না আবহাওয়ার? সৎ উত্তর পেতে প্রতিটি খদ্দেরকে মুদ্রা ছুড়ে কাপ দিতে হয়, যাতে বৃষ্টি, পরীক্ষা আর মেজাজ দুই দলে সমান ভাগ হয়। এটিই randomised experiment-এর মূল কথা।

একটি A/B test (অনলাইনে একে online controlled experiment-ও বলে) এলোমেলোভাবে ব্যবহারকারীদের দুই দলে ভাগ করে: control (A) পুরোনো ব্যবস্থা পায়, treatment (B) নতুন। ভাগ এলোমেলো বলে দলের মধ্যে বাকি সব পার্থক্য গড়ে সমান, তাই ফলের পার্থক্যকে নতুন ব্যবস্থার প্রভাব বলা যায়। কেন randomisation এটি নিশ্চিত করে, তার আনুষ্ঠানিক যুক্তি (potential outcomes) আসবে পর্ব ৬৪ (Causal inference-এর পরিচয়)-এ।

বাজারঘরের product দল checkout পাতায় একটি পরিবর্তন চায়: “ক্যাশ অন ডেলিভারি” বোতাম ওপরে তোলা আর মোট দামের নিচে delivery-র আনুমানিক দিন দেখানো। Test চালুর আগে চারটি জিনিস লিখে রাখতে হয়:

Hypothesis

“Delivery-র দিন দেখালে অনিশ্চয়তা কমে, তাই checkout শুরু করা গ্রাহকদের বেশি অংশ অর্ডার শেষ করবেন।” একটি কারণ, একটি দিক, একটি মাপা যায় এমন ফল।

Primary metric

একটিমাত্র primary metric, যার ওপর সিদ্ধান্ত: checkout-এ আসা গ্রাহকের মধ্যে কতজন অর্ডার শেষ করলেন (conversion). বেশি metric মানে বেশি সুযোগে কাকতালীয় “জয়”।

Guardrail

Guardrail metric এমন মাপ যা খারাপ হওয়া চলবে না: অর্ডার বাতিলের হার, ফেরতের হার, পাতা খোলার সময়, অভিযোগ। এগুলো জিততে হয় না, শুধু ক্ষতি ধরতে হয়।

Randomisation-এর একক

কাকে ধরে মুদ্রা ছোড়া হবে: গ্রাহক, session, বিক্রেতা, নাকি delivery zone. Randomisation unit ভুল হলে দুই দল একে অপরকে প্রভাবিত করে (অংশ ৭)।

নিচের ছবি পুরো চক্রটি দেখায়। এই পর্ব ধাপ ১ থেকে ৫ নিয়ে, ধাপ ৬ পর্ব ৬৩-এর, আর ধাপ ৭-এর সিদ্ধান্ত লেখা পর্ব ৬৭ (Data storytelling)-এর বিষয়।

একটি A/B test-এর চক্র

The experiment loop: plan first, then run, then analyse

The A/B test loop from hypothesis to decision ১ · প্রশ্ন আর hypothesis ২ · Primary metric + guardrail ৩ · MDE, α, power → n আর দিন ৪ · Randomisation-এর একক ৫ · চালানো: SRM, A/A যাচাই মাঝপথে থামা নয় ৬ · বিশ্লেষণ (পর্ব ৬৩) ৭ · সিদ্ধান্ত আর শিক্ষা নতুন প্রশ্ন
ছবি ১.১ — নীল চারটি ধাপ test চালুর আগে, কাগজে। কমলা ধাপে শুধু ডেটা জমে আর যন্ত্রের স্বাস্থ্য দেখা হয়, সিদ্ধান্ত নয়। সবুজ ধাপের পর নতুন প্রশ্ন নিয়ে চক্র আবার শুরু।

২ · Metric বাছাই — conversion, revenue per visitor আর ভারী লেজ

Primary metric-এর দুটি গুণ লাগে: ব্যবসার লক্ষ্যের সঙ্গে সম্পর্ক, আর সংবেদনশীলতা, অর্থাৎ ছোট আসল পরিবর্তনেও নড়ে। বাজারঘরের দুটি প্রার্থী:

  • Conversion: প্রতিটি গ্রাহকের ফল ০ বা ১; variance p(1 − p), p = ০.০৫ হলে ০.০৪৭৫. সীমিত, স্থির, আর ছোট n-এও গড় প্রায় normal.
  • Revenue per visitor (RPV): বেশিরভাগ গ্রাহকের আয় ০, কয়েকজন ৳৫০০, আর মাঝে মাঝে কেউ ৳২০,০০০-এর ফ্রিজ কেনেন। এই heavy-tailed বণ্টনে SD গড়ের অনেক গুণ।

গড়ের তুলনার জন্য প্রতি দলের n-এর সূত্র (দুই দলে সমান n, দুই দিকের test):

n = 2 (z1−α/2 + z1−β)² σ² ÷ δ²

এখানে σ একজন গ্রাহকের metric-এর standard deviation, δ যে পার্থক্য ধরতে চান, আর z-গুলো standard normal-এর quantile (পরের অংশে বিস্তারিত)। α = ০.০৫ আর power ৮০% হলে (z1−α/2 + z1−β)² = (১.৯৬০ + ০.৮৪২)² = ৭.৮৪৯. ধরুন RPV-এর গড় ৳৪০, SD ৳২৫০ (coefficient of variation σ/গড় = ৬.২৫), আর আপনি ৫% উন্নতি, মানে δ = ৳২, ধরতে চান: n = ২ × ৭.৮৪৯ × ২৫০² ÷ ২² ≈ ২,৪৫,২৭৭ জন প্রতি দলে। একই ৫% আপেক্ষিক উন্নতি conversion-এ (৫% থেকে ৫.২৫%) ধরতে লাগে প্রায় ১,২২,১২৪ (অংশ ৩-এর টেবিল): প্রায় অর্ধেক। কারণ n চলে coefficient of variation-এর বর্গের সঙ্গে, আর conversion-এর CV √০.০৪৭৫ ÷ ০.০৫ ≈ ৪.৩৬, RPV-র ৬.২৫-এর চেয়ে কম।

ভারী লেজের তিন ফাঁদ। (১) একজন বড় ক্রেতা যে দলে পড়েন, সেই দল “জেতে”; একটি outlier পুরো সিদ্ধান্ত ঘোরাতে পারে। (২) ছোট n-এ গড়ের বণ্টন তখনও normal নয়, তাই z-test-এর p-value ভুল হতে পারে। (৩) pilot ডেটার SD নিজেও অস্থির, তাই sample size-এর হিসাব নড়ে। প্রতিকার পরিকল্পনাতেই লিখে রাখুন: আয়কে আগে ঠিক করা একটি সীমায় কাটা (capping বা winsorising), বা conversion-কে primary আর RPV-কে secondary রাখা। বিশ্লেষণের দিক (trimmed mean, bootstrap CI, ratio metric-এর delta method) পর্ব ৬৩-এ।

অনেক প্রতিষ্ঠান কয়েকটি metric-কে ওজন দিয়ে মিলিয়ে একটি overall evaluation criterion বানায়, যাতে দীর্ঘমেয়াদি মূল্য ধরা পড়ে; Kohavi, Tang আর Xu-র ২০২০ সালের বই Trustworthy Online Controlled Experiments এই ধারণা বিস্তারে আলোচনা করে। ছোট দলের জন্য সরল নিয়মই যথেষ্ট: একটি primary, দুই-তিনটি guardrail, বাকি সব “শুধু দেখার জন্য”, আর তালিকাটি চালুর আগে লেখা।

৩ · MDE, α আর power — sample size-এর সূত্র হাতে ও কোডে

একটি test দুই রকম ভুল করতে পারে। আসলে পার্থক্য নেই, তবু “আছে” বলা: Type I error, যার হার α. আসলে পার্থক্য আছে, তবু ধরতে না পারা: Type II error, যার হার β. আর 1 − β হলো power: আসল প্রভাব থাকলে test তা ধরবে, এমন সম্ভাবনা।

আসল অবস্থাTest বলে “পার্থক্য আছে”Test বলে “দেখা যায়নি”
পার্থক্য নেই (H0 সত্য)Type I error, হার α (সাধারণত ০.০৫)ঠিক, হার 1 − α
পার্থক্য আছে, আকার MDE বা বেশিঠিক, হার = power (সাধারণত ০.৮০)Type II error, হার β

তৃতীয় সংখ্যাটি minimum detectable effect (MDE): সবচেয়ে ছোট যে পার্থক্য আপনার কাছে কাজের, আর যাকে ৮০% সম্ভাবনায় ধরতে চান। MDE ঠিক করে ব্যবসা, পরিসংখ্যান নয়: পরিবর্তনটি বানাতে আর রাখতে যে খরচ, তা উঠে আসতে কত উন্নতি লাগবে? “আমরা ২০% উন্নতি আশা করি” বলে বড় MDE বাছা একটি পরিচিত ফাঁদ; আশা বড় হলে n ছোট দেখায়, আর আসল ছোট প্রভাব ধরা পড়ে না।

দুটি অনুপাত p1 (control) আর p2 = p1(1 + MDE)-এর তুলনায় normal approximation থেকে প্রতি দলের n:

n = [ z1−α/2 √(2 p̄ (1 − p̄)) + z1−β √(p1(1 − p1) + p2(1 − p2)) ]² ÷ (p2 − p1)²

p̄ দুই হারের গড়। যুক্তি সরল: H0-এর অধীনে পার্থক্যের standard error থেকে বাতিলের সীমা z1−α/2 দূরে বসে; আর আসল পার্থক্য δ হলে পার্থক্যের বণ্টনের ৮০% সেই সীমার ওপারে চাই, তাই সীমা থেকে δ পর্যন্ত আরও z1−β standard error. দুই দূরত্বের যোগ δ-এর সমান ধরলেই সূত্রটি আসে। একটি পরিচিত সংক্ষিপ্ত রূপ Lehr-এর নিয়ম: n ≈ ১৬σ²/δ², এখানে ১৬ × ০.০৫২৫ × ০.৯৪৭৫ ÷ ০.০০৫² ≈ ৩১,৮৩৬, প্রায় একই।

বাজারঘর checkout: control 5%, ধরতে চাই 5.5%; α = 0.05 (দুই দিকে), power 0.80
ধাপ
হিসাব
ফল
  1. ইনপুট। “১০% উন্নতি” আপেক্ষিক: ৫% থেকে ৫.৫%. পরম পার্থক্য δ = ০.০০৫, খুব ছোট; তাই n বড় হবে।
  2. z-মান। দুই দিকের α = ০.০৫ মানে দুই লেজে ২.৫% করে, সীমা ১.৯৬০. Power ৮০% মানে আরও ০.৮৪২ standard error দূরত্ব।
  3. H0-এর অংশ। পার্থক্য না থাকলে দুই দলের হার p̄, আর পার্থক্যের SD-এর √n গুণ √(2p̄(1 − p̄)) = ০.৩১৫৪২. গুণ ১.৯৬০ = ০.৬১৮২০.
  4. বিকল্পের অংশ। আসল হার ০.০৫ আর ০.০৫৫ হলে SD-এর √n গুণ √(০.০৪৭৫ + ০.০৫১৯৭৫) = ০.৩১৫৪০. গুণ ০.৮৪২ = ০.২৬৫৪৪.
  5. যোগ ও ভাগ। ০.৮৮৩৬৫-এর বর্গ ০.৭৮০৮৪, δ² = ০.০০০০২৫ দিয়ে ভাগে ৩১,২৩৩.৪. মানুষ ভগ্নাংশে হয় না, আর কম নিলে power ৮০%-এর নিচে নামে, তাই ওপরে গোল: ৩১,২৩৪.
  6. সময়। দিনে ৪,৫০০ জন checkout-এ এলে ৬২,৪৬৮ জন জমতে ১৩.৯ দিন। সাপ্তাহিক ছন্দ পুরো ধরতে ১৪ দিন, মানে ঠিক দুই সপ্তাহ (অংশ ৯)।

নিচের ব্লক একই হিসাব নিজে লেখা ফাংশনে করে, তারপর statsmodels-এর দুটি ফাংশনের সঙ্গে মেলায়। statsmodels অনুপাতকে সরাসরি নয়, Cohen's h নামের effect size দিয়ে দেখে: h = 2·arcsin√p2 − 2·arcsin√p1. Arcsine রূপান্তরে অনুপাতের variance প্রায় স্থির হয়, তাই উত্তর সামান্য আলাদা। শেষে চারটি baseline আর তিনটি MDE-র একটি টেবিল।

import numpy as np
import pandas as pd
from scipy.stats import norm
from statsmodels.stats.power import NormalIndPower, zt_ind_solve_power
from statsmodels.stats.proportion import proportion_effectsize

def n_per_arm(p1, rel_mde, alpha=0.05, power=0.80):
    p2 = p1 * (1 + rel_mde)                       # যে নতুন হার ধরতে চাই
    za, zb = norm.ppf(1 - alpha / 2), norm.ppf(power)
    pbar = (p1 + p2) / 2
    top = za * np.sqrt(2 * pbar * (1 - pbar)) + zb * np.sqrt(p1 * (1 - p1) + p2 * (1 - p2))
    return int(np.ceil(top**2 / (p2 - p1) ** 2))

p1, mde = 0.05, 0.10                               # বাজারঘর checkout: 5% → 5.5%
print("z(0.975) =", round(norm.ppf(0.975), 4), "| z(0.80) =", round(norm.ppf(0.80), 4))
print("নিজে লেখা সূত্র, প্রতি দলে n =", n_per_arm(p1, mde))

h = proportion_effectsize(p1 * (1 + mde), p1)     # Cohen's h = 2·asin√p2 − 2·asin√p1
n_sm = NormalIndPower().solve_power(effect_size=h, alpha=0.05, power=0.80, ratio=1)
n_zt = zt_ind_solve_power(effect_size=h, alpha=0.05, power=0.80)
print(f"Cohen's h = {h:.5f} | NormalIndPower n = {n_sm:.1f} | zt_ind_solve_power n = {n_zt:.1f}")

rows = {}
for base in [0.02, 0.05, 0.10, 0.20]:
    rows[f"p = {base:.0%}"] = [n_per_arm(base, m) for m in (0.05, 0.10, 0.20)]
tab = pd.DataFrame(rows, index=["MDE 5%", "MDE 10%", "MDE 20%"]).T
print("\nপ্রতি দলে প্রয়োজনীয় n (α = 0.05 দুই দিকে, power 80%):")
print(tab.to_string())
print("\nMDE অর্ধেক করলে n কত গুণ:", round(tab.iloc[1, 0] / tab.iloc[1, 1], 2))

আসল আউটপুট (ব্রাউজারের Python):

z(0.975) = 1.96 | z(0.80) = 0.8416
নিজে লেখা সূত্র, প্রতি দলে n = 31234
Cohen's h = 0.02242 | NormalIndPower n = 31217.4 | zt_ind_solve_power n = 31217.4

প্রতি দলে প্রয়োজনীয় n (α = 0.05 দুই দিকে, power 80%):
         MDE 5%  MDE 10%  MDE 20%
p = 2%   315206    80682    21109
p = 5%   122124    31234     8158
p = 10%   57763    14751     3841
p = 20%   25583     6510     1683

MDE অর্ধেক করলে n কত গুণ: 3.91

মিল: নিজে লেখা সূত্র ৩১,২৩৪, stepper-এর হাতের হিসাবের সমান। statsmodels-এর দুটি ফাংশন একই কাজ দুই পথে করে আর দেয় ৩১,২১৭.৪; পার্থক্য ০.১%-এর কম, আসে Cohen's h-এর arcsine রূপান্তর থেকে। টেবিল পড়া: একটি সারি ধরে ডানে গেলে MDE দ্বিগুণ, n প্রায় চার ভাগের এক ভাগ (শেষ লাইনে ৩.৯১ গুণ), কারণ n চলে ১/δ²-এর সঙ্গে। ওপর থেকে নিচে baseline বাড়লে একই আপেক্ষিক MDE-তে পরম δ বড় হয়, তাই n কমে। ২% baseline-এ ৫% আপেক্ষিক উন্নতি ধরতে প্রতি দলে ৩ লাখের বেশি: ছোট দোকানের জন্য অসম্ভব, তাই সেখানে বড় MDE মেনে নেওয়া বা উঁচু-funnel-এর metric বাছাই বাস্তবসম্মত। উল্টো দিক থেকেও প্রশ্ন করা যায়: হাতে n = ২০,০০০ থাকলে কত ছোট প্রভাব ধরা যাবে? এখানে MDE প্রায় ১২.৬% (অনুশীলন ৪)।

৪ · Power curve — simulation দিয়ে সূত্র যাচাই

সূত্র একটি approximation. যাচাইয়ের সবচেয়ে সৎ পথ Monte Carlo simulation: একটি আসল উন্নতি ধরে হাজারটি কাল্পনিক test চালান, আর গুনুন কতগুলোতে |z| > ১.৯৬. সেই অনুপাতই power. নিচের ব্লক চারটি n আর ০% থেকে ২০% পর্যন্ত এগারোটি আসল উন্নতিতে ২,০০০টি করে test চালায়, বিন্দু আঁকে simulation-এর, রেখা আঁকে সূত্রের।

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

rng = np.random.default_rng(62)
p1, sims = 0.05, 2000
lifts = np.linspace(0, 0.20, 11)                  # আসল আপেক্ষিক উন্নতি 0%–20%
fig, ax = plt.subplots(figsize=(7, 4.2))
for n in [8000, 15000, 31234, 60000]:             # প্রতি দলে n
    power_sim, power_th = [], []
    for lift in lifts:
        p2 = p1 * (1 + lift)
        a = rng.binomial(n, p1, sims) / n          # 2000টি কাল্পনিক test একসঙ্গে
        b = rng.binomial(n, p2, sims) / n
        pool = (a + b) / 2
        z = (b - a) / np.sqrt(pool * (1 - pool) * 2 / n)
        power_sim.append(np.mean(np.abs(z) > 1.96))
        se = np.sqrt((p1 * (1 - p1) + p2 * (1 - p2)) / n)
        power_th.append(norm.cdf((p2 - p1) / se - 1.96) + norm.cdf(-(p2 - p1) / se - 1.96))
    ax.plot(lifts * 100, power_sim, "o", ms=4)
    ax.plot(lifts * 100, power_th, "-", color=ax.lines[-1].get_color(), label=f"n = {n:,}")
    i = 5                                          # lift 10%
    print(f"n={n:6d}: lift 0% → {power_sim[0]:.3f} | lift 10% → sim {power_sim[i]:.3f}, "
          f"সূত্র {power_th[i]:.3f}")
ax.axhline(0.8, ls="--", color="gray", lw=1)
ax.axhline(0.05, ls=":", color="gray", lw=1)
ax.set_xlabel("True relative lift (%)")
ax.set_ylabel("Power = P(reject H0)")
ax.set_title("Power curves, baseline 5%, two-sided alpha 0.05")
ax.legend(title="per arm", fontsize=8)
plt.show()

আসল আউটপুট (ব্রাউজারের Python; ছবিটি ব্লকের নিচে আঁকা হয়):

n=  8000: lift 0% → 0.047 | lift 10% → sim 0.284, সূত্র 0.294
n= 15000: lift 0% → 0.045 | lift 10% → sim 0.491, সূত্র 0.493
n= 31234: lift 0% → 0.052 | lift 10% → sim 0.786, সূত্র 0.800
n= 60000: lift 0% → 0.035 | lift 10% → sim 0.969, সূত্র 0.973

বাম প্রান্ত (lift ০%): এখানে কোনো আসল পার্থক্য নেই, তাই “power” আসলে Type I error-এর হার: ০.০৩৫ থেকে ০.০৫২, ৫%-এর চারপাশে simulation-এর নড়াচড়া (২,০০০ test-এ SE প্রায় ০.০০৫)। ১০% lift: n = ৩১,২৩৪-এ simulation ০.৭৮৬, সূত্র ০.৮০০; পার্থক্যটি দুই SE-এর মতো, simulation-এর এলোমেলোতার মধ্যে। n = ৮,০০০-এ power মাত্র ০.২৮–০.২৯: এমন test চারবারের মধ্যে প্রায় তিনবার আসল ১০% উন্নতি মিস করে। ছবি: প্রতিটি রেখা S-আকারের; n বাড়লে রেখা বাঁয়ে সরে, মানে ছোট প্রভাবও ধরা পড়ে।

কম power-এর আরেক বিপদ। কম power-এর test যখন “জেতে”, তখন সাধারণত শুধু সেইসব run জেতে যেখানে এলোমেলোতায় পার্থক্য বড় দেখিয়েছে। তাই significant ফলের আকার আসলের চেয়ে বড় হয়, কখনো দিকও উল্টো। Andrew Gelman আর John Carlin ২০১৪ সালের একটি প্রবন্ধে এদের Type M (magnitude) আর Type S (sign) error নাম দেন। n = ৮,০০০-এর test-এ “১৫% উন্নতি” পেলে, আসল প্রভাব সম্ভবত অনেক ছোট।

৫ · Peeking-এর ফাঁদ — রোজ দেখলে ভুল “জয়” বাড়ে

Test চলছে, dashboard রোজ নতুন p-value দেখায়। তৃতীয় দিনে p = ০.০৩: “জিতেছি, চালু করে দিই!” এই অভ্যাসের নাম peeking: ডেটা জমার মাঝে বারবার test করা আর প্রথম significant ফলে থামা। প্রতিটি দেখা আলাদাভাবে ৫%-এর test, কিন্তু অনেকবার দেখলে “কোনো একবার ৫% পার হওয়া”র সম্ভাবনা ৫%-এর চেয়ে অনেক বেশি।

ক্রিকেট ম্যাচের মাঝে প্রতি ওভারে রান রেট দেখে “এখনই জয় ঘোষণা” করার নিয়ম ভাবুন। আসলে সমান দুই দল হলেও কোনো না কোনো ওভারে এক দল অনেক এগিয়ে থাকে। যত বেশি ওভারে দেখবেন, তত বেশি ভুল ঘোষণা। পুরো ম্যাচ শেষে একবার দেখলে এই ভুল হয় না।

দুই দলে একই ব্যবস্থা (কোনো আসল পার্থক্য নেই); প্রতিদিন শেষে জমা ডেটায় z
কবে দেখা
সীমা
ভুল “জয়”-এর মোট সম্ভাবনা
  1. একবার দেখা। আগে ঠিক করা দিনে একবার test: H0 সত্য হলে |z| > ১.৯৬-এর সম্ভাবনা ঠিক ৫%. এটিই α-এর প্রতিশ্রুতি।
  2. দুইবার দেখা। সপ্তম দিনে দেখলে সেখানেই ৫% ভুলের সুযোগ। দুই দিনের z স্বাধীন নয়: সপ্তম দিনের ডেটা চোদ্দতম দিনের অর্ধেক, তাই correlation √(৭/১৪) = ০.৭০৭.
  3. দ্বিতীয় সুযোগ। সপ্তম দিনে যে পথগুলো সীমা পার হয়নি, তাদের কিছু চোদ্দতম দিনে পার হয়। দুই চলকের normal বণ্টন থেকে এই বাড়তি অংশ ০.০৩৩, মোট ০.০৮৩.
  4. বেশি দেখা। প্রতিটি নতুন দেখা আরেকটি সুযোগ। ৫ বারে প্রায় ১৫%, রোজ ১৪ বারে প্রায় ২৩% (নিচের simulation). p-value বদলায়নি, বদলেছে থামার নিয়ম।
  5. অসীম দেখা। z-এর পথ একটি random walk-এর মতো; একটি স্থির সীমা ১.৯৬ যথেষ্ট লম্বা সময়ে প্রায় নিশ্চিতভাবে একবার না একবার পার হয় (law of the iterated logarithm). তাই “significant না হওয়া পর্যন্ত চালাও” প্রায় সবসময় একদিন significant হয়।
  6. প্রতিকার। বারবার দেখতে চাইলে প্রতিটি দেখার সীমা উঁচু করুন, যাতে সব দেখা মিলে মোট ভুল ৫% থাকে: Pocock-এর সমান সীমা, বা O'Brien–Fleming-এর শুরুতে খুব কড়া আর শেষে প্রায় ১.৯৬ সীমা (অংশ ৬)।

নিচের ব্লক ৪,০০০টি A/A test-এর ১৪ দিনের z-পথ বানায়, দিনে প্রতি দলে ২,২৫০ জন (অংশ ৩-এর হিসাবের মতো দুই সপ্তাহে প্রতি দলে ৩১,৫০০)। প্রথমে দেখায় দেখার সংখ্যা বাড়লে ভুল কীভাবে বাড়ে। তারপর একই পথ থেকে Pocock আর O'Brien–Fleming ধাঁচের সীমা simulation দিয়েই বের করে, আর নতুন পথে তিন নিয়মের ভুল, power আর গড় থামার দিন মাপে; আসল ১০% উন্নতির পথও বানানো হয়।

import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(7)
D, per_day, p, sims = 14, 2250, 0.05, 4000         # 14 দিন, দিনে প্রতি দলে 2250 জন
n = per_day * np.arange(1, D + 1)                  # প্রতিদিন শেষে জমা গ্রাহক
def z_paths(p_b):                                  # প্রতিদিন শেষে জমা ডেটায় |z|
    a = rng.binomial(per_day, p, (sims, D)).cumsum(1)
    b = rng.binomial(per_day, p_b, (sims, D)).cumsum(1)
    pool = (a + b) / (2 * n)
    return np.abs((b - a) / n / np.sqrt(pool * (1 - pool) * 2 / n))

Z = z_paths(p)                                     # A/A: কোনো আসল পার্থক্য নেই
fpr = [np.mean((Z[:, :k] > 1.96).any(1)) for k in range(1, D + 1)]
print("শুধু শেষ দিনে একবার দেখা:", round(float(np.mean(Z[:, -1] > 1.96)), 3))
print("রোজ দেখে p < 0.05 হলেই থামা, 2/5/8/11/14 বার:", [round(float(f), 3) for f in fpr[1::3]])
t = n / n[-1]                                      # কতটা ডেটা এসেছে (information fraction)
c_poc = np.quantile(Z.max(1), 0.95)                # Pocock: সব দিনে একই সীমা
c_obf = np.quantile((Z * np.sqrt(t)).max(1), 0.95) # O'Brien–Fleming ধাঁচ: সীমা c/√t
print(f"Pocock সীমা {c_poc:.2f} | OBF সীমা দিন 1: {c_obf / np.sqrt(t[0]):.2f}, "
      f"দিন 7: {c_obf / np.sqrt(t[6]):.2f}, দিন 14: {c_obf:.2f}")
Z2, Zb = z_paths(p), z_paths(p * 1.10)             # নতুন A/A, আর আসল 10% উন্নতি
for name, bound in [("fixed", np.r_[[np.inf] * (D - 1), 1.96]),
                    ("Pocock", np.full(D, c_poc)), ("OBF", c_obf / np.sqrt(t))]:
    hit = Zb > bound
    stop = np.where(hit.any(1), hit.argmax(1) + 1, D)
    print(f"{name:6s}: A/A ভুল {(Z2 > bound).any(1).mean():.3f} | power {hit.any(1).mean():.3f}"
          f" | গড়ে থামার দিন {stop.mean():.1f}")

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(range(1, D + 1), fpr, "o-", label="peek daily, stop at p < 0.05")
ax.axhline(0.05, ls="--", color="gray", label="nominal 5%")
ax.set_xlabel("Number of daily looks"); ax.set_ylabel("False-positive rate (A/A)")
ax.set_title("Peeking inflates false positives"); ax.legend()
plt.show()

আসল আউটপুট (ব্রাউজারের Python; ছবিটি ব্লকের নিচে):

শুধু শেষ দিনে একবার দেখা: 0.053
রোজ দেখে p < 0.05 হলেই থামা, 2/5/8/11/14 বার: [0.09, 0.145, 0.183, 0.206, 0.229]
Pocock সীমা 2.64 | OBF সীমা দিন 1: 7.91, দিন 7: 2.99, দিন 14: 2.11
fixed : A/A ভুল 0.053 | power 0.800 | গড়ে থামার দিন 14.0
Pocock: A/A ভুল 0.050 | power 0.663 | গড়ে থামার দিন 9.6
OBF   : A/A ভুল 0.050 | power 0.777 | গড়ে থামার দিন 10.6

প্রথম দুই লাইন: শেষ দিনে একবার দেখলে ভুল ৫.৩%, প্রত্যাশিত ৫%-এর কাছে (৪,০০০ test-এ SE প্রায় ০.৩৫ শতাংশ বিন্দু)। রোজ দেখলে ২ বারে ৯.০%, ৫ বারে ১৪.৫%, ১৪ বারে ২২.৯%: stepper-এর হিসাবের সঙ্গে simulation-এর নড়াচড়ার মধ্যে মেলে; দুই বারের তাত্ত্বিক মান ৮.৩ শতাংশ। ছবিতে রেখাটি থামে না, শুধু ধীর হয়। বাকি লাইনগুলো অংশ ৬-এ পড়ব।

৬ · সমাধান — fixed horizon, group-sequential সীমা আর alpha spending

প্রথম সমাধান সবচেয়ে সরল: fixed horizon. n আর দিন আগে ঠিক, শেষ দিনে একবার test. মাঝে dashboard দেখা যায় শুধু যন্ত্রের স্বাস্থ্য (SRM, error, guardrail-এর বড় ক্ষতি) দেখতে, সিদ্ধান্তের জন্য নয়। ওপরের আউটপুটে “fixed” সারি: A/A ভুল ০.০৫৩, power ০.৮০০, থামা সবসময় দিন ১৪.

দ্বিতীয় সমাধান group-sequential test: কয়েকটি আগে ঠিক করা দেখা, প্রতিটিতে উঁচু সীমা, যাতে মোট ভুল α থাকে। এই ধারা এসেছে ক্লিনিক্যাল ট্রায়াল থেকে, যেখানে ওষুধ স্পষ্ট উপকারী বা ক্ষতিকর হলে আগে থামা নৈতিক দায়িত্ব। Armitage, McPherson আর Rowe ১৯৬৯ সালে দেখান বারবার test-এ ভুল কতটা বাড়ে; Stuart Pocock ১৯৭৭ সালে সব দেখায় সমান সীমা প্রস্তাব করেন, আর Peter O'Brien ও Thomas Fleming ১৯৭৯ সালে এমন সীমা দেন যা শুরুতে খুব কড়া আর শেষে প্রায় সাধারণ।

  • Pocock (Pocock boundary): ওপরের run-এ ১৪টি দেখায় প্রতিদিন |z| > ২.৬৪. মোট ভুল ০.০৫০, কিন্তু শেষ দিনেও ২.৬৪ লাগে বলে power ০.৮০০ থেকে ০.৬৬৩-এ নামে। আসল প্রভাব থাকলে গড়ে থামা ৯.৬ দিনে।
  • O'Brien–Fleming (O'Brien–Fleming boundary): সীমা c/√t, t হলো এ পর্যন্ত আসা তথ্যের ভাগ। প্রথম দিনে ৭.৯১ (প্রায় অসম্ভব), সপ্তম দিনে ২.৯৯, শেষে ২.১১. মোট ভুল ০.০৫০, power ০.৭৭৭ (fixed-এর কাছাকাছি), আর আসল প্রভাব থাকলে গড়ে ১০.৬ দিনে থামে।

এখানে সীমাগুলো simulation-এর ৯৫তম percentile থেকে বের করা, যাতে ধারণাটি স্বচ্ছ থাকে; পাঠ্যবইয়ের টেবিল বা বিশেষ সফটওয়্যার সংখ্যাগুলো সংখ্যাগত সমাকলন দিয়ে আরও নিখুঁত দেয়, আর প্রকৃত ব্যবহারে সেগুলোই নিন। বাস্তবে আরেকটি সমস্যা: দেখার দিন আর সংখ্যা আগে জানা না-ও থাকতে পারে। Lan আর DeMets ১৯৮৩ সালে এর সমাধান দেন alpha spending দিয়ে: মোট α-কে একটি ফাংশন α(t) অনুযায়ী তথ্যের ভাগ t-এর সঙ্গে “খরচ” করা। দুটি পরিচিত ফাংশন, হাতে হিসাব করা মান:

তথ্যের ভাগ tOBF ধাঁচ: 2 − 2Φ(1.96/√t)Pocock ধাঁচ: 0.05 · ln(1 + (e − 1)t)
০.২৫০.০০০০৯০.০১৭৯
০.৫০০.০০৫৫৭০.০৩১০
০.৭৫০.০২৩৬০.০৪১৪
১.০০০.০৫০০০.০৫০০

Φ standard normal-এর cumulative distribution. টেবিলের মান মোট খরচ করা α, প্রতিটি দেখার সীমা নয়: OBF ধাঁচে অর্ধেক ডেটায় মাত্র ০.০০৫৬ খরচ হয়েছে, বাকি প্রায় সবটুকু শেষের জন্য জমা। প্রতিটি দেখার আসল সীমা আগের দেখাগুলোর correlation ধরে হিসাব করতে হয়, যা সফটওয়্যারের কাজ।

তৃতীয় পথ: সবসময় বৈধ পদ্ধতি। Abraham Wald-এর sequential probability ratio test (১৯৪০-এর দশক) থেকে আসা একটি ধারা এমন p-value আর confidence interval দেয়, যা যেকোনো সময় দেখলেও বৈধ থাকে (always-valid inference, confidence sequence). Johari, Pekelis আর Walsh-এর mixture SPRT-ভিত্তিক কাজ, আর Howard ও সহকর্মীদের confidence sequence এই ধারার পরিচিত উদাহরণ; কয়েকটি বাণিজ্যিক experiment প্ল্যাটফর্ম এমন পদ্ধতি ব্যবহার করে বলে জানা যায়। দাম সাধারণত একটি: নির্দিষ্ট n-এ fixed horizon-এর চেয়ে power কিছুটা কম। কোনটি ভালো, তা দলের অভ্যাসের ওপর নির্ভর করে, আর এ নিয়ে অনুশীলনকারীদের মধ্যে মতভেদ আছে।

৭ · Randomisation-এর একক, interference আর design effect

সাধারণ z-test ধরে নেয়, প্রতিটি গ্রাহকের ফল স্বাধীন, আর একজনের দল আরেকজনের ফল বদলায় না। দ্বিতীয় শর্তের নাম Rubin-এর SUTVA (stable unit treatment value assumption); ভাঙলে বলে interference. বাজারঘরের মতো দোকানে এটি সহজেই ভাঙে: একই এলাকার courier, একই পণ্যের মজুত, একই ক্রেতার জন্য প্রতিযোগী বিক্রেতা। তখন randomisation-এর একককে বড় করতে হয়: এক zone বা এক পণ্য-শ্রেণির সবাই একসঙ্গে এক দলে। একে বলে cluster randomisation.

প্রশ্ন একটাই: একজনের দল কি আরেকজনের ফল বদলাতে পারে?
Experiment
Interference-এর পথ
একক আর দাম
  1. গ্রাহক ধরে। Checkout পাতার পরিবর্তন একজনের অভিজ্ঞতা, আরেকজনের নয়। login করা গ্রাহকের ID-তে hash করে দল ঠিক করুন, যাতে তিনি যতবারই আসুন একই রূপ দেখেন।
  2. Page view ধরে নয়। প্রতিটি page view-এ মুদ্রা ছুড়লে একই গ্রাহক কখনো A, কখনো B দেখেন; প্রভাব মিশে যায়, আর একজনের অনেক view স্বাধীন নমুনা নয়।
  3. Zone ধরে। নতুন slot-নিয়ম treatment গ্রাহকদের দ্রুত slot দিলে একই courier-দের control গ্রাহকেরা পান কম। তুলনাটি তখন “নতুন বনাম পুরোনো” নয়, “নতুন বনাম ক্ষতিগ্রস্ত পুরোনো”। পুরো zone এক দলে দিলে এই ছিদ্র বন্ধ, দাম design effect.
  4. পণ্য-শ্রেণি ধরে। একই শ্রেণির বিক্রেতারা একই ক্রেতা টানেন। শ্রেণি ধরে ভাগ করলে প্রতিযোগিতার প্রভাব এক দলের ভেতরে থাকে, কিন্তু cluster কম বলে power কম; সময় ধরে পালাবদল (switchback) আরেকটি বিকল্প, যার নিজস্ব অনুমান আছে।
  5. নিয়ম। একক বাছুন সেই স্তরে, যেখানে প্রভাব ছড়ানো থামে। আর বিশ্লেষণ করুন সেই এককেই: zone ধরে ভাগ করে গ্রাহক ধরে সাধারণ z-test করলে ভুল “জয়” অনেক বাড়ে (অংশ ৮-এর ব্লক)।

একই zone-এর গ্রাহকেরা একে অপরের মতো: একই courier, একই রাস্তা, কাছাকাছি আয়। এই মিলের মাপ intraclass correlation (ICC, ρ): একই cluster-এর দুজনের ফলের correlation. Leslie Kish-এর ১৯৬৫ সালের Survey Sampling বইয়ে প্রচলিত design effect:

DEFF = 1 + (m − 1) ρ

m প্রতিটি cluster-এর গড় আকার। Cluster randomisation-এ variance সাধারণ হিসাবের DEFF গুণ, তাই একই power-এর জন্য n-ও DEFF গুণ লাগে; উল্টো দিক থেকে, N জন গ্রাহক আসলে প্রায় N/DEFF জন স্বাধীন গ্রাহকের সমান তথ্য দেয়। ρ ছোট দেখালেও m বড় হলে DEFF বিশাল: ρ = ০.০১, m = ৪০০ হলে ১ + ৩৯৯ × ০.০১ = ৪.৯৯; ρ = ০.০০১ হলে ১.৪০. তাই cluster-এ ভাগ করলে সংখ্যা আর আকার দুটিই জরুরি: ৮০টি zone, প্রতিটিতে ৪০০ জনের চেয়ে ৪০০টি zone, প্রতিটিতে ৮০ জন অনেক বেশি তথ্য দেয়।

৮ · বিশ্বাসের পরীক্ষা — sample ratio mismatch আর A/A test

ফল দেখার আগে প্রশ্ন: যন্ত্রটি ঠিক চলেছে তো? দুটি পরীক্ষা প্রায় সব অভিজ্ঞ experiment দলই চালায়।

Sample ratio mismatch (SRM): পরিকল্পনা ৫০/৫০, কিন্তু দলের আকার এলোমেলোতার চেয়ে বেশি আলাদা। কারণ প্রায়ই প্রযুক্তিগত: treatment পাতা ধীরে খোলে বলে কিছু গ্রাহক log হওয়ার আগেই চলে যান, bot একটি দলে বেশি পড়ে, redirect-এ কিছু হারায়। যে কারণেই হোক, দুই দল তখন আর তুলনীয় নয়, আর বাকি সব বিশ্লেষণ অর্থহীন। যাচাই chi-square goodness-of-fit test দিয়ে: χ² = ∑ (দেখা − প্রত্যাশিত)² ÷ প্রত্যাশিত, ১ df. সাধারণ test-এর চেয়ে কড়া সীমা (যেমন p < ০.০০১) ব্যবহারের পরামর্শ দেওয়া হয়, কারণ SRM check অনেক test-এ বারবার চলে।

A/A test: দুই দলেই একই পুরোনো ব্যবস্থা। পুরো যন্ত্র (ভাগ করা, log, metric-এর হিসাব, test) সৎ হলে অনেক A/A test-এ p-value সমভাবে ০ থেকে ১-এ ছড়ায়, আর মাত্র প্রায় ৫% significant হয়। এর বেশি হলে কোথাও ভুল: SRM, ভুল variance, বা অংশ ৭-এর মতো একক আর বিশ্লেষণের অমিল।

নিচের ব্লক প্রথমে একটি SRM check করে। তারপর ৮০টি zone-এর ৪,০০০টি A/A test চালায়, যেখানে প্রতিটি zone-এর নিজস্ব conversion হার Beta বণ্টন থেকে আসে (গড় ০.০৫, ICC ০.০১): একবার গ্রাহক ধরে ভাগ, একবার zone ধরে ভাগ, আর দ্বিতীয়টি সাধারণ z-test দিয়ে ও design effect দিয়ে সংশোধন করে।

import numpy as np
from scipy.stats import chisquare, norm

obs = np.array([31512, 30612])                     # control, treatment: পরিকল্পনা 50/50
chi2, p_srm = chisquare(obs, f_exp=[obs.sum() / 2] * 2)
print(f"SRM: treatment-এর ভাগ {obs[1] / obs.sum():.4f}, chi2 = {chi2:.2f}, p = {p_srm:.1e}")

rng = np.random.default_rng(62)
Z, m, rho, sims = 80, 400, 0.01, 4000              # 80টি zone, প্রতিটিতে 400 গ্রাহক, ICC 0.01
a, b = 0.05 * (1 / rho - 1), 0.95 * (1 / rho - 1)  # Beta(a, b): গড় 0.05, ICC = 1/(a+b+1)
deff = 1 + (m - 1) * rho
print(f"design effect = 1 + ({m} − 1) × {rho} = {deff:.2f}; "
      f"{Z * m:,} জন ≈ {Z * m / deff:,.0f} জন স্বাধীন গ্রাহকের সমান তথ্য")

def ztest(x1, n1, x2, n2):
    pool = (x1 + x2) / (n1 + n2)
    return (x2 / n2 - x1 / n1) / np.sqrt(pool * (1 - pool) * (1 / n1 + 1 / n2))

pz = rng.beta(a, b, (sims, Z))                     # A/A: প্রতিটি zone-এর নিজস্ব হার
nt = rng.binomial(m, 0.5, (sims, Z))               # গ্রাহক ধরে: zone-এর ভেতরে অর্ধেক-অর্ধেক
xt, xc = rng.binomial(nt, pz), rng.binomial(m - nt, pz)
z_cust = ztest(xc.sum(1), (m - nt).sum(1), xt.sum(1), nt.sum(1))

conv = rng.binomial(m, pz)                         # zone ধরে: অর্ধেক zone পুরোটা treatment
tz = rng.random((sims, Z)).argsort(1) < Z // 2
z_zone = ztest((conv * ~tz).sum(1), m * Z // 2, (conv * tz).sum(1), m * Z // 2)

p_cust = 2 * norm.sf(np.abs(z_cust))
print("A/A, গ্রাহক ধরে: p < 0.05 হার", np.mean(p_cust < 0.05).round(3))
print("   p-value-এর দশ ভাগে সংখ্যা:", np.histogram(p_cust, bins=10, range=(0, 1))[0])
print("A/A, zone ধরে, সাধারণ z-test: p < 0.05 হার", np.mean(np.abs(z_zone) > 1.96).round(3))
print("A/A, zone ধরে, z ÷ √deff:     p < 0.05 হার",
      np.mean(np.abs(z_zone) / np.sqrt(deff) > 1.96).round(3))

আসল আউটপুট (ব্রাউজারের Python):

SRM: treatment-এর ভাগ 0.4928, chi2 = 13.04, p = 3.1e-04
design effect = 1 + (400 − 1) × 0.01 = 4.99; 32,000 জন ≈ 6,413 জন স্বাধীন গ্রাহকের সমান তথ্য
A/A, গ্রাহক ধরে: p < 0.05 হার 0.042
   p-value-এর দশ ভাগে সংখ্যা: [367 419 422 411 417 370 396 372 420 406]
A/A, zone ধরে, সাধারণ z-test: p < 0.05 হার 0.382
A/A, zone ধরে, z ÷ √deff:     p < 0.05 হার 0.052

SRM: ৬২,১২৪ জনের মধ্যে treatment ৪৯.২৮%; মাত্র ০.৭২ শতাংশ বিন্দুর ঘাটতি, তবু p = ০.০০০৩১, সীমা ০.০০১-এর নিচে। এই test-এর conversion ফল দেখার আগে কারণ খুঁজতে হবে। গ্রাহক ধরে A/A: ৪.২% significant, আর p-value-এর দশটি ভাগে প্রায় সমান (৩৬৭ থেকে ৪২২, প্রত্যাশিত ৪০০): যন্ত্র সৎ। Zone ধরে, সাধারণ z-test: ৩৮.২%! কোনো আসল পার্থক্য নেই, তবু প্রায় তিনটিতে একটি “জয়”, কারণ z-test ৩২,০০০ জনকে স্বাধীন ধরেছে, অথচ তথ্য আসলে প্রায় ৬,৪১৩ জনের। z-কে √DEFF দিয়ে ভাগ করলে ৫.২%, আবার ঠিক। বাস্তবে ρ অজানা, তাই zone-স্তরের গড় তুলনা বা cluster-robust standard error দিয়ে বিশ্লেষণ করা হয় (পর্ব ৬৩ আর ৬৪)।

৯ · কত দিন চালাবেন — novelty, সাপ্তাহিক ছন্দ আর লিখিত test plan

n থেকে দিন বের হয়, কিন্তু দিনের সংখ্যায় আরও তিনটি বিষয় ঢোকে:

  • সাপ্তাহিক ছন্দ: বাজারঘরে শুক্রবার আর মাসের শুরুতে (বেতনের পর) ক্রেতার মিশ্রণ আলাদা হতে পারে। তাই অন্তত এক পুরো সপ্তাহ, আর সম্ভব হলে পুরো সপ্তাহের গুণিতকে চালান, যাতে প্রতিটি বার সমান সংখ্যায় থাকে। ঈদের আগের সপ্তাহের মতো অস্বাভাবিক সময়ে চালানো test-এর ফল সারা বছরের জন্য না-ও খাটতে পারে।
  • Novelty আর primacy: নতুন কিছু দেখে নিয়মিত ক্রেতারা কয়েক দিন বেশি ক্লিক করতে পারেন (novelty effect), যা পরে মিলিয়ে যায়। উল্টোটিও হয়: অভ্যাস ভাঙায় প্রথমে খারাপ, পরে ভালো (primacy effect). ধরার উপায়: প্রভাবকে দিন ধরে আঁকা, আর নতুন বনাম পুরোনো গ্রাহক আলাদা দেখা। সন্দেহ থাকলে একটি ছোট দলকে দীর্ঘদিন পুরোনো ব্যবস্থায় রাখা (holdout) সাহায্য করে।
  • সর্বোচ্চ সময়: খুব লম্বা test-এ cookie মুছে যাওয়া, গ্রাহকের যন্ত্র বদল আর অন্য test-এর সঙ্গে সংঘর্ষ বাড়ে। n খুব বড় লাগলে MDE বা metric নতুন করে ভাবুন।

শেষ ধাপ সব সিদ্ধান্ত চালুর আগে লেখা, একটি pre-registration: যাতে ফল দেখে metric, segment বা থামার দিন বদলানোর সুযোগ না থাকে (পর্ব ০৪-এর forking paths). বাজারঘরের checkout test-এর জন্য একটি ছাঁচ:

বাজারঘর test plan · checkout-delivery-days · v1 (চালুর আগে লেখা)
Hypothesis   : delivery-র আনুমানিক দিন দেখালে checkout conversion বাড়বে
Primary      : checkout শুরু করা গ্রাহকের মধ্যে অর্ডার শেষের হার (baseline 5.0%)
Guardrails   : বাতিলের হার, ফেরতের হার, পাতা খোলার p95 সময়, অভিযোগ
Unit         : login করা গ্রাহক (ID hash), ভাগ 50/50; বিশ্লেষণও গ্রাহক ধরে
MDE, α, power: 10% আপেক্ষিক (5.0% → 5.5%), 0.05 দুই দিকে, 80%
Sample size  : প্রতি দলে 31,234; দিনে ~4,500 → 14 দিন (দুই পুরো সপ্তাহ)
Looks        : শুধু দিন 14-এ সিদ্ধান্ত; রোজ শুধু SRM আর guardrail
Checks       : SRM chi-square p < 0.001 হলে বিশ্লেষণ বন্ধ; আগের A/A test পাস
Analysis     : দুই অনুপাতের z-test আর 95% CI; segment শুধু নতুন/পুরোনো গ্রাহক
Decision     : CI পুরোটা 0-এর ওপরে আর guardrail ঠিক থাকলে চালু; নইলে চালু নয়

এই ছাঁচ একটি উদাহরণ, কোনো মানদণ্ড নয়। মূল কথা: প্রতিটি লাইন ফল দেখার আগে লেখা, তারিখসহ, আর বদলালে কেন বদলালেন তাও লেখা।

আরও গভীরে: Group-sequential পদ্ধতির পূর্ণ গণিত Jennison আর Turnbull-এর বই Group Sequential Methods with Applications to Clinical Trials (২০০০)-এ। Cluster randomisation-এ বিশ্লেষণের দিক (mixed model, GEE, cluster-robust SE) আর interference-এর আধুনিক কাজ, যেমন marketplace-এ switchback ও দুই-পক্ষের randomisation, দ্রুত বদলাচ্ছে; কাজে লাগানোর আগে সাম্প্রতিক প্রবন্ধ দেখুন। আর থামার নিয়ম ছাড়াও Bayesian পদ্ধতিতে prior-এর ওপর নির্ভরতা আছে: “Bayesian হলে peeking সমস্যা নেই” দাবিটি বিতর্কিত, কারণ বারবার দেখলে frequentist ভুলের হার সেখানেও বাড়ে।

পরীক্ষা ও ইন্টারভিউতে:

  • p = ০.০৫, আপেক্ষিক MDE ১০%, α = ০.০৫, power ৮০%: প্রতি দলে n? প্রায় ৩১,২৩৪ (Lehr-এর নিয়মে প্রায় ৩১,৮০০)।
  • MDE অর্ধেক করলে n? প্রায় চার গুণ, কারণ n ∝ ১/δ².
  • রোজ দেখে প্রথম significant ফলে থামলে সমস্যা কী? Type I error ৫% ছাড়িয়ে যায়; ১৪ বার দেখলে প্রায় ২০% বা বেশি। সমাধান fixed horizon বা group-sequential সীমা।
  • ৫০,৬০০ বনাম ৪৯,৪০০, পরিকল্পনা ৫০/৫০: SRM? χ² = ২ × ৬০০² ÷ ৫০,০০০ = ১৪.৪, p ≈ ০.০০০১৫; হ্যাঁ, কারণ খুঁজুন।
  • Cluster-এ m = ১০০, ICC = ০.০২: design effect? ১ + ৯৯ × ০.০২ = ২.৯৮.

১০ · AI-এর যুগে — AI-এর বানানো test plan আর power হিসাব যাচাই

“আমার A/B test-এর sample size বলো” লিখলে AI assistant মুহূর্তে একটি সংখ্যা দেয়। সমস্যা হলো সংখ্যাটির পেছনের অনুমান প্রায়ই অদৃশ্য:

AI প্রায়ই যা করেকেন ভুলকীভাবে ধরবেন
আপেক্ষিক আর পরম MDE গুলিয়ে ফেলে (“১০%” মানে ৫% → ১৫%?)n শত গুণ পর্যন্ত বদলায়p1 আর p2 দুটিই লিখিয়ে নিন, হাতে মেলান
মোট n-কে প্রতি দলের n বলে, বা উল্টোদিনের হিসাব দ্বিগুণ ভুল“per arm” কি না জিজ্ঞেস করুন; statsmodels-এ nobs1 প্রতি দলের
ফাংশনের বানানো argument বা ভুল effect size (অনুপাত সরাসরি পাঠানো)statsmodels Cohen's h বা d চায়; ভুল ইনপুটে নীরবে ভুল উত্তরনিজের version-এর docs; জানা উদাহরণে (৩১,২৩৪) চালিয়ে দেখুন
Revenue metric-এ conversion-এর সূত্র বসায়, বা SD আন্দাজ করেভারী লেজে n অনেক বড় লাগেআগের ডেটা থেকে SD মাপুন, capping লিখে রাখুন
“Significant হলেই থামুন” পরামর্শ দেয়Peeking: ভুল “জয়” কয়েক গুণথামার নিয়ম আগে লিখুন; A/A simulation চালান
Cluster-এ ভাগ করে গ্রাহক ধরে বিশ্লেষণ করেSE ছোট; এই পর্বের zone-উদাহরণে A/A-তে ভুল ৩৮%একক মেলান; design effect হিসাব করুন

প্রম্পটে সংখ্যা আর কাঠামো দিন, আসল গ্রাহকের ডেটা নয়; উত্তরের সঙ্গে যাচাইয়ের কোড চেয়ে নিন।

আমি একটি অনলাইন দোকানের checkout পাতায় A/B test পরিকল্পনা করছি। Baseline conversion 5%, আপেক্ষিক MDE 10% (মানে 5% → 5.5%), দুই দিকের α = 0.05, power 80%, দুই দল সমান। দিনে প্রায় 4,500 জন checkout-এ আসেন। ১. Normal approximation-এর সূত্র লিখে প্রতি দলের n হাতে হিসাব করো, প্রতিটি ধাপ দেখিয়ে। ২. একই হিসাব statsmodels-এর বর্তমান version-এ (NormalIndPower আর proportion_effectsize) দাও, আর বলো কোন argument প্রতি দলের n. ৩. একটি simulation লেখো যা এই n-এ আসল 10% উন্নতিতে power আর A/A-তে Type I error মাপে। ৪. প্রতিদিন ফল দেখতে চাইলে কী করা উচিত, আর কোন অনুমান তুমি নিশ্চিত নও তা স্পষ্ট বলো।

AI-এর দেওয়া test plan হাতে পেলে — যাচাইয়ের চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১ক্লিনিক্যাল ট্রায়াল কেন group-sequential পদ্ধতি বানাল, আর অনলাইন দোকানের জন্য তা কতটা খাটে?

ওষুধের ট্রায়ালে রোগীরা ধীরে ধীরে যোগ দেন, ফল জমতে মাস বা বছর লাগে। নতুন ওষুধ যদি স্পষ্ট ক্ষতিকর হয়, শেষ পর্যন্ত অপেক্ষা করা অনৈতিক; যদি স্পষ্ট উপকারী হয়, control দলকে বঞ্চিত রাখাও অনৈতিক। তাই মাঝপথে দেখার প্রয়োজন ছিল বাস্তব। কিন্তু ১৯৬৯ সালে Armitage, McPherson আর Rowe হিসাব করে দেখান, প্রতিবার সাধারণ ৫% সীমায় test করলে ভুল আবিষ্কারের হার কয়েক গুণ বাড়ে। সমাধান খুঁজতে গিয়ে Pocock (১৯৭৭) সমান সীমা আর O'Brien ও Fleming (১৯৭৯) শুরুতে কড়া সীমা প্রস্তাব করেন; Lan আর DeMets (১৯৮৩) alpha spending দিয়ে দেখার সময় নমনীয় করেন। ট্রায়ালে এখন সাধারণত একটি স্বাধীন data monitoring committee আগে থেকে লেখা নিয়মে অন্তর্বর্তী ফল দেখে।

অনলাইন দোকানে পরিস্থিতি কিছুটা আলাদা। ডেটা আসে দ্রুত, test অনেক, আর “ক্ষতি” সাধারণত টাকার, জীবনের নয়। তবু মূল সমস্যা একই: dashboard রোজ p-value দেখালে মানুষ প্রথম সবুজ সংকেতে থামতে চায়। এই পর্বের simulation-এ ১৪ বার দেখায় ভুল প্রায় ২৩%. O'Brien–Fleming ধাঁচের সীমা প্রায় একই power রেখে গড়ে কয়েক দিন আগে থামতে দেয়, যা বড় ক্ষতির পরিবর্তন দ্রুত বন্ধ করতেও কাজে লাগে।

তবে আগে থামারও দাম আছে: আগে থামা test-এর প্রভাবের অনুমান প্রায়ই বড় হয়, কারণ এলোমেলোতায় বড় দেখানো মুহূর্তেই থামা হয়। আর সাপ্তাহিক ছন্দের মাঝপথে থামলে ফল এক ধরনের দিনের প্রতিনিধি হয়। তাই অনেক দল দেখার দিন সপ্তাহের শেষে রাখে।

মূল উপলব্ধি: মাঝপথে দেখা অপরাধ নয়; অপরাধ হলো দেখার নিয়ম না লিখে, সাধারণ ৫% সীমায় দেখে থামা।

প্র ০২α = ০.০৫ আর power ৮০% কোথা থেকে এল, আর বাজারঘরের জন্য অন্য সংখ্যা কি যুক্তিসংগত?

০.০৫-এর জনপ্রিয়তার পেছনে বড় ভূমিকা Ronald Fisher-এর; ১৯২৫ সালের Statistical Methods for Research Workers-এ তিনি এই সীমাকে সুবিধাজনক বলে উল্লেখ করেন, তবে তিনি নিজে একে যান্ত্রিক নিয়ম হিসেবে দেখেননি বলে অনেক ইতিহাসবিদ মনে করেন। Jerzy Neyman আর Egon Pearson ১৯৩৩ সালে দুই রকম ভুলের কাঠামো দেন, যেখান থেকে power-এর ধারণা আসে। ৮০% power-এর প্রচলন অনেকটা Jacob Cohen-এর; তাঁর power analysis-এর বইয়ে (দ্বিতীয় সংস্করণ ১৯৮৮) তিনি প্রস্তাব করেন, অন্য যুক্তি না থাকলে β = ০.২০, কারণ সাধারণত Type I error-কে Type II-এর চেয়ে প্রায় চার গুণ গুরুতর ধরা হয়।

কিন্তু এগুলো প্রথা, প্রাকৃতিক নিয়ম নয়। বাজারঘরের প্রশ্ন হলো দুই ভুলের দাম কত। একটি ছোট লেখা বদলের test-এ ভুল চালুর ক্ষতি সামান্য আর সহজে ফেরানো যায়, কিন্তু একটি ভালো পরিবর্তন মিস করা ব্যয়বহুল; সেখানে α = ০.১০ আর power ৯০% যুক্তিসংগত হতে পারে। উল্টো দিকে, দাম-নীতি বা ফেরত-নীতির বড় পরিবর্তন সহজে ফেরানো যায় না আর গ্রাহকের আস্থায় আঘাত দেয়; সেখানে কড়া α আর বড় n ভালো।

আরেকটি দিক: কম power-এর test শুধু প্রভাব মিসই করে না, যা “ধরে” তার আকারও বাড়িয়ে বলে (Gelman ও Carlin-এর Type M error). তাই কম traffic-এর দোকানে সবচেয়ে বড় ভুল প্রায়ই α নয়, বরং ১০,০০০ জনের test-এ ৫% উন্নতি খোঁজা। সংখ্যাগুলো যা-ই হোক, চালুর আগে কারণসহ লিখে রাখাই আসল নিয়ম।

মূল উপলব্ধি: α আর power হলো দুই ভুলের দামের ব্যবসায়িক সিদ্ধান্ত; ০.০৫ আর ৮০% একটি যুক্তিসংগত শুরু, কিন্তু চিন্তা বাদ দেওয়ার অজুহাত নয়।

প্র ০৩Marketplace-এ interference কেন এত কঠিন সমস্যা, আর cluster randomisation কী দাম দিয়ে তা সমাধান করে?

Donald Rubin ১৯৮০ সালের দিকে SUTVA শর্তটির নাম দেন: একজনের ফল শুধু তাঁর নিজের treatment-এর ওপর নির্ভর করে। টিকার গবেষণায় এটি বহু আগে থেকেই ভাঙা জানা ছিল: অন্যরা টিকা নিলে আপনার সংক্রমণের ঝুঁকিও কমে। দুই-পক্ষের বাজারে (ক্রেতা আর বিক্রেতা, বা গ্রাহক আর courier) সমস্যাটি আরও তীব্র, কারণ সবাই একই সীমিত সম্পদের জন্য প্রতিযোগিতা করে।

একটি উদাহরণ ভাবুন: বাজারঘর treatment গ্রাহকদের দ্রুত delivery slot আগে দেখায়। Treatment দল সেই slot বেশি নেয়, ফলে একই zone-এর control গ্রাহকেরা কম পান আর তাঁদের conversion কমে। Test তখন দুই দিক থেকে পার্থক্য বাড়িয়ে দেখায়: treatment ভালো হলো, control খারাপ হলো। পুরো দোকানে চালু করলে সবাই একই slot-এর জন্য লড়বে, আর সেই কল্পিত বড় লাভ আসবে না। এই ধরনের পক্ষপাত test-এর n বাড়িয়ে দূর হয় না।

Cluster randomisation পুরো zone একসঙ্গে এক দলে দিয়ে ছিদ্রটি বন্ধ করে। দাম design effect: Leslie Kish-এর সূত্রে ১ + (m − ১)ρ. এই পর্বের উদাহরণে ৮০টি zone-এ ৪.৯৯ গুণ variance, মানে ৩২,০০০ জনের তথ্য প্রায় ৬,৪০০ জনের সমান। আর cluster কম হলে আরেক সমস্যা: এলোমেলো ভাগেও দুই দলের zone-গুলো ধনী-গরিব বা শহর-গ্রামে অসম হতে পারে, তাই stratification বা জোড়া মিলিয়ে ভাগ (matched pairs) কাজে লাগে। বিকল্প হিসেবে সময় ধরে পালাবদল (switchback) কিছু marketplace-এ ব্যবহার হয় বলে জানা যায়, তবে সেখানে আগের সময়ের প্রভাব পরের সময়ে গড়ানোর (carryover) নতুন সমস্যা আসে।

মূল উপলব্ধি: Interference-এ পক্ষপাত আর cluster-এ variance: এই দুইয়ের মধ্যে বাছাই করতে হয়, আর বিশ্লেষণকে সবসময় randomisation-এর এককের সঙ্গে মেলাতে হয়।

প্র ০৪অভিজ্ঞ experiment দলগুলো কেন A/A test আর SRM check-কে এত গুরুত্ব দেয়?

একটি A/B test-এর ফল বিশ্বাস করতে হলে শুধু পরিসংখ্যান নয়, পুরো প্রকৌশল ঠিক থাকতে হয়: কে কোন দলে গেল, কোন ঘটনা log হলো, কোন গ্রাহক bot বলে বাদ পড়ল, metric কীভাবে গোনা হলো। এর যেকোনো জায়গায় একটি ছোট ভুল দুই দলকে অসম করে দিতে পারে, আর তখন সবচেয়ে সুন্দর p-value-ও অর্থহীন।

Ron Kohavi, Diane Tang আর Ya Xu তাঁদের ২০২০ সালের বই Trustworthy Online Controlled Experiments-এ বড় প্রযুক্তি প্রতিষ্ঠানের অভিজ্ঞতা থেকে এমন অনেক উদাহরণ দেন, আর Twyman's law নামে একটি অনানুষ্ঠানিক নিয়ম জনপ্রিয় করেন: যে ফল খুব আকর্ষণীয় বা অস্বাভাবিক দেখায়, সেটি সম্ভবত ভুল। তাঁদের পরামর্শ, প্রতিটি test-এ স্বয়ংক্রিয় SRM check, আর নতুন ব্যবস্থা বা নতুন metric-এ আগে A/A test. Fabijan ও সহকর্মীদের ২০১৯ সালের একটি প্রবন্ধ SRM-এর সাধারণ কারণগুলোর একটি শ্রেণিবিভাগ দেয়: ভাগ করার ভুল, চালানোর সময়ের ভুল (যেমন redirect), log-এর ভুল, আর বিশ্লেষণের সময় ভুল ছাঁকনি।

SRM ধরার জন্য সাধারণ ০.০৫-এর চেয়ে কড়া সীমা কেন? কারণ প্রতিটি test-এ, প্রতিদিন SRM check চলে; ০.০৫-এ বহু মিথ্যা সতর্কবার্তা আসবে আর দল সেগুলো উপেক্ষা করতে শিখবে। আর A/A test শুধু “৫% significant” দেখে না, পুরো p-value-এর বণ্টন সমান কি না দেখে; এই পর্বের zone-উদাহরণে ৩৮% ভুল “জয়” A/A ছাড়া ধরা কঠিন ছিল, কারণ প্রতিটি একক test দেখতে স্বাভাবিক।

মূল উপলব্ধি: A/A আর SRM হলো যন্ত্রের নিজের test; এগুলো পাস না করলে A/B-এর ফল পড়াই উচিত নয়।

প্র ০৫Pre-registration কি শুধু বিজ্ঞানীদের আনুষ্ঠানিকতা, নাকি একটি ছোট product দলেরও কাজে লাগে?

চিকিৎসা-গবেষণায় দেখা গিয়েছিল, নেতিবাচক ট্রায়াল প্রায়ই প্রকাশিত হয় না, আর প্রকাশিত ট্রায়ালে কখনো মূল ফলের metric চুপচাপ বদলানো হয়। এর প্রতিক্রিয়ায় জার্নাল সম্পাদকদের আন্তর্জাতিক কমিটি (ICMJE) ২০০৪ সালে ঘোষণা করে, ২০০৫ সাল থেকে শুরু হওয়া ট্রায়াল প্রকাশ করতে হলে আগে নিবন্ধন লাগবে। মনোবিজ্ঞানে replication সংকটের পর Andrew Gelman আর Eric Loken “garden of forking paths” নামে দেখান, গবেষক সৎ থাকলেও ডেটা দেখে নেওয়া ছোট ছোট সিদ্ধান্ত (কোন segment, কোন সময়, কোন outlier বাদ) মিলে ভুল আবিষ্কারের হার বাড়ায়।

একটি product দলে একই চাপ আরও বেশি: দল চায় পরিবর্তনটি জিতুক, হাতে অনেক metric আর segment, আর dashboard যেকোনো সময় খোলা। test plan লিখে রাখলে তিনটি লাভ। প্রথমত, primary metric আর থামার দিন বাঁধা থাকে, তাই peeking আর metric-বদল বন্ধ হয়। দ্বিতীয়ত, power-এর হিসাব আগে হয়, তাই অসম্ভব ছোট প্রভাব খোঁজার test শুরুই হয় না। তৃতীয়ত, পরে কেউ ফল পর্যালোচনা করলে দেখতে পারেন কোনটি পরিকল্পিত আর কোনটি পরে খোঁজা (exploratory).

তবে কঠোরতার একটি সীমা আছে। Exploratory বিশ্লেষণ নিষিদ্ধ নয়; শুধু তাকে নতুন hypothesis হিসেবে চিহ্নিত করে পরের test-এ যাচাই করতে হয়। আর পরিকল্পনা বদলানোও চলে, যদি ফল দেখার আগে বদলানো হয় আর কারণসহ লেখা থাকে। Bayesian বা সবসময় বৈধ পদ্ধতিও লিখিত পরিকল্পনার বিকল্প নয়: prior আর সিদ্ধান্তের নিয়ম সেখানেও আগে ঠিক করতে হয়।

মূল উপলব্ধি: এক পাতার test plan সবচেয়ে সস্তা পরিসংখ্যানিক সুরক্ষা; এটি peeking, metric-বদল আর segment-শিকার একসঙ্গে ঠেকায়।

অনুশীলন

হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। একটির জন্য কোড লেখা লাগবে।

  1. হাতের হিসাব: (ক) Baseline ১০%, আপেক্ষিক MDE ২০%, α = ০.০৫ দুই দিকে, power ৮০%: প্রতি দলে n কত? দিনে মোট ১,০০০ জন এলে কত দিন চালাবেন? (খ) Cluster-এর গড় আকার ২০০, ICC ০.০২: design effect কত, আর ৪০,০০০ জন কত জন স্বাধীন গ্রাহকের সমান? (গ) OBF ধাঁচের alpha spending ফাংশন 2 − 2Φ(1.96/√t)-এ অর্ধেক তথ্যে কত α খরচ হয়?

    (ক) p2 = ০.১২, p̄ = ০.১১. ১.৯৬০ × √(২ × ০.১১ × ০.৮৯) = ১.৯৬০ × ০.৪৪২৫ = ০.৮৬৭৩; ০.৮৪২ × √(০.০৯ + ০.১০৫৬) = ০.৮৪২ × ০.৪৪২৩ = ০.৩৭২২. যোগ ১.২৩৯৫, বর্গ ১.৫৩৬৪, ÷ ০.০২² = ৩,৮৪১ (অংশ ৩-এর টেবিলের সমান)। দুই দলে ৭,৬৮২ জন, দিনে ১,০০০ জনে ৭.৭ দিন; পুরো সপ্তাহে গোল করলে ১৪ দিন, কারণ ৭ দিনে n পূর্ণ হয় না।

    (খ) DEFF = ১ + ১৯৯ × ০.০২ = ৪.৯৮; ৪০,০০০ ÷ ৪.৯৮ ≈ ৮,০৩২ জন।

    (গ) 1.96/√0.5 = ২.৭৭২; ২ − ২Φ(২.৭৭২) ≈ ০.০০৫৬. মোট ০.০৫-এর মাত্র প্রায় ১১%.

  2. সমালোচনা ও নকশা: একটি দলের পরিকল্পনা: “নতুন ‘বিনামূল্যে delivery’ সীমা (৳৯৯৯-এর বদলে ৳৭৯৯) test করব। প্রতিটি অর্ডার এলোমেলোভাবে A বা B. Metric: conversion, গড় অর্ডার মূল্য, ফেরতের হার, বাতিলের হার আর আয়; যেটিতে significant উন্নতি আসবে, সেটি রিপোর্ট করব। রোজ দেখব, p < ০.০৫ হলেই থামব। Sample size লাগবে না, যথেষ্ট ডেটা আসা পর্যন্ত চালাব।” সমস্যাগুলো কী, আর কীভাবে নতুন করে সাজাবেন?

    সমস্যা: (১) একক “অর্ডার”: একই গ্রাহক দুই সীমা দেখতে পারেন, আর যিনি অর্ডার করেননি তিনি গোনাতেই নেই, তাই conversion মাপাই যায় না। (২) Primary metric নেই; পাঁচটির যেটি জেতে সেটি রিপোর্ট মানে একাধিক তুলনা। (৩) রোজ দেখে থামা: peeking. (৪) Sample size আর MDE নেই, তাই power অজানা। (৫) Guardrail নেই: delivery খরচ আর লাভের হার। (৬) আয়ের মতো ভারী লেজের metric-এর কোনো পরিকল্পনা নেই।

    নতুন নকশা: একক login করা গ্রাহক; primary metric প্রতি দর্শকে লাভ বা conversion, একটিই, কারণসহ; guardrail delivery খরচ, ফেরত আর বাতিল; আগের ডেটা থেকে SD মেপে MDE আর n; পুরো সপ্তাহের গুণিতকে দিন; একটি থামার দিন বা আগে লেখা OBF সীমা; রোজ শুধু SRM আর guardrail; আয় ৯৯তম percentile-এ কাটা; আগে A/A; সব একটি তারিখসহ test plan-এ।

  3. আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড কী ছাপবে (কাছাকাছি পূর্ণসংখ্যা হলেই চলবে)।
    from scipy.stats import norm
    z = norm.ppf(0.975) + norm.ppf(0.8)
    for sd, d in [(1, 0.1), (1, 0.05), (2, 0.1)]:
        print(round(2 * z**2 * sd**2 / d**2))
    1570
    6279
    6279

    z² = (১.৯৬০ + ০.৮৪২)² = ৭.৮৪৯, তাই প্রথমটি ২ × ৭.৮৪৯ ÷ ০.০১ ≈ ১,৫৭০. δ অর্ধেক হলে n চার গুণ: ৬,২৭৯. SD দ্বিগুণ হলেও চার গুণ, ঠিক একই সংখ্যা, কারণ n নির্ভর করে শুধু σ/δ অনুপাতের ওপর। আউটপুট একটি ডেস্কটপ run-এ যাচাই করা।

  4. কোড লিখুন: একটি ফাংশন mde_for_n(n, p1) লিখুন, যা প্রতি দলের n আর baseline p1 নিয়ে ৮০% power-এ ধরা যায় এমন সবচেয়ে ছোট আপেক্ষিক উন্নতি দেয়। n = ৫,০০০, ২০,০০০ আর ৩১,২৩৪-এ p1 = ০.০৫ দিয়ে চালান, আর statsmodels-এর NormalIndPower().solve_power(nobs1=n, ...)-এর effect size-এর সঙ্গে মেলান।
    import numpy as np
    from scipy.stats import norm
    from scipy.optimize import brentq
    from statsmodels.stats.power import NormalIndPower
    
    def power(n, p1, p2, alpha=0.05):          # একদিকের লেজ বাদ, ছোট অংশ
        se = np.sqrt((p1 * (1 - p1) + p2 * (1 - p2)) / n)
        return norm.cdf(abs(p2 - p1) / se - norm.ppf(1 - alpha / 2))
    
    def mde_for_n(n, p1, target=0.80):
        p2 = brentq(lambda q: power(n, p1, q) - target, p1 + 1e-6, 0.99)
        return p2 / p1 - 1
    
    for n in [5000, 20000, 31234]:
        h = NormalIndPower().solve_power(nobs1=n, alpha=0.05, power=0.80)
        print(n, f"{mde_for_n(n, 0.05):.1%}", f"Cohen's h {h:.4f}")

    একটি ডেস্কটপ run-এ (Python 3.14, SciPy 1.18.1, statsmodels 0.15.0) আউটপুট:

    5000 25.9% Cohen's h 0.0560
    20000 12.6% Cohen's h 0.0280
    31234 10.0% Cohen's h 0.0224

    ৩১,২৩৪-এ ঠিক ১০%: অংশ ৩-এর হিসাব উল্টো দিক থেকে মিলল। statsmodels effect size ফেরত দেয় h হিসেবে; ৩১,২৩৪-এ ০.০২২৪, যা ৫% → ৫.৫%-এর h (০.০২২৪২)-এর সমান। h থেকে p2 = sin²(arcsin√p1 + h/2). লক্ষ করুন MDE ১/√n-এর সঙ্গে কমে: n চার গুণ (৫,০০০ থেকে ২০,০০০) হলে MDE প্রায় অর্ধেক।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি A/B test-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Hypothesis আর একটি primary metric, সঙ্গে guardrail লেখা
  2. MDE, α আর power থেকে প্রতি দলের n আর দিনের সংখ্যা বের করা
  3. Randomisation-এর একক বেছে test plan তারিখসহ জমা রাখা
  4. চালানো, আর রোজ শুধু SRM আর guardrail দেখা
  5. আগে ঠিক করা দিনে primary metric বিশ্লেষণ করে সিদ্ধান্ত লেখা

দুই অনুপাতের A/B test-এ বাকি সব এক রেখে minimum detectable effect (পরম পার্থক্য) অর্ধেক করলে প্রতি দলের প্রয়োজনীয় sample size প্রায় কত গুণ হয়?

  • ২ গুণ
  • প্রায় ১.৪ গুণ
  • প্রায় ৪ গুণ
  • বদলায় না, কারণ α আর power একই

একটি দল কোনো আসল পার্থক্য ছাড়া (A/A) ১৪ দিন ধরে প্রতিদিন জমা ডেটায় দুই অনুপাতের z-test করে, আর প্রথমবার p < 0.05 হলেই “জয়” ঘোষণা করে। এমন অনেক test-এ ভুল “জয়”-এর হার কত কাছাকাছি হবে?

  • প্রায় ৫%, কারণ প্রতিটি test-এর α = 0.05
  • প্রায় ২০%
  • প্রায় ৭০% (১৪ × ৫%)
  • ৫%-এর কম, কারণ প্রতিদিন n ছোট

পরিকল্পনা ৫০/৫০, কিন্তু control-এ ৫০,৬০০ আর treatment-এ ৪৯,৪০০ জন। Chi-square goodness-of-fit-এ χ² কত, আর কী করা উচিত?

  • ১৪.৪; SRM, ফল পড়ার আগে কারণ খোঁজা
  • ০.০১২; পার্থক্য নগণ্য, বিশ্লেষণ চালিয়ে যাওয়া
  • ৭.২; সীমান্তবর্তী, আরও কয়েক দিন চালানো
  • ১৪.৪; বড় দল থেকে ১,২০০ জন এলোমেলোভাবে বাদ দিয়ে বিশ্লেষণ

একটি test-এ ১০০টি delivery zone ধরে randomise করা হয়েছে, প্রতিটিতে গড়ে ১০০ গ্রাহক, intraclass correlation 0.02. Design effect কত, আর মোট ১০,০০০ গ্রাহক প্রায় কত জন স্বাধীন গ্রাহকের সমান?

  • ১.০০; ১০,০০০
  • ১.০২; প্রায় ৯,৮০৪
  • ৩.০০; প্রায় ৩,৩০০, কারণ zone ১০০টি
  • ২.৯৮; প্রায় ৩,৩৫৬

O'Brien–Fleming ধাঁচের group-sequential সীমা সম্পর্কে কোনটি ঠিক?

  • প্রতিটি দেখায় একই উঁচু সীমা, যেমন ২.৬
  • শুরুতে খুব কড়া, শেষে প্রায় ১.৯৬-এর কাছে, তাই power প্রায় fixed horizon-এর মতো
  • মোট Type I error α-এর চেয়ে বেশি হতে দেয়, বিনিময়ে দ্রুত থামা
  • যেকোনো সময়, যতবার খুশি দেখা যায়, কোনো পরিকল্পনা ছাড়া

৪,০০০টি A/A test-এ (দুই দলে একই ব্যবস্থা) p < 0.05 হয়েছে ১,৪৮০টিতে। সবচেয়ে সম্ভাব্য ব্যাখ্যা কোনটি?

  • যন্ত্রে বা বিশ্লেষণে ভুল, যেমন randomisation আর বিশ্লেষণের এককের অমিল
  • দুই দলের মধ্যে আসল পার্থক্য আছে
  • স্বাভাবিক, কারণ α = 0.05
  • Power বেশি বলে বেশি significant ফল

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. আগে লেখা, পরে চালু: hypothesis, একটি primary metric, guardrail, randomisation-এর একক, MDE, α, power, থামার দিন আর সিদ্ধান্তের নিয়ম, সব তারিখসহ test plan-এ।
  2. Sample size: n = [z1−α/2√(2p̄(1 − p̄)) + z1−β√(p1q1 + p2q2)]² ÷ δ²; ৫% থেকে ৫.৫%-এ প্রতি দলে ৩১,২৩৪. n ∝ ১/δ², আর ভারী লেজের metric-এ σ বড় বলে n আরও বড়।
  3. Power যাচাই: simulation-এ আসল প্রভাব বসিয়ে power, আর প্রভাব ০ রেখে Type I error মাপুন। কম power-এর “জয়” প্রভাবের আকার বাড়িয়ে বলে।
  4. Peeking: রোজ দেখে p < ০.০৫-এ থামলে ১৪ দিনে ভুল প্রায় ২৩%. Fixed horizon, বা Pocock / O'Brien–Fleming সীমা, বা alpha spending.
  5. একক আর বিশ্বাস: interference থাকলে cluster ধরে ভাগ, DEFF = ১ + (m − ১)ρ, বিশ্লেষণও সেই এককে; SRM-এর chi-square, A/A test, আর পুরো সপ্তাহের গুণিতকে চালানো।

পরের পর্বে: পর্ব ৬৩ (A/B test-এর বিশ্লেষণ — t-test, chi-square, CUPED আর ভুল ব্যাখ্যা): শেষ হওয়া test-এর ফল থেকে প্রভাব, CI আর সিদ্ধান্ত, revenue-র ভারী লেজ আর variance কমানোর কৌশল।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান