층화 무작위 배정 — Pre-experiment 정보로 표본 균형 보장 (Buisson Ch.9 overview)

단순 무작위의 한계, 거리 기반 매칭, AirCnC 청소 정책 실험으로의 적용

Buisson (2021) Ch.9 의 전체 흐름을 압축한 overview. 층화 무작위 배정 (Stratified Randomization) 의 의의, 단순 무작위의 작은 표본 imbalance 문제, AirCnC 의 청소 정책 사례 (24 시간 → 8 시간 무료 청소 vs 1 박 최소 booking), 거리 기반 매칭의 직관, rescaling·one-hot encoding 의 필요성을 단계별로 시연한다.

Experimentation
Causal Inference
저자

Kwangmin Kim

공개

2026년 05월 08일

1 정의

정의: Stratified Randomization (층화 무작위 배정)

배정 전 표본의 특성을 알고 있을 때, 유사한 사람끼리 짝 (stratum) 으로 묶고 그 안에서 무작위 배정 하는 절차 (Buisson, 2021, Ch.9).

단순 무작위 vs 층화 무작위:

단순 무작위:
   N 명 → 각자 uniform(0,1) → 0.5 미만 control / 0.5 이상 treatment
   → 그룹 평균 ≈ 모집단 평균 (large N), 그러나 small N 에서 imbalance

층화 무작위:
   N 명 → 비슷한 K 개 stratum → 각 stratum 안에서 절반씩 control/treatment
   → 그룹 평균 = 모집단 평균 (보장), small N 에서도 균형

→ 층화는 단순 무작위의 개선 (improvement). 안 좋아질 일 없음.

직관 — 층화의 목적

분석가의 자연스러운 질문: “왜 단순 무작위로 안 되는가?”

작은 표본의 함정:

  • 20 명 (남 10, 여 10) 무작위 배정 시 5:5 일 확률 = 34.4%
  • 7:3 또는 더 imbalance 일 확률 = 8.9%
  • 8.9% 의 실험에서 control 이 더 남자 많음 → 결과 편향

대처 방법:

  1. 큰 표본 (100+ 남, 100+ 여): imbalance 가능성 → 0
  2. 층화: 작은 표본에서도 정확히 5:5 보장

큰 표본 가능하면 단순 무작위 OK. 표본 작거나 비싸면 층화 필수.

→ 층화는 표본 크기 제약 의 해결.

단순 무작위의 두 위험

작은 표본의 단순 무작위가 실패할 시나리오:

  1. 알려진 변수의 imbalance: 남녀 7:3 → 결과 해석 어려움
  2. 모르는 변수의 imbalance: 통계적으로 5:5 였지만, 다른 잠재 변수 (성격, 소득) 에서 imbalance 가능

→ 층화로 알려진 변수 균형. 모르는 변수는 큰 표본만이 해결.

층화 + 큰 표본 = 가장 안전.

2 AirCnC 사례 — 청소 정책 실험

2.1 비즈니스 맥락

두 부서의 갈등

AirCnC 의 default: 두 booking 사이 24 시간 청소 시간.

문제: 고수요 시장에서 24 시간이 매출 손실.

해결책 (두 부서의 제안):

  1. Finance: 1 박 minimum booking 강제 (24 시간 그대로, 그러나 1 박 미만 차단)
  2. Customer Experience: 무료 청소 서비스 제공 (24 시간 → 8 시간 단축)

각 제안의 trade-off:

장점 단점
1 박 minimum 매출 ↑ CSAT ↓ (단기 고객 불편)
무료 청소 CSAT 유지 비용 ↑ ($10/일)

비즈니스 의사결정의 어려움.

직관 — 정치적 결정 vs 실험

흔한 시나리오:

  • 두 부서가 각자 anecdotal 근거 (“다른 회사가 X 한다”)
  • 정치적 power 가 강한 쪽이 win
  • 데이터 없이 결정

Buisson 의 통찰:

“실험이 정치를 우회하는 만능 약은 아니다.”

