ICC 와 검정력 — Cluster Experiment 의 Effective N (Buisson Ch.10.3)

Intra-Cluster Correlation 의 정의·계산·해석, Cluster Experiment 의 Power Trade-off

Buisson (2021) Ch.10 의 power analysis 절을 자세히 정리한다. ICC 의 정의와 계산, Effective Sample Size 의 도출, Cluster 수 vs Cluster 크기의 trade-off, Permutation-based power simulation, 결정 임계값 조정 (decision threshold) 의 실무 적용을 단계별로 시연한다.

Experimentation
Causal Inference
저자

Kwangmin Kim

공개

2026년 05월 08일

1 정의

정의: Intra-Cluster Correlation (ICC)

같은 cluster 의 두 individual 의 outcome 상관 (Buisson, 2021, Ch.10).

수식:

\[ \text{ICC} = \rho = \frac{\sigma^2_{\text{between}}}{\sigma^2_{\text{between}} + \sigma^2_{\text{within}}} \]

해석:

  • ICC = 0: cluster 무관 (individuals 독립)
  • ICC = 1: cluster 가 모든 variation 설명
  • 보통 0.05 ~ 0.5

비즈니스 사례 별 ICC 범위:

분야 ICC 일반
학교 교육 0.10~0.30
병원 의료 0.05~0.20
매장 운영 0.10~0.40
콜센터 0.30~0.60 (high — strong norms)
직관 — ICC 의 비즈니스 의미

ICC 가 높으면 (예: 0.5):

  • 같은 cluster 의 individuals 가 매우 비슷
  • Cluster 의 norms·culture 강함
  • Effective N 이 작음 (cluster 수에 가까움)

ICC 가 낮으면 (예: 0.05):

  • 같은 cluster 의 individuals 도 다양
  • Effective N ≈ total N

ICC 의 결정:

  • 데이터의 cluster 구조의 강도
  • 도메인의 social norms
  • 측정 metric 의 sensitivity

→ ICC 는 cluster experiment 의 statistical efficiency 의 핵심.

2 Effective Sample Size

2.1 수식 도출

Design Effect

\[ \text{DE} = 1 + (m - 1) \cdot \text{ICC} \]

여기서 \(m\) = cluster 당 평균 individual 수.

Effective N:

\[ n_{\text{eff}} = \frac{n_{\text{total}}}{\text{DE}} = \frac{n_{\text{total}}}{1 + (m - 1) \cdot \text{ICC}} \]

해석:

  • DE = 1: ICC = 0 (cluster 무관, \(n_{\text{eff}} = n_{\text{total}}\))
  • DE = m: ICC = 1 (cluster 가 모든 정보, $n_{} = $ cluster 수)
  • 일반적: 그 사이
직관 — 콜센터 사례 계산

AirCnC:

  • \(n_{\text{total}} = 695,205\) calls
  • \(m = 69,520\) calls/center
  • ICC = 0.56

Design Effect:

\[ \text{DE} = 1 + (69519) \cdot 0.56 = 38,931 \]

Effective N:

\[ n_{\text{eff}} = 695205 / 38931 \approx 17.9 \]

해석: 695,205 calls 가 effective N 으로 18 개 unit !

→ 거의 모든 정보가 cluster level. Individual call 수 늘려도 power 미미.

2.2 Cluster 수 vs Cluster 크기

두 차원의 trade-off

같은 total N 이지만 다른 design:

Design Cluster 수 (k) Cluster 크기 (m) Total DE \(n_{\text{eff}}\) (ICC=0.5)
A 10 1,000 10,000 500.5 20
B 100 100 10,000 50.5 198
C 500 20 10,000 10.5 952
D 1,000 10 10,000 5.5 1,818
E 2,000 5 10,000 3.0 3,333

같은 total N (10,000) 이지만 Effective N 이 20 ~ 3,333 (170 배 차이).

→ Cluster 수 ↑ + Cluster 크기 ↓ = Effective N ↑.

