1 도입 — 임상시험의 3 가지 다중성
Schulz Ch.19 는 임상시험 다중성의 3 가지 측면을 다룬다.
- Multiple endpoints — 여러 결과 변수
- Composite endpoints — 결과 변수 합성
- Multiarm trials — 여러 처치 그룹
이 글은 각각의 처리 전략, 함정, 권장 접근 을 정리한다.
2 Multiple Endpoints — 함정과 처리
2.1 데이터 드레징 (Data Dredging)
“가장 위험한 다중성 남용은 발표되지 않는 데이터 드레징 (data dredging) 이다. 연구자가 여러 endpoints 를 분석하지만 유리한 유의 결과만 보고.” (Schulz 2019, Ch.19)
2.2 Selective Reporting 의 영향
이 관행이 미치는 영향:
- 효과 과대 평가 — 우연한 유의 결과만 보고됨
- 재현 위기 — 후속 연구가 발견을 재현 못함
- 지식 왜곡 — 메타 분석이 부정확
2.3 Schulz 의 권장 — 사전 등록
프로토콜 (사전 등록):
Primary endpoint: 1 개 명시
Secondary endpoints: 미리 명시 (수 제한)
분석 계획: Primary 우선
결과 보고: 모든 endpoints (유의 + 미유의)
2.4 CONSORT 권고
CONSORT 2010 는 모든 endpoints 보고 를 요구한다.
- 사전 정의된 모든 outcomes 보고 (15 a)
- 사후 분석은 exploratory 로 표시 (15 b)
- 보정 적용 시 절차 명시 (17)
이 표준이 selective reporting 을 회피하는 핵심 도구.
축구 경기 비유: 경기 시작 전에 “B 팀이 이길 것” 이라 예측. 경기 후 결과를 보고 예측을 그대로 보고하는 것이 정직.
vs
경기 결과를 본 후에 “B 팀의 후반 골이 결정적이었다” 라고 그 골을 핵심 발견 으로 만드는 것은 사후 합리화. 진짜 통계 결과가 아니다.
A/B 테스트의 사전 등록도 같은 원리. 실험 시작 전에 primary metric, 분석 계획, 예측 을 등록. 자료 수집 후 그대로 분석.
도구: OSF (Open Science Framework), AsPredicted.org, 회사 내 위키. 사전 등록이 selective reporting 을 물리적으로 회피.
2.5 Endpoints 수 최소화 권장
“가장 작은 수 의 endpoints 만 사용. 임상적으로 의미 있는 1-2 개로 한정.”
해석 단순성 + 검정력 보존.
3 Composite Endpoints — 다중성 회피의 한 방법
여러 endpoints 를 OR 로 결합한 단일 결과:
\(\text{Composite event} = E_1 \cup E_2 \cup \cdots \cup E_k\)
예: 심혈관 사건 = 심근경색 OR 뇌졸중 OR 심혈관 사망
각 component 가 발생할 때마다 composite 도 발생.
3.1 장점
- 다중성 회피 — 1 개 endpoint 로 처리, 보정 불필요
- Event rate 증가 — 기여 events 합산으로 표본 절감
- 검정력 향상 — 같은 표본에서 더 큰 효과 검출 가능
3.2 함정 — 해석의 모호성
Schulz 의 사례 (Aspirin):
“Aspirin 의 cardiovascular composite endpoint 18 % 감소 (RR 0.82). 그러나 각 component 분석:
- 심근경색: 44 % 감소
- 뇌졸중: 22 % 증가
- 심혈관 사망: 거의 변화 없음
Composite 18 % 감소는 심근경색 감소 가 다른 효과를 압도한 결과. 임상적으로 오해 가능.”
3.3 Composite 의 함정
같은 RR (0.82) 이라도 components 분석이 완전히 다른 그림 을 보여줌. 의사결정에 원치 않는 처치 효과 (뇌졸중 증가) 가 가려질 수 있음.
A/B 테스트 비유: “engagement” composite metric (= 클릭 OR 댓글 OR 좋아요).
신규 변형이 engagement 10 % 증가. 그러나 component 분석:
- 클릭: 30 % 증가
- 댓글: 10 % 감소
- 좋아요: 거의 변화 없음
Composite 10 % 증가가 클릭 증가 만으로 인한 결과. 댓글 감소 (사용자 참여 저하) 는 비즈니스적 우려. Composite 만 보면 이 신호 놓침.
권장: Composite 보고 시 모든 components 도 함께 보고. Composite 가 대표성 을 잃지 않도록 주기적 점검.
3.4 Composite 의 권장 사용
- Components 가 임상적으로 동등한 중요성
- Components 가 비슷한 방향 효과 기대
- 각 component 도 함께 보고
4 Multiarm Trials — 다중 처치
4.1 정의와 빈도
3 개 이상의 처치 그룹 비교. 일반적으로 대조군 + 2 개 이상 처치.
PubMed 무작위 시험의 22 % 가 multiarm: - 15 %: 3-arm - 4 %: 4-arm - 3 %: 5-arm 이상
4.2 Multiarm 의 비교 가능성
3-arm 시험 (A, B, C) 의 가능한 비교 (Schulz Fig. 19.1):
| 비교 유형 | 비교 |
|---|---|
| 쌍별 (3 개) | A vs B, A vs C, B vs C |
| 1 vs 평균 (3 개) | A vs (B+C)/2, B vs (A+C)/2, C vs (A+B)/2 |
| 옴니버스 (1 개) | 모두 같은가? |
| 합계 | 7 개 |
5-arm 이면 비교 수 폭증. 사전 명시 필요.
4.3 Multiarm 의 장점
시나리오: 표준 처치 + 신규 약 A + 신규 약 B 비교
2-arm 접근 (순차):
시험 1: 표준 vs A → 결과
시험 2: 표준 (또는 A) vs B → 결과
총 시험 2 회, 비용 2 배
Multiarm 접근:
시험 1 회: 표준 vs A vs B → 모든 비교 동시
대조군 공유로 표본 절감
후자가 비용 효율적 + 동시 평가 가능.
4.4 Multiarm 의 통계 처리
A-MAX5-3 의 Dunnett 절차가 multiarm 의 표준:
- 대조군 1 개 + 처치군 다수 (예: A, B, C 가 신규, D 가 대조군)
- Dunnett 임계값으로 Bonferroni 보다 효율적 보정
- 검정력 보존
4.5 Multiarm 의 다중성 우려
Schulz 의 우려: “Multi-treatment 다중성보다 multi-endpoint 다중성이 더 위험. 이유는 treatment 추가는 물리적으로 어려움 (자료 추가 필요), endpoint 추가는 분석에서 자유. Cherry picking 기회가 endpoint 측에 더 많음.”
따라서 multiarm 시험에서는 다중성 보정이 상대적으로 덜 critical. 단 사전 명시한 비교만.
5 보정의 역할 — Schulz 의 종합
5.1 Schulz 권장 흐름
다중성 시나리오?
↓
Endpoints 다중성?
↓ Yes
임상 의사결정이 universal null 형태?
Yes → Bonferroni 또는 Hochberg 적절
No → 보정 X, interval estimation 우선
↓
Composite endpoint 사용?
↓ Yes
Components 도 함께 보고 + 임상적 동질성 확인
↓
Multiarm trial?
↓ Yes
Dunnett (대조군 vs 처치군) 또는
옴니버스 + 사전 명시 비교만
5.2 핵심 원칙 5 가지
- 사전 등록 — 모든 endpoints 와 분석 계획 미리 명시
- 모든 결과 보고 — Selective reporting 회피
- Endpoints 수 최소화 — 1-2 개 primary 우선
- Composite 신중 사용 — Components 도 함께 보고
- 보정은 의사결정 규칙에 따라 — 기계적 적용 X
6 A/B 테스트의 매핑
| 임상 시나리오 | A/B 테스트 매핑 |
|---|---|
| Multiple endpoints | Multiple metrics (클릭률, 매출, 체류시간) |
| Composite endpoint | Composite metric (engagement = 클릭 OR 댓글) |
| Multiarm trial | A/B/C/D 테스트 |
| Subgroup analysis | Segment 분석 (모바일·데스크톱) |
| Sequential testing | 일별 모니터링 |
| Cherry picking | 유리한 metric 만 보고 |
A/B 테스트의 다중성 함정과 처리 방법은 임상시험과 거의 동일. 도메인 차이는 효과 크기 정의 와 베이스라인 비율 정도.
7 코드 예시 — Multiarm Trial Dunnett
import numpy as np
import pandas as pd
from scipy.stats import f
from statsmodels.formula.api import ols
import statsmodels.api as sm
np.random.seed(42)
n_each = 30
# 4-arm trial (대조군 + 신약 A, B, C)
group_control = np.random.normal(120, 12, n_each)
group_drug_a = np.random.normal(115, 12, n_each) # 5 mmHg 감소
group_drug_b = np.random.normal(118, 12, n_each) # 2 mmHg 감소
group_drug_c = np.random.normal(110, 12, n_each) # 10 mmHg 감소 (가장 효과)
groups = ['control']*n_each + ['A']*n_each + ['B']*n_each + ['C']*n_each
all_data = np.concatenate([group_control, group_drug_a, group_drug_b, group_drug_c])
df = pd.DataFrame({'bp': all_data, 'treatment': groups})
# 옴니버스 ANOVA
model = ols('bp ~ C(treatment)', data=df).fit()
anova_table = sm.stats.anova_lm(model)
print("옴니버스 ANOVA:")
print(anova_table)
ms_w = anova_table['mean_sq'].iloc[1]
df_w = int(anova_table['df'].iloc[1])
# Dunnett 검정 (대조군 vs 각 처치군)
# 자체 구현 (statsmodels 에 직접 없음, 근사용 Bonferroni)
print("\n대조군 vs 각 처치군 (Bonferroni 근사):")
control_mean = df[df['treatment']=='control']['bp'].mean()
treatments = ['A', 'B', 'C']
n_comparisons = len(treatments)
alpha_per = 0.05 / n_comparisons
from scipy.stats import t as t_dist
for tx in treatments:
tx_mean = df[df['treatment']==tx]['bp'].mean()
diff = control_mean - tx_mean
se = np.sqrt(ms_w * (1/n_each + 1/n_each))
t_stat = diff / se
p_two = 2 * (1 - t_dist.cdf(abs(t_stat), df_w))
sig_bonf = "유의" if p_two < alpha_per else "미유의"
print(f" vs {tx}: 차이 = {diff:+.2f}, t = {t_stat:.3f}, p = {p_two:.4f}, "
f"Bonferroni 임계 {alpha_per:.4f} → {sig_bonf}")이 코드는 4-arm trial 의 Dunnett 분석 을 시뮬레이션한다. 실제 Dunnett 분포 사용은 별도 패키지 (R multcomp::glht()) 가 더 정확.
8 SCH Ch.19 마무리
다중성의 임상적 처리 핵심 원칙:
- Reporting 정직성 — 모든 결과 보고가 보정보다 중요
- 사전 등록 — Selective reporting 의 물리적 회피
- Endpoints 수 최소화 — 검정력 + 해석 단순성
- Composite 신중 — Components 도 함께 보고
- Multiarm + Dunnett — 효율적 비교 구조
- 보정은 의사결정 규칙 함수 — 기계적 적용 회피
9 Composite Endpoint — 추가 사례
9.1 CHARM 시험 (2003) — Heart Failure
Candesartan in Heart failure — Assessment of Reduction in Mortality and morbidity.
Composite primary endpoint: - 심혈관 사망 또는 - 심부전 입원
결과: - Composite: HR 0.85, 95 % CI [0.77, 0.95] - 사망률 단독: HR 0.92, p = 0.13 (NS) - 입원률 단독: HR 0.79, p < 0.001
논의: - Composite 효과 명확 (HR 0.85) - 입원률 감소가 driver. 사망률 효과는 부수적 - 임상적 결론: 입원 감소가 가치 있는 효과
9.2 Composite 의 적절성 기준
- Components 가 임상적으로 동등한 중요성 (예: 사망 + MI + Stroke 가 비슷한 가치)
- Components 가 비슷한 방향 효과 기대
- Components 가 비슷한 빈도 (희귀 + 흔함의 혼합 X)
- Components 가 비슷한 처치 효과 크기
이 기준을 깨지면 composite 결과 해석 어려움.
9.3 부적절한 Composite 사례
“심혈관 사건 = 사망 OR 비치명적 MI OR 비치명적 Stroke OR 입원”
문제: - 사망과 입원이 임상적 중요성 매우 다름 - 입원이 사망보다 흔함 → composite 가 입원 에 가깝게 driven
해법: - Hierarchical composite (사망 우선, 그 다음 MI 등) - Components 분석 항상 보고
10 Multiarm 의 비교 구조
10.1 Pairwise vs vs Control
| 비교 | 비교 수 (4-arm) | 적합 절차 |
|---|---|---|
| Pairwise (모든 쌍) | \(\binom{4}{2} = 6\) | Tukey HSD |
| vs Control | 3 | Dunnett |
| 하나 vs 나머지 평균 | 4 | 사전 계획된 대비 |
| Trend (순서) | 1 | Linear contrast |
각 비교 구조의 적합 절차 가 다름.
10.2 Multiarm Trial 의 표본 크기
시나리오: 4-arm trial (control + 3 treatments)
가정:
대조군 사망률: 0.10
처치군 1: 0.07 (RR 0.7)
처치군 2: 0.08 (RR 0.8)
처치군 3: 0.06 (RR 0.6)
α = 0.05, power = 0.80
Dunnett 절차로 표본 산정:
각 비교를 위한 power 0.80
α 보정: Dunnett 임계값 (Bonferroni 보다 약간 효율적)
→ n_per_group ≈ 750
→ 총: 750 * 4 = 3000
이 표본 산정이 multi-arm 의 효율성 + Dunnett 의 검정력 보존 활용.
11 A/B/C/D 테스트 권장 절차
11.1 Single Primary Metric
import numpy as np
from scipy.stats import chi2_contingency
from statsmodels.stats.multitest import multipletests
np.random.seed(42)
n_per = 5000
# 4 그룹 (대조군 + 3 신규)
groups = {
'control': np.random.binomial(1, 0.05, n_per),
'treatment_A': np.random.binomial(1, 0.052, n_per),
'treatment_B': np.random.binomial(1, 0.058, n_per),
'treatment_C': np.random.binomial(1, 0.062, n_per),
}
# Dunnett 의 근사 (Bonferroni 사용)
control_p = groups['control'].mean()
n_treatments = 3
alpha_bonf = 0.05 / n_treatments
print("대조군 vs 각 처치군 (Bonferroni 보정):")
p_values = []
for name, data in groups.items():
if name == 'control':
continue
treatment_p = data.mean()
table = [[data.sum(), n_per - data.sum()],
[groups['control'].sum(), n_per - groups['control'].sum()]]
chi2, p, _, _ = chi2_contingency(table)
sig = "유의" if p < alpha_bonf else "미유의"
print(f" {name}: p_T = {treatment_p*100:.3f} %, p = {p:.4f} ({sig})")
p_values.append(p)
# Holm 보정
reject_holm, p_holm, _, _ = multipletests(p_values, alpha=0.05, method='holm')
print(f"\nHolm 보정: 기각 = {reject_holm.sum()} / {len(p_values)}")11.2 Multiple Metrics
각 metric 의 p 값 + 효과 크기 + CI 보고. Primary 명시.
# 가상의 5 메트릭
metrics_p_values = {
'revenue': 0.003,
'click_rate': 0.012,
'time_on_page': 0.045,
'bounce_rate': 0.18,
'returning_users': 0.08
}
# Primary 만 unadjusted, secondary FDR
primary = 'revenue'
secondary_p = [v for k, v in metrics_p_values.items() if k != primary]
secondary_names = [k for k in metrics_p_values if k != primary]
reject_fdr, p_fdr, _, _ = multipletests(secondary_p, alpha=0.05, method='fdr_bh')
print(f"\nPrimary ({primary}): p = {metrics_p_values[primary]:.4f}")
print(f"\nSecondary (FDR 보정):")
for name, p_orig, p_adj, rej in zip(secondary_names, secondary_p, p_fdr, reject_fdr):
sig = "유의" if rej else "미유의"
print(f" {name}: p_orig = {p_orig:.4f} → p_adj = {p_adj:.4f} ({sig})")12 CONSORT 의 Multiplicity 권고
CONSORT 2010 가 권고:
- Primary outcome 1 개 사전 명시
- Secondary outcomes 사전 등록
- 모든 outcomes 결과 보고 (유의 + 미유의)
- 다중성 보정 절차 명시
- Subgroup 분석 exploratory 표시
A/B 테스트도 유사 표준 채택 권장.
13 후속 — 역학 표본 크기
다음 시리즈 (A-WOO8-) 는 역학 연구* (cohort, case-control, cross-sectional) 의 표본 크기 산정을 다룬다. Schulz Ch.11 (임상 RCT) 의 일반화.
14 관련 주제
선행 지식
후속 주제 (Phase A)
- A-WOO8-* (역학 표본 크기)
- A-BUI7-, A-WOO14- (부트스트랩, 컴퓨터 집약 방법)
다른 카테고리 연결