이유:

  1. Trade-off 의 본질적 정치성: 매출 ↑ X 일 때 CSAT 하락 Y 까지 허용? 부서마다 답 다름.
  2. 결과 후 합리화: 진 쪽이 “이 시장은 다르다”, “측정 도구가 잘못”, “장기 효과는 다를 것” 등 사후 변명.

해결: 사전 합의 + 명시적 trade-off + 잘 설계된 실험.

→ 실험은 의사결정 보조 도구. 정치적 합의 대체 안 함.

2.2 ToC 적용

4 구성 요소
INTERVENTION (2 가지):
  1. 무료 청소 (8 시간 단축)
  2. 1 박 minimum

BUSINESS GOAL: 매출 (booking profit)

TARGET METRIC: 일평균 booking profit (단, 무료 청소는 - $10/일 비용 차감)

BEHAVIORAL LOGIC:
  무료 청소: cleaning window ↓ → bookings/month ↑ → profit ↑
  1 박 min: avg booking duration ↑ → profit ↑, 그러나 customers ↓

CD:

Free cleaning offer ──→ Cleaning (Y/N) ──→ # bookings ─→
                                                         ↘
1 박 minimum offer ──→ Min booking (Y/N) ──→ duration ─→ avg booking profit
                                                         ↗
                                              fixed costs ─→

두 treatment + 1 control = 3 그룹.

CSAT 의 처리

문제: Customer Experience 부서의 우려 — CSAT 하락.

OEC 사용 시도:

  • 50% 매출 + 50% CSAT 가중 평균
  • 단일 metric 으로 의사결정

Buisson 의 권장 (재방문):

  • Single target metric (booking profit)
  • Guardrail metric (CSAT) — 모니터링하되 분석에 통합 안 함
  • 실험 후 “매출 ↑ + CSAT 큰 하락 안 함” 확인 필수

이유:

  • OEC 의 가중치 합의 어려움
  • Linear trade-off 가정의 위험 (1 점 CSAT = $10M 직선 가정?)
  • 실제는 “5 점 CSAT 하락 → social media storm” 같은 비선형
직관 — Customer 관점의 복잡성

CSAT 측정의 미묘함:

  • 1 박 minimum 시: 1 박 원하는 customer 가 다른 property (control 또는 treat1) 로 이동 → 그 customer 의 CSAT 어떻게 측정?
  • 또는 호텔로 떠남 → AirCnC 에 부재

Buisson 의 권장:

  • 1 박 desired duration customer 추적
  • 모든 booking 의 CSAT 점검
  • 다른 metric 으로 보완 (호텔 이탈률 추정)

→ CSAT 가 “하나의 그룹으로 깔끔하게 측정” 안 됨. 부분 정보로 종합 판단.

3 Random Assignment 의 두 단계

3.1 단계 1: Level 결정

Owner-Level 배정

AirCnC 의 두 treatment 의 logistics:

  • 무료 청소: owner 와의 계약 필요 (booking 단위 불가능)
  • 1 박 minimum: owner 의 property 정책 (booking 단위 불가능)

Owner-level 배정 (5,000 명 owner).

이 결정의 implications:

  • 한 owner 의 모든 property/booking 이 같은 그룹
  • Customer 가 different owner 의 property 본다면 different group 노출 가능
  • Property 단위 측정 (booking profit per day per property)
직관 — Level 결정의 logistics 의존

Visit-level 또는 booking-level 이 안 되는 이유:

  • 무료 청소 = owner 와 회사 의 계약 → booking 마다 다른 정책 불가
  • 1 박 minimum = property 의 booking 정책 → 같은 property 가 다른 booking 에 다른 정책 = 운영 혼란

따라서 owner-level 만 가능.

→ Logistics 의 제약이 statistical 결정 (level) 을 결정.

3.2 단계 2: 배정 방법 — 단순 vs 층화

단순 (no stratification) 의 한계
def no_strat_assign(owners, n_groups=3):
    """단순 무작위 배정."""
    owners["assgnt"] = np.random.uniform(0, 1, len(owners))
    owners["group"] = np.where(
        owners["assgnt"] < 1/n_groups, "control",
        np.where(owners["assgnt"] < 2/n_groups, "treat1", "treat2"),
    )
    return owners