직관 — Power 의 비즈니스 함의

분석가의 결정:

  • Total N 을 늘려도 cluster 수 안 늘면 효과 미미
  • 비용 효과적: cluster 수 ↑

콜센터 사례:

  • 10 콜센터 → 50 콜센터 (5 배)
  • \(n_{\text{eff}}\) ≈ 18 → 89 (5 배)
  • Power 크게 향상

vs:

  • 10 콜센터, calls/center 2 배 (700K → 1.4M)
  • \(n_{\text{eff}}\) ≈ 18 → 18.5 (3% 만 ↑)
  • Power 거의 변화 없음

→ Cluster 수 늘리는 것이 효과적.

3 AirCnC Power Analysis

3.1 비즈니스 제약

VP 의 결정

VP 의 요구:

  1. 실험 기간: 1 개월 (max)
  2. MDE: 0.6 CSAT
  3. Power: 가능한 최대

분석가의 도전:

  • 1 개월 = ~230,000 calls
  • 10 콜센터 = effective N ~ 18
  • 0.6 effect 검출에 필요한 power?

답: Permutation-based simulation.

직관 — 제약의 해석

VP 의 제약 = “기다림 vs 정확도” trade-off.

  • 1 개월 = 빠른 결정 우선
  • MDE 0.6 = “이 정도 효과는 검출하고 싶음”
  • Power 제한 가능 (small effect 놓쳐도 OK)

분석가가:

  • Power 50% 면 “절반 확률로 implement”
  • Power 75% 면 “3/4 확률”
  • Power 90% 면 거의 확실

비즈니스 결정:

  • 75% 면 충분 (작은 risk 수용)
  • 50% 미만이면 더 많은 cluster 필요

이 communication 이 분석가의 역할.

3.2 Permutation Power Simulation

32 permutation 실행
import numpy as np
import statsmodels.formula.api as smf

def cluster_power_perm(hist_data, pairs, effect_size, threshold=0):
    """Permutation-based power."""
    n_pairs = len(pairs)
    n_perms = 2 ** n_pairs

    decisions = []
    for perm in range(n_perms):
        binary = format(perm, f"0{n_pairs}b")

        # 배정
        treatment_centers = [
            pairs[i][int(b)] for i, b in enumerate(binary)
        ]

        sim = hist_data.copy()
        sim["group"] = np.where(
            sim["center_ID"].isin(treatment_centers),
            "treatment", "control",
        )
        sim["call_CSAT"] = np.where(
            sim["group"] == "treatment",
            sim["call_CSAT"] + effect_size,
            sim["call_CSAT"],
        )

        # HLM
        try:
            mixed = smf.mixedlm(
                "call_CSAT ~ group",
                data=sim,
                groups=sim["center_ID"],
            ).fit(disp=False)
            est = mixed.params.get("group[T.treatment]", 0)
        except Exception:
            est = 0

        decisions.append(1 if est > threshold else 0)

    return np.mean(decisions)

이 함수가 모든 32 permutation 의 정확한 power 계산.

3.3 CI 의 Discontinuity

Buisson 의 발견

96 simulation (32 perm × 3 month) 의 CI 분석:

CI 분포:
   [-1.0, -0.5]: 25% (강한 음)
   [-0.3, -0.1]: 25% (약한 음)
   [+0.1, +0.3]: 25% (약한 양)
   [+0.5, +1.0]: 25% (강한 양)

CI 가 4 cluster 로 분리:

  • 0 근처 CI 거의 없음
  • “Tight clusters” 패턴

이게 stratified cluster 의 statistical 결과:

  • 5 pair × 32 perm = limited variability
  • 같은 pair 의 두 cluster swap → 효과 추정 변화
  • 다른 pair 의 swap → 다른 cluster 의 효과
  • 이 조합이 4 distinct values 산출
직관 — Discontinuity 의 비즈니스 함의

표준 CI 해석 (CI 가 0 포함하면 no effect):

  • 0 근처 CI 거의 없음 → CI 거의 항상 0 미포함
  • Standard 의사결정: CI > 0 → implement
  • Effect = 0 일 때도 50% 가 양수 CI → false positive 50%!

분석가의 대응:

  • CI 의 표준 해석 안 통함
  • 임계값 (effect 추정 > X) 으로 의사결정 변경

→ Cluster experiment 의 미묘한 함정. 분석가가 수동 검증 필요.

3.4 Threshold 조정

0.25 임계값

기본 임계값 (effect > 0):

  • False positive: 50% (위에서 본 것)
  • True positive (effect 0.6): 75%

수정 임계값 (effect > 0.25):

  • False positive: 25% (50% → 25%)
  • True positive: 75% (보존)

비즈니스 함의:

  • “0.25 미만의 효과는 무시” 의 의미
  • VP 의 target = 0.6 → 0.25 임계값 OK
  • False positive 25% 는 여전히 큼 (acceptable?)
직관 — Threshold 의 비즈니스 의미

비즈니스 임계값:

“0.25 CSAT 미만의 개선은 implement 가치 없음.”

분석가의 보고:

“이 SOP 의 추정 효과가 0.25 초과 시 implement. Effect = 0.6 이면 75% 확률로 검출. Effect = 0 이면 25% 확률로 false positive.

25% false positive 가 큰 risk면, 더 많은 cluster (50 콜센터) 또는 더 긴 실험 필요. 현재 제약 (10 cluster, 1 개월) 에서는 이게 최선.”

VP 의 결정:

  • 25% risk 수용 → 진행
  • 거부 → cluster 늘리거나 다른 design

→ 분석가가 honest 하게 trade-off 보고.

4 Power 와 Confidence 의 Trade-off

4.1 Different Confidence Levels

Buisson 의 비교
Confidence False Positive Power (effect 0.6)
90% (CI > 0) 50% 75%
80% 35% 80%
60% 20% 85%
40% 10% 90%

낮은 confidence = 좁은 CI = 더 자주 0 미포함:

  • False positive ↑
  • Power ↑
직관 — Confidence 의 미묘함

Stratified individual 실험 (Ch.9):

  • 90% CI 가 over-coverage (95% 가 0 포함)
  • Free power, low FPR

Stratified cluster 실험 (Ch.10):

  • 90% CI 가 under-coverage (50% 가 0 포함)
  • Higher FPR, lower power

이유: cluster experiment 의 limited permutations + fixed pair 구조.

→ 두 case 모두 standard 90% CI 가 부정확. 분석가 case-by-case.

4.2 분석가의 결정

Standard convention 의 한계

흔한 default:

  • 90% CI
  • α = 0.05
  • Power = 0.80

이 default 는 cluster experiment 에 안 맞음.

해결:

  1. Empirical FPR 측정 (effect = 0 simulation)
  2. Empirical power 측정 (target effect simulation)
  3. 임계값 조정 으로 FPR 감소 + power 보존

→ Cluster experiment 는 case-by-case. Standard convention 따르지 말 것.

5 Cluster 수 늘리기 — 비용 효과 분석

5.1 비용 분석

50 cluster 의 비용

10 cluster vs 50 cluster:

비용 10 cluster 50 cluster
Setup time 1 주 5 주
Training $50K $250K
Monitoring $10K/월 $50K/월
Total (1개월) $60K $300K

5 배 비용 증가.

이익: Power 75% → 95% (estimated, more cluster).

비즈니스 함의:

  • 정확도 ↑ vs 비용 ↑ trade-off
  • 의사결정 가치가 큰 (예: SOP 변경의 매출 영향 $1M+) 면 추가 비용 OK
  • 작은 의사결정 (UI 변경) 이면 부적절

5.2 통합 vs 분리 실험

직관 — 단계별 실험

대안: 작은 실험 → 큰 실험.

Phase 1: 2 콜센터 pilot
   - 빠른 trial (1 주)
   - Compliance 점검
   - 효과 first estimate
   - $10K
   ↓