문제: 5,000 명 owner 의 무작위 배정 시 그룹별 특성이 정확히 같지 않음.

예 (가상):

Control Treat1 (무료 청소) Treat2 (1 박 min)
평균 sq_ft 800 820 790
평균 review 7.2 7.5 7.0
Tier 1 비율 30% 35% 28%

이 imbalance 가 효과 측정의 noise. 큰 N 으로 줄지만 zero 안 됨.

직관 — Stratification 의 약속

같은 5,000 명을 층화로 배정하면:

  • 그룹별 sq_ft 평균: 모두 800 (정확)
  • 그룹별 review: 모두 7.3
  • 그룹별 tier 분포: 동일

→ 측정 noise 감소 → 같은 sample size 에서 더 큰 power.

또는 (역으로): 더 작은 sample size 에서 같은 power.

→ 층화 = “공짜 점심” (free lunch) — 안 좋아질 일 없음.

4 층화의 직관

4.1 Toy 예시 — 20 명 시나리오

작은 표본의 imbalance

20 명 (남 10, 여 10) 의 무작위 배정 (50:50):

수학적 분석 (hypergeometric distribution):

  • 정확히 5:5 (남자) 일 확률 = 34.4%
  • 7:3 또는 worse 일 확률 = 8.9%
  • 9:1 일 확률 = 0.1%

해석:

  • 9% 의 실험에서 7+ 명 남자가 한 그룹
  • 분석 시 성별 효과가 treatment 효과로 오인 가능

큰 표본 (100 + 100 명):

  • 7:3 가능성 ≈ 0
  • Imbalance 무시 가능
직관 — Hypergeometric Distribution

배정 = 모집단에서 표본 추출 (replacement 없음) → Hypergeometric.

20 명 중 10 명을 control 로 추출 시 남자 K 명 일 확률:

\[ P(K = k) = \frac{\binom{10}{k} \binom{10}{10-k}}{\binom{20}{10}} \]

  • \(k = 5\): \(\binom{10}{5}^2 / \binom{20}{10} = 0.344\)
  • \(k = 7\): 0.0796
  • \(k \geq 7\) 또는 \(k \leq 3\): 0.089

이 imbalance 가 학계의 작은 실험에서 흔한 문제. 비즈니스 분석에서도 SaaS 스타트업의 첫 실험이나, 임상 시험의 phase I 에서 흔함.

→ 작은 표본 = 층화 필수.

4.2 층화의 작동

Toy 예시 — 층화 적용

같은 20 명 (남 10, 여 10) 의 층화 배정:

Stratum 1: 남자 10 명 → 5 명 control, 5 명 treatment (무작위)
Stratum 2: 여자 10 명 → 5 명 control, 5 명 treatment (무작위)

결과:

  • Control: 남 5, 여 5 (정확)
  • Treatment: 남 5, 여 5 (정확)
  • 성별 perfect balance.

→ 알려진 변수의 imbalance 0%. 층화의 보장.

직관 — 층화 후에도 보존되는 것

층화의 약속:

  • 알려진 변수: perfect balance
  • 무작위성: 그룹 내 individual 의 배정은 여전히 무작위

→ 통계 검정의 가정 (random assignment) 보존.

층화는 무작위성을 깨지 않음. 단지 알려진 정보를 활용 해 sampling error 감소.

→ 무작위 + 층화의 조합. 단순 무작위보다 strict 한 향상.

5 거리 기반 매칭 — 다중 변수의 층화

5.1 단일 변수 vs 다중 변수

단일 변수 층화

성별만 (1 변수):

Stratum: 남, 여 (2 개)
각 stratum 안에서 무작위 배정

쉬움. 그룹별 성별 perfect balance.

다중 변수의 어려움

성별 + 나이 (60 vs 30) + 주거 지역 (50 주):

가능한 stratum 수 = 2 × 2 × 50 = 200

5,000 owner / 200 stratum = 25 명/stratum (3 그룹) → 가능

성별 + 나이 + 지역 + sq_ft + tier + review:

  • 정확한 stratum 수: ~수백만
  • 5,000 / 수백만 = 0.001 명/stratum → 불가능