Phase 2: 10 콜센터 cluster experiment
   - Phase 1 의 effect estimate 기반 power 계산
   - 1 개월 운영
   - $60K
   ↓
Phase 3: 결정 후 50 콜센터 확장 (선택)

각 단계의 결정:

  • Phase 1: 효과 있을 가능성 → 진행
  • Phase 2: 정량적 효과 측정 → 결정
  • Phase 3: 다른 cluster 에도 효과 있나 검증

→ 단계별이 비용 ↓ + 정보 ↑ + 의사결정 정확.

6 코드 예시 — 통합 Power Analysis

6.1 ICC + Power Curve

import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt


def cluster_power_analysis(hist_data, cluster_col, outcome_col,
                             pairs, effect_grid, threshold=0.25):
    """Cluster experiment 의 종합 power 분석."""

    # 1. ICC 계산
    empty = smf.mixedlm(
        f"{outcome_col} ~ 1",
        data=hist_data,
        groups=hist_data[cluster_col],
    ).fit(disp=False)
    icc = empty.cov_re.iloc[0, 0] / (empty.cov_re.iloc[0, 0] + empty.scale)

    # 2. Effective N
    n_total = len(hist_data)
    mean_cluster_size = n_total / hist_data[cluster_col].nunique()
    n_eff = n_total / (1 + (mean_cluster_size - 1) * icc)

    # 3. Power curve at multiple effect sizes
    powers = {}
    for eff in effect_grid:
        # Simulate
        decisions = []
        n_pairs = len(pairs)
        for perm in range(2 ** n_pairs):
            binary = format(perm, f"0{n_pairs}b")
            treatment_clusters = [
                pairs[i][int(b)] for i, b in enumerate(binary)
            ]
            sim = hist_data.copy()
            sim["group"] = np.where(
                sim[cluster_col].isin(treatment_clusters),
                "treatment", "control",
            )
            sim[outcome_col] = np.where(
                sim["group"] == "treatment",
                sim[outcome_col] + eff,
                sim[outcome_col],
            )
            try:
                mixed = smf.mixedlm(
                    f"{outcome_col} ~ group",
                    data=sim,
                    groups=sim[cluster_col],
                ).fit(disp=False)
                est = mixed.params.get("group[T.treatment]", 0)
            except Exception:
                est = 0
            decisions.append(1 if est > threshold else 0)
        powers[eff] = np.mean(decisions)

    return {
        "icc": icc,
        "n_total": n_total,
        "n_eff": n_eff,
        "powers": powers,
    }
직관 — 통합 보고

이 함수의 출력이 분석가에게 주는 것:

  • ICC 진단 (cluster 효과 강도)
  • Effective N (진짜 표본 크기)
  • Power curve (효과 별 검출 능력)

비즈니스 보고:

“ICC = 0.56 (very high). Effective N = 18. Effect 0.6 검출 power = 75% (threshold 0.25).”

이 한 sentence 가 cluster experiment 의 진단.

6.2 Cluster 수의 효과 시뮬레이션

def simulate_more_clusters(hist_data, target_effect, n_clusters_grid,
                              icc=0.5, residual_var=1.0):
    """더 많은 cluster 의 power 효과 추정."""
    results = []

    for n_clusters in n_clusters_grid:
        # 시뮬레이션 (단순화 — 진짜 historical data 없이)
        np.random.seed(42)
        n_per = 1000
        cluster_baselines = np.random.normal(7, np.sqrt(icc / (1 - icc)), n_clusters)

        records = []
        for c_idx in range(n_clusters):
            for _ in range(n_per):
                records.append({
                    "cluster_ID": f"C{c_idx}",
                    "csat": cluster_baselines[c_idx] + np.random.normal(0, np.sqrt(residual_var)),
                })
        df = pd.DataFrame(records)

        # 무작위 배정 + 효과
        treatment_clusters = np.random.choice(
            df["cluster_ID"].unique(), n_clusters // 2, replace=False
        )
        df["group"] = np.where(
            df["cluster_ID"].isin(treatment_clusters), "treatment", "control"
        )
        df["csat"] = np.where(
            df["group"] == "treatment", df["csat"] + target_effect, df["csat"]
        )

        # HLM
        mixed = smf.mixedlm(
            "csat ~ group",
            data=df,
            groups=df["cluster_ID"],
        ).fit(disp=False)
        est = mixed.params.get("group[T.treatment]", 0)
        se = mixed.bse.get("group[T.treatment]", 1)
        ci_lo = est - 1.645 * se  # 90% CI

        results.append({
            "n_clusters": n_clusters,
            "estimate": est,
            "se": se,
            "ci_lo": ci_lo,
            "rejects_zero": ci_lo > 0,
        })

    return pd.DataFrame(results)