해결: 거리 기반 매칭 (distance-based matching).

5.2 거리의 정의

단일 numeric 변수

두 owner 의 나이 차이:

  • Owner A: 56 세
  • Owner B: 58 세
  • 거리 = \(|58 - 56| = 2\)

다중 numeric 변수 (Manhattan):

\[ d_{AB} = \sum_{j} |x_{A,j} - x_{B,j}| \]

다중 numeric 변수 (Euclidean):

\[ d_{AB} = \sqrt{\sum_{j} (x_{A,j} - x_{B,j})^2} \]

Euclidean 이 표준.

5.3 Rescaling 의 필요성

단위 차이의 함정

원본 변수:

  • 나이: 18~80 (범위 62)
  • sq_ft: 460~1120 (범위 660)

거리 계산 (rescaling 없이):

  • Owner A (나이 50, sq_ft 800) vs Owner B (나이 30, sq_ft 800)

  • 차이: 나이 20, sq_ft 0

  • 거리 = \(\sqrt{20^2 + 0^2} = 20\)

  • Owner A (나이 50, sq_ft 800) vs Owner C (나이 50, sq_ft 850)

  • 차이: 나이 0, sq_ft 50

  • 거리 = \(\sqrt{0^2 + 50^2} = 50\)

→ Owner A 가 Owner C 보다 Owner B 와 더 가까움 (20 < 50).

문제: 비즈니스 직관과 다름! 나이 20 살 차이가 sq_ft 50 평방피트 차이보다 더 큰 영향.

→ Rescaling 필수.

Min-Max Rescaling

각 변수를 [0, 1] 로 변환:

\[ x_{\text{scaled}} = \frac{x - x_{\min}}{x_{\max} - x_{\min}} \]

예:

  • 나이 50 → (50-18)/(80-18) = 0.516
  • sq_ft 800 → (800-460)/(1120-460) = 0.515

이제 거리:

  • Owner A vs B (나이 50 vs 30): scaled 0.516 vs 0.194 → 거리 0.322
  • Owner A vs C (sq_ft 800 vs 850): scaled 0.515 vs 0.591 → 거리 0.076

→ B 가 C 보다 A 와 더 멀음 (직관 일치).

이 rescaling 후 거리가 의미 있는 distance.

직관 — Rescaling 의 비유

비유: 사과와 바나나 비교.

  • 사과 (개수): 0~10 개
  • 바나나 (kg): 0~5 kg
  • 같은 단위 아니므로 단순 sum 못 함
  • 각각 [0, 1] 로 변환 후 비교 가능

분석 변수도 같음. 각 변수를 [0, 1] 로 변환하면 의미 있는 distance.

다른 rescaling 방법:

  • Z-score: \((x - \mu) / \sigma\) (평균 0, 분산 1)
  • Quantile: rank 기반

Min-Max 가 가장 단순. Z-score 는 outlier 민감.

→ 비즈니스 분석에서 default = Min-Max.

5.4 Categorical 변수의 처리

“Townhouse” vs “Apartment” 의 거리?

Categorical 변수의 거리:

  • 같은 카테고리: 0
  • 다른 카테고리: 1

해결: One-hot encoding.

Property type: ("house", "townhouse", "apartment")
   ↓ one-hot
type_house: 0 또는 1
type_townhouse: 0 또는 1
type_apartment: 0 또는 1

각 변수가 binary [0, 1].

거리:

  • House vs House: type_house 0, type_townhouse 0, type_apartment 0 → 거리 0
  • House vs Townhouse: type_house 1, type_townhouse 1, type_apartment 0 → 거리 √2 ≈ 1.41
  • House vs Apartment: 거리 √2

→ 다른 카테고리는 모두 같은 거리. 카테고리 자체의 ordering 무시.

직관 — One-Hot Encoding 의 함의

One-hot 후 거리:

  • 같은 카테고리: 0
  • 다른 카테고리: \(\sqrt{2}\) (≈ 1.41)

이 거리가 numeric 의 distance 1 (max - min) 와 비교 가능.

비즈니스 함의:

  • “townhouse 와 apartment 의 차이” = “가장 작은 property 와 가장 큰 property 의 차이”
  • 둘 다 1 거리 unit
  • Debatable 하지만 실용적 default

다른 방법:

  • 카테고리 ordering (예: tier 1 > tier 2 > tier 3) → ordinal 변수로 처리
  • Domain 거리 (예: “townhouse 와 house 가 더 가까움”) → manual 정의

→ Default = one-hot. Domain 직관이 강하면 customize.

5.5 Ordered Categorical — Tier

직관 — Tier 의 ordering 보존

AirCnC 의 tier (1, 2, 3) 는 ordered:

  • Tier 1 > Tier 2 > Tier 3 (descending)
  • 1 vs 2 차이 < 1 vs 3 차이

Treatment 1: ordered 처리

df["tier"] = pd.Categorical(df["tier"], categories=[3, 2, 1], ordered=True)

거리 계산:

  • Numeric 으로 처리 (1, 2, 3)
  • 또는 (1/3, 2/3, 1) 로 rescale
  • 거리: |tier_A - tier_B|

Treatment 2: one-hot 처리 (ordering 무시)

  • tier_1, tier_2, tier_3 binary
  • 1 vs 2 거리 = 1 vs 3 거리 (잘못)

분석가의 선택:

  • Domain 직관 (ordered) → numeric or ordinal
  • Domain 직관 약함 → one-hot

AirCnC 의 tier: ordered 처리가 더 정확.

6 Pair Matching 알고리즘

6.1 Optimal vs Greedy

알고리즘 종류
알고리즘 정확도 속도 사용
Optimal 최적 (전역) 느림 (O(N³)) 작은 N
OptGreedy 매우 좋음 (지역) 중간 (O(N²)) 중간 N
NaiveGreedy 좋음 빠름 (O(N²) but constant 작음) 큰 N

R 의 block() 함수 (blockTools 패키지):

stratified_data <- block(
    prepped_data,
    id.vars = c("ID"),
    n.tr = 3,           # 그룹 수 (control + 2 treatment)
    algorithm = "naiveGreedy",
    distance = "euclidean"
)

n.tr = 3 의 의미: 3 명씩 stratum 형성 (control, treat1, treat2 각 1 명).

직관 — NaiveGreedy 의 작동
Step 1: 모든 owner pair 의 거리 계산
Step 2: 가장 가까운 pair 선택 → 한 stratum
Step 3: 사용된 owner 제외
Step 4: 다음 가장 가까운 pair → 다음 stratum
Step 5: 반복

문제: 첫 pair 가 좋아도 나중 pair 가 나쁠 수 있음. 전역 optimum 아님.

해결: OptGreedy 가 약간 더 똑똑 (다음 pair 도 고려). Optimal 은 모든 가능 매칭 점검.

→ Trade-off: 정확도 vs 시간. NaiveGreedy 가 큰 데이터에 default.

6.2 5,000 명의 매칭 시간

계산 비용

5,000 owner, 3 그룹 → 1,667 stratum (각 3 명).

  • NaiveGreedy: ~수십초
  • OptGreedy: ~수분
  • Optimal: 며칠 (실용 불가능)

분석가 default: NaiveGreedy 로 빠르게 + 결과 점검 → OptGreedy 로 정밀화 (선택).

7 코드 예시 — Python 으로 층화 구현

7.1 Data Preparation

import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler, OneHotEncoder


def strat_prep_fun(df):
    """층화 위한 데이터 준비."""
    df = df.copy()

    # Numeric 변수 추출
    num_cols = df.select_dtypes(include=[np.number]).columns.tolist()
    cat_cols = df.select_dtypes(include=["object", "category"]).columns.tolist()

    # ID 가 numeric 으로 분류되면 제외
    if "ID" in num_cols:
        num_cols.remove("ID")
        cat_cols.append("ID")

    # Numeric 변수 [0, 1] rescale
    scaler = MinMaxScaler()
    num_scaled = scaler.fit_transform(df[num_cols])
    num_df = pd.DataFrame(num_scaled, columns=num_cols)

    # Categorical 변수 one-hot
    if cat_cols and len([c for c in cat_cols if c != "ID"]) > 0:
        non_id_cat = [c for c in cat_cols if c != "ID"]
        enc = OneHotEncoder(handle_unknown="ignore", sparse_output=False)
        cat_array = enc.fit_transform(df[non_id_cat])
        cat_df = pd.DataFrame(
            cat_array,
            columns=enc.get_feature_names_out(non_id_cat),
        )
    else:
        cat_df = pd.DataFrame()

    # ID 보존
    id_df = df[["ID"]].reset_index(drop=True) if "ID" in df.columns else None

    # 결합
    if id_df is not None:
        prepped = pd.concat([id_df, num_df, cat_df], axis=1)
    else:
        prepped = pd.concat([num_df, cat_df], axis=1)

    return prepped


# AirCnC 가상 데이터
np.random.seed(42)
n_owners = 200  # 작게 시뮬레이션
df = pd.DataFrame({
    "ID": range(n_owners),
    "sq_ft": np.random.uniform(460, 1120, n_owners),
    "tier": np.random.choice([1, 2, 3], n_owners),
    "avg_review": np.random.uniform(0, 10, n_owners),
})

prepped = strat_prep_fun(df)
print("=== 준비된 데이터 ===")
print(prepped.head())
직관 — 준비 함수의 의도

이 함수가 자동화하는 것:

  1. Numeric 식별 + rescale
  2. Categorical 식별 + one-hot
  3. ID 보존
  4. 결합

분석가가 수동 변수 처리 부담 없음. 데이터만 입력 → 매칭 가능 형태.

→ Boilerplate code 의 표준화.

7.2 거리 행렬 + 매칭

from scipy.spatial.distance import pdist, squareform


def naive_greedy_matching(prepped_df, n_groups=3, id_col="ID"):
    """NaiveGreedy 매칭 — 가장 가까운 N_groups 명을 묶음."""
    # ID 분리
    ids = prepped_df[id_col].values
    features = prepped_df.drop(columns=[id_col]).values

    # 거리 행렬
    distances = squareform(pdist(features, metric="euclidean"))
    np.fill_diagonal(distances, np.inf)  # 자기 자신 제외

    # 매칭
    n = len(ids)
    used = np.zeros(n, dtype=bool)
    strata = []

    while used.sum() < n:
        # 미사용 중 가장 가까운 pair 찾기
        masked_dist = distances.copy()
        masked_dist[used, :] = np.inf
        masked_dist[:, used] = np.inf

        # 가장 작은 거리의 (i, j)
        min_idx = np.unravel_index(np.argmin(masked_dist), masked_dist.shape)
        i, j = min_idx

        if masked_dist[i, j] == np.inf:
            break  # 더 매칭 못 함

        # i, j 를 묶고 추가로 가장 가까운 (n_groups - 2) 명 추가
        stratum = [i, j]
        used[i] = True
        used[j] = True

        for _ in range(n_groups - 2):
            avail = np.where(~used)[0]
            if len(avail) == 0:
                break
            avg_dist_to_stratum = distances[stratum, :][:, avail].mean(axis=0)
            next_idx = avail[np.argmin(avg_dist_to_stratum)]
            stratum.append(next_idx)
            used[next_idx] = True

        if len(stratum) == n_groups:
            strata.append(stratum)
        else:
            # 마지막 stratum 이 부족하면 버림 (또는 무시)
            pass

    # Stratum 안에서 무작위로 그룹 배정
    np.random.seed(42)
    assignments = np.empty(n, dtype=object)
    for stratum in strata:
        groups = np.random.permutation(["control", "treat1", "treat2"])
        for k, idx in enumerate(stratum):
            assignments[idx] = groups[k]

    result_df = pd.DataFrame({
        id_col: ids,
        "group": assignments,
    })
    return result_df


# 매칭 실행
matched = naive_greedy_matching(prepped, n_groups=3)
print("\n=== 매칭 결과 ===")
print(matched["group"].value_counts())
직관 — 알고리즘의 단계별 작동

이 함수의 작동:

  1. 모든 pair 의 거리 계산 (O(N²))
  2. 가장 가까운 pair 선택
  3. 그 pair 에 가장 가까운 1 명 추가 (3 그룹이면)
  4. 3 명 stratum 형성
  5. 사용된 사람 제외 + 반복

scipypdist 가 빠른 거리 계산. NumPy vectorization 으로 큰 데이터 처리.

→ 5,000 owner: 수 분.

7.3 균형 검증

# 매칭 후 그룹별 특성 비교
result_with_features = matched.merge(df, on="ID")

print("\n=== 그룹별 특성 (층화 후) ===")
print(result_with_features.groupby("group").agg(
    sq_ft_mean=("sq_ft", "mean"),
    avg_review_mean=("avg_review", "mean"),
    tier_1_pct=("tier", lambda x: (x == 1).mean()),
    tier_2_pct=("tier", lambda x: (x == 2).mean()),
    tier_3_pct=("tier", lambda x: (x == 3).mean()),
))


# 비교: 단순 무작위
np.random.seed(42)
df["random_group"] = np.random.choice(["control", "treat1", "treat2"], len(df))

print("\n=== 그룹별 특성 (단순 무작위) ===")
print(df.groupby("random_group").agg(
    sq_ft_mean=("sq_ft", "mean"),
    avg_review_mean=("avg_review", "mean"),
    tier_1_pct=("tier", lambda x: (x == 1).mean()),
    tier_2_pct=("tier", lambda x: (x == 2).mean()),
    tier_3_pct=("tier", lambda x: (x == 3).mean()),
))
직관 — 결과의 의미

예상:

  • 층화 후: 그룹별 sq_ft 평균이 매우 가까움 (e.g., 800, 802, 798)
  • 단순 무작위: sq_ft 평균이 다소 다름 (e.g., 780, 820, 800)

층화의 약속이 검증됨.

비즈니스 의사결정:

  • 층화 후 sample size 줄여도 같은 power → 비용 절약
  • 또는 같은 sample size 에서 더 정확한 효과 추정

8 응용 — 다른 비즈니스 사례

8.1 마케팅 캠페인 — 고객 segment 균형

시나리오

분석 질문: “이메일 캠페인 A vs B.”

데이터: 100,000 고객 (인구통계 + 과거 구매).

층화 변수:

  • Age (numeric)
  • Gender (categorical)
  • Past purchase frequency (numeric)
  • Region (categorical, 50 주)

→ 다중 변수 → 거리 기반 매칭.

5,000 stratum (각 20 명) → 그룹별 5 명.

결과: 두 그룹의 인구통계·구매 패턴 거의 동일 → 캠페인 효과 정확.

8.2 임상 시험 — 환자 baseline 균형

시나리오

분석 질문: “약물 A vs Placebo.”

200 환자 (treatment 100, placebo 100).

층화 변수:

  • Age (numeric)
  • Sex (categorical)
  • Disease severity (numeric, 0~10)
  • Comorbidity 수 (numeric)

작은 표본 + 다중 baseline → 층화 거의 필수.

거리 매칭으로 100 pair 형성 → 각 pair 의 무작위 배정 → balance 보장.

CONSORT 표준 절차.

9 종합 — 분석가의 default

Stratification 사용 결정
표본 크기 < 1,000 → 거의 항상 층화
표본 크기 1,000 ~ 10,000 → 층화 권장 (작은 비용, 큰 이익)
표본 크기 > 10,000 → 선택 사항 (단순 무작위 OK)

층화의 비용 (모두 작음):

  • 데이터 준비 (rescaling, one-hot)
  • 알고리즘 실행 시간 (수초~수분)
  • 코드 복잡도 ↑

층화의 이익:

  • 표본 크기 ↓ (같은 power)
  • 결과 정확도 ↑
  • 비즈니스 파트너 신뢰 ↑

→ 층화는 분석가의 default 도구.

10 관련 주제

10.1 Ch.9 의 형제 글

10.2 이전 챕터

10.3 후속 챕터

10.5 카테고리 진입점

Subscribe

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