# 시뮬레이션
result = simulate_more_clusters(None, 0.5, [10, 20, 50, 100, 200])
print(result)
직관 — Cluster 수 효과의 정량화

예상:

n_clusters Estimate SE CI_lo
10 0.50 0.30 -0.0
20 0.50 0.20 0.17
50 0.50 0.13 0.29
100 0.50 0.09 0.35
200 0.50 0.06 0.40

Cluster 수 ↑ → SE ↓ → CI 넓이 ↓.

비즈니스 결정:

  • 10 cluster: borderline (CI 가 0 가까이)
  • 50+ cluster: 안정 (CI 정확)

→ 정확도 위해 cluster 수 늘림. 비용 ↑.

6.3 분석가의 보고서

def power_report(result):
    """Power analysis 의 비즈니스 보고."""
    print("=" * 60)
    print("Cluster Experiment Power Analysis")
    print("=" * 60)
    print(f"\nICC: {result['icc']:.3f}")
    if result['icc'] > 0.3:
        print("  → 높은 ICC. Cluster effect 강함.")
    elif result['icc'] > 0.1:
        print("  → 중간 ICC. Cluster effect 적당.")
    else:
        print("  → 낮은 ICC. Cluster 무시 가능?")

    print(f"\nTotal N: {result['n_total']:,}")
    print(f"Effective N: {result['n_eff']:.0f}")
    print(f"  → Total 의 {result['n_eff']/result['n_total']*100:.1f}% 가 effective")

    print("\nPower at various effect sizes:")
    for eff, p in result['powers'].items():
        bar = "█" * int(p * 20)
        print(f"  Effect {eff:.2f}: {p:.2%} {bar}")

    # 권장
    target_power = 0.8
    sufficient_effects = [e for e, p in result['powers'].items() if p >= target_power]
    if sufficient_effects:
        print(f"\n  → Power 80%+ 검출 가능 effect: {min(sufficient_effects):.2f} 이상")
    else:
        print("\n  → 80% Power 도달 불가. Cluster 수 ↑ 필요.")
직관 — 보고서의 비즈니스 가치

이 보고서가 비즈니스 파트너에게:

  • ICC 의 의미 (cluster 효과의 강도)
  • Effective N (진짜 정보량)
  • Power curve (시각적 비교)
  • 권장 (충분한 power 위한 effect 또는 cluster 수)

분석가의 결정 도구. 짧은 시간에 power 진단 + 의사결정 보조.

7 Cluster 수의 결정 trade-off

분석가의 default workflow
1. ICC 진단 (empty model)
2. Effective N 계산
3. Power curve (다양한 effect 별)
4. 비즈니스 임계값 조정
   - Effect 추정값 임계값 (vs 0)
5. Cluster 수 결정
   - 충분한 power 가능?
   - 추가 cluster 비용 vs 정확도 trade-off
6. Pilot 권장 (단계별)

이 workflow 가 cluster experiment 의 표준.

8 관련 주제

8.1 Ch.10 의 형제 글 (Ch.10 완결)

8.2 후속 챕터

8.3 카테고리 진입점

Subscribe

Enjoy this blog? Get notified of new posts by email: