1 정의
여러 moderator 가 한 효과에 영향을 미칠 때 (Buisson, 2021, Ch.11):
- Parallel Moderators: 여러 moderator 가 독립적으로 효과 modify
- Interacting Moderators (Moderated Moderation): 한 moderator 의 효과가 다른 moderator 에 의존
수식:
Parallel:
Y = β_0 + β_X X + β_M1 M1 + β_M2 M2 + β_i1 (X*M1) + β_i2 (X*M2)
Moderated Moderation:
Y = β_0 + β_X X + ... + β_i1 (X*M1) + β_i2 (X*M2) + β_3way (X*M1*M2)
3-way interaction term 이 moderated moderation 의 핵심.
비유: 운동 효과의 moderation.
Parallel:
- 성별 (M1) 이 운동 효과 modify
- 나이 (M2) 도 운동 효과 modify
- 두 moderator 가 독립적으로 작동
- 남자 효과 별도, 노인 효과 별도
Moderated Moderation:
- 성별 의 효과 modify 정도 가 나이에 따라 다름
- “젊은 남자: 운동 +10kg, 노인 남자: 운동 +5kg”
- 성별의 moderation 자체가 나이 dependent
→ Parallel 은 단순, moderated moderation 은 복잡 (3-way interaction).
2 Parallel Moderators
2.1 C-Mart 사례 — Children + Age
PlayArea 의 visit duration 효과를 두 변수가 modify:
Children (Y/N) ─→
↘
PlayArea ──→ VisitDuration
↗
Age (numeric) ─→
수식:
\[ \text{Duration} = \beta_0 + \beta_p P + \beta_c C + \beta_a A + \beta_{pc} (P \cdot C) + \beta_{pa} (P \cdot A) \]
해석:
- \(\beta_{pc}\): Children 이 PA 의 효과 modify
- \(\beta_{pa}\): Age 가 PA 의 효과 modify
- 두 modification 은 독립적
“독립적 moderation” 의 의미:
- Children 의 modification 효과 = 모든 age 에서 동일
- Age 의 modification 효과 = 모든 children 상태에서 동일
수학적으로:
\[ \text{Effect of P at (C, A)} = \beta_p + \beta_{pc} C + \beta_{pa} A \]
각 모더레이션이 별도로 추가.
비교: Moderated moderation 은 children 의 modification 자체가 age 에 따라 다름 (다음 절).
2.2 Age 의 numeric 처리
Age 가 numeric (연속 변수) 인 경우:
- \(\beta_{pa}\) = “Age 1 살 차이당 PA 효과 차이”
해석:
- \(\beta_{pa} > 0\): 나이 들수록 PA 효과 큼
- \(\beta_{pa} < 0\): 어릴수록 PA 효과 큼
- \(\beta_{pa} = 0\): Age 무관
대안: Age 를 binning 으로 categorical 처리.
각 그룹의 \(\beta\) 별도 추정.
| 처리 | 장점 | 단점 |
|---|---|---|
| Numeric | Parsimonious (1 coefficient), 정량적 | Linear 가정 |
| Categorical (binned) | 비선형 가능, 해석 쉬움 | 변수 수 ↑, 임의 binning |
분석가의 결정:
- Linear 관계 가정 OK → numeric
- 비선형 의심 + 큰 sample → categorical
비즈니스 보고:
- 의사결정 → categorical (해석 쉬움, “young 에 큰 효과”)
- 정량 → numeric (continuous 효과)
→ 분석 목적에 따라.
2.3 회귀 코드
* operators
R:
Python:
import statsmodels.formula.api as smf
m = smf.ols(
"duration ~ play_area * children + play_area * age",
data=df,
).fit()
print(m.summary())각 * 가 별도 interaction 추가:
play_area * children→ main effects +play_area:childrenplay_area * age→ main effects +play_area:age
3-way (PA × C × A) interaction 은 추가 안 됨 (parallel 가정).
play_area * children + play_area * age 의 의미:
- play_area + children + play_area:children + age + play_area:age
- 6 terms (intercept 제외)
3-way 자동 추가 안 함:
- 그것은
play_area * children * age(다음 절)
분석가가 의도 명확히:
- Parallel:
+로 분리 (독립 moderator) - Moderated moderation:
*로 결합 (interacting)
3 Moderated Moderation (Interacting Moderators)
3.1 정의
Children 의 PA 효과 modification 이 Age 에 따라 다름.
비즈니스 직관:
- Young 부모 + 자녀: PA 의 효과 큼 (child 가 PA 사용 좋아함)
- Old 조부모 + 손자녀: PA 의 효과 작음 (잘 안 씀)
수식:
\[ \text{Duration} = \beta_0 + \beta_p P + \beta_c C + \beta_a A + \beta_{pc} (PC) + \beta_{pa} (PA) + \beta_{ca} (CA) + \beta_{pca} (PCA) \]
3-way interaction term: \(\beta_{pca} (P \cdot C \cdot A)\).
이 term 이 moderated moderation 의 핵심.
비유: 약물의 효과.
- 약물 X 가 환자에게 효과
- 효과는 체중 (M1) 에 따라 다름
- 나이 (M2) 에 따라 체중 효과의 modification 이 또 다름
3-way interaction:
- Age 별로 weight × treatment 의 interaction 이 다름
- “젊은 환자: 체중 무관, 노인: 체중 ↑ → 효과 ↓”
비즈니스에서 흔함:
- Marketing campaign × demographic1 × demographic2
- Pricing × seasonality × customer type
3.2 R / Python 코드
*
3-way interaction:
- 8 terms = 큰 변수 수
- 작은 sample 에서 unstable
- Multicollinearity 위험
분석가의 default:
- 큰 sample (10K+) 만 3-way 사용
- Pre-registered 가설 우선
- Bootstrap CI 로 robust 검증
3.3 결과 해석
Estimate
(Intercept) 20.02
play_area 3.91
children 9.99
age ~0
play_area:children 29.16
play_area:age ~0 (not significant)
children:age ~0 (not significant)
play_area:children:age ~0 (not significant)
해석:
- Children 의 modification 효과 = 29.16 (큼)
- Age 의 main effect 거의 0
- Age 의 interaction 효과 거의 0 (모든 case)
- 3-way 거의 0 → moderated moderation 약함
결론: PlayArea 의 효과가 Children 에 강하게 의존. Age 는 무관.
→ 단순화: 회귀에서 Age 관련 term 제거 가능.
이 분석이 비즈니스 의사결정에:
- Age 별 정책 차별화 불필요
- Children 별 정책 차별화 핵심
- Targeting: 자녀 동반 customer 에 PA 우선
만약 3-way 가 통계적·실용적 유의 시:
- “젊은 부모 + 자녀: PA 큰 효과”
- “조부모 + 손자녀: PA 작은 효과”
- 더 정밀한 segmentation 필요
→ 분석가가 단계별 simplification.
3.4 시각화
Buisson 의 Figure 11-19 같은 visualization:
- X-axis: Children (0, 1)
- Y-axis: Average Duration
- 8 subgraph: age 별 (20, 30, 40, 50, 60, 70, 80)
- 각 subgraph: 두 line (PA = 0, 1)
import matplotlib.pyplot as plt
import seaborn as sns
g = sns.FacetGrid(df, col="age_group", col_wrap=4, height=3)
g.map_dataframe(
lambda data, **kwargs: sns.pointplot(
data=data, x="children", y="duration", hue="play_area"
)
)
plt.savefig("moderated_moderation.png")
plt.show()각 subgraph 의 두 line 의 거리 차이가 moderated moderation 의 visual 증거.
4 Bootstrap CI 의 우월성
4.1 P-value vs CI
전통 회귀의 p-value:
- Normal distribution 가정
- Standard error 가 계수 분포 정규일 때만 정확
- Interaction term 의 분포는 보통 skewed → p-value 부정확
Bootstrap CI:
- 분포 가정 없음
- Robust to skewed/heavy-tailed
- Interaction 같은 비선형 통계량에 적합
import numpy as np
def bootstrap_interaction_CI(df, formula, target_term, B=1000, conf=0.9):
"""Interaction term 의 Bootstrap CI."""
coeffs = []
for _ in range(B):
sample = df.sample(len(df), replace=True)
m = smf.ols(formula, data=sample).fit()
coeff = m.params.get(target_term, np.nan)
if not np.isnan(coeff):
coeffs.append(coeff)
coeffs.sort()
lo = coeffs[int(B * (1 - conf) / 2)]
hi = coeffs[int(B * (1 + conf) / 2)]
return lo, hi
# 사용
ci = bootstrap_interaction_CI(
df, "duration ~ play_area * children", "play_area:children", B=1000, conf=0.9,
)
print(f"play_area:children 90% CI: [{ci[0]:.2f}, {ci[1]:.2f}]")Bootstrap CI 의 procedural 단계:
- 원본 데이터에서 같은 크기 (with replacement) sample
- 회귀 적합
- Interaction coefficient 추출
- B 번 반복
- Coefficients 의 quantile
장점:
- 분포 가정 없음
- 각 sample 의 다양성으로 robust 추정
- Skewed/heavy-tailed 분포에 작동
비즈니스 분석에서 default 도구.
4.2 CI 의 비즈니스 해석
가상 결과:
play_area:children 90% CI: [27.5, 30.8]
해석:
- “Interaction 의 진짜 값이 [27.5, 30.8] 사이일 가능성 90%”
- 0 미포함 → 통계적 유의
- 27.5 자체가 비즈니스적으로 큰 → 실용적 유의
만약 CI 가 0 포함:
play_area:children 90% CI: [-0.5, 1.2]
- 0 포함 → 통계적 유의 안 함
- “Interaction 효과 있을 수도, 없을 수도”
- 더 큰 sample 필요 또는 효과 작음
비즈니스 의사결정:
- CI 좁고 0 미포함 → 진행
- CI 넓고 0 포함 → 추가 데이터 또는 보수적 결정
분석가의 보고:
“PlayArea × Children interaction = 29.16 (90% CI [27.5, 30.8]). 자녀 동반 customer 의 PA 효과가 자녀 없는 customer 보다 평균 29 분 더 김. CI 가 좁고 0 미포함 → 통계적·실용적으로 강한 effect. 자녀 동반 매장 우선 PA 설치 권장.”
이 한 paragraph 이 분석의 핵심 산출물.
5 Multiple Moderators 의 Bootstrap
5.1 Joint CI
Parallel moderators 의 회귀:
여러 interaction terms:
play_area:childrenplay_area:age
Bootstrap 으로 동시 검증:
def bootstrap_multiple_interactions(df, formula, terms, B=1000, conf=0.9):
"""여러 interaction terms 의 Bootstrap CI 동시."""
results = {term: [] for term in terms}
for _ in range(B):
sample = df.sample(len(df), replace=True)
m = smf.ols(formula, data=sample).fit()
for term in terms:
results[term].append(m.params.get(term, np.nan))
cis = {}
for term, coeffs in results.items():
coeffs = sorted([c for c in coeffs if not np.isnan(c)])
lo = coeffs[int(B * (1 - conf) / 2)]
hi = coeffs[int(B * (1 + conf) / 2)]
cis[term] = (lo, hi)
return cis각 interaction 별 CI 비교:
play_area:childrenCI: [27, 31] — 강한 effectplay_area:ageCI: [-0.5, +0.5] — 효과 없음
분석가의 결정:
- Children 은 강한 moderator → 비즈니스 분석에 통합
- Age 는 무관 → 단순화 (회귀에서 제거)
이 검증이 모형 선택 가이드.
6 다중 모더레이터의 함정
6.1 False Discovery Rate
100 가지 가능 moderator 시도 → 평균 5 개 false positive (α = 0.05).
다중 검정 보정:
- Bonferroni: \(\alpha_{adj} = \alpha / N_{tests}\)
- Holm-Bonferroni: 순차 조정 (덜 보수적)
- FDR (Benjamini-Hochberg): false discovery rate 통제
Buisson 의 권장: Bonferroni 안 사용.
이유:
- NHST framework 에 의존
- 너무 보수적 → false negative 증가
- 비즈니스 분석에 부적합
“Promising moderator 발견 시 follow-up 실험으로 검증.”
이 권장의 가치:
- Bonferroni 의 conservatism 회피
- 실험으로 직접 confirmation
- 비즈니스 가치 있는 효과만 검증
Workflow:
- 1 차 분석: 여러 moderator 시도 (탐색적)
- Promising 발견 (large effect, narrow CI)
- 2 차 실험: 그 moderator 정식 검증
- Pre-registered hypothesis
- 결과 → 의사결정
→ 발견은 가설, 검증은 confirmation.
6.2 Provisional Hypotheses
분석가의 보고:
“1 차 분석에서 promising moderator 발견: Children 이 PA 효과 큰 modify (CI [27, 31]). 이는 provisional hypothesis — 추가 검증 필요.
권장: 1. Follow-up 실험 (children-focused) 2. Pre-registered hypothesis 3. 결과 따라 implementation 결정.
1 차 분석 결과만으로 정책 변경하지 말 것.”
이 framing 이:
- 분석가의 신뢰성 보호
- 비즈니스 파트너에게 정확한 risk 전달
- False positive 위험 줄임
→ Discovery 와 Confirmation 의 분리.
7 코드 예시 — 종합
7.1 Parallel Moderators
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
# 가상 데이터
np.random.seed(42)
n = 5000
df = pd.DataFrame({
"play_area": np.random.binomial(1, 0.5, n),
"children": np.random.binomial(1, 0.4, n),
"age": np.random.uniform(20, 80, n),
})
# True coefficients
beta_0 = 20
beta_p = 4
beta_c = 10
beta_a = 0 # age main 무관
beta_pc = 25
beta_pa = -0.05 # 약한 negative (older 효과 약간 작음)
df["duration"] = (
beta_0 + beta_p * df["play_area"]
+ beta_c * df["children"] + beta_a * df["age"]
+ beta_pc * df["play_area"] * df["children"]
+ beta_pa * df["play_area"] * df["age"]
+ np.random.normal(0, 5, n)
)
m = smf.ols(
"duration ~ play_area * children + play_area * age",
data=df,
).fit()
print(m.summary().tables[1])예상:
- \(\hat{\beta}_p \approx 4\)
- \(\hat{\beta}_{pc} \approx 25\)
- \(\hat{\beta}_{pa} \approx -0.05\)
- 모두 진짜 값에 가까움
분석가의 다음 단계:
- \(\beta_{pc}\) 의 큰 effect → 자녀 segmentation 핵심
- \(\beta_{pa}\) 의 작은 effect → age modification 미미
7.2 Bootstrap CI 적용
def bootstrap_multi(df, formula, terms, B=1000, conf=0.9):
"""여러 term 의 Bootstrap CI."""
results = {t: [] for t in terms}
for _ in range(B):
sample = df.sample(len(df), replace=True)
m = smf.ols(formula, data=sample).fit()
for t in terms:
results[t].append(m.params.get(t, np.nan))
return {
t: (np.percentile(c, 5), np.percentile(c, 95))
for t, c in results.items() if c
}
cis = bootstrap_multi(
df,
"duration ~ play_area * children + play_area * age",
["play_area:children", "play_area:age"],
)
print("\n=== Bootstrap CI ===")
for term, (lo, hi) in cis.items():
print(f" {term}: [{lo:.3f}, {hi:.3f}]")7.3 직관 — CI 의 결정
예상:
- play_area:children CI: [23, 27] — 0 미포함, 강함
- play_area:age CI: [-0.08, -0.02] — 0 미포함이지만 작은 effect
분석가의 결론:
- Children moderator: 통계적·실용적 유의 → 핵심
- Age moderator: 통계적 유의 + 작은 effect → 무시
비즈니스 의사결정:
- Children-focused targeting
- Age 는 marketing 시 소소한 조정 only
7.4 Moderated Moderation
# 3-way interaction
np.random.seed(42)
n = 5000
df_3 = pd.DataFrame({
"play_area": np.random.binomial(1, 0.5, n),
"children": np.random.binomial(1, 0.4, n),
"age_group": np.random.choice(["young", "middle", "old"], n),
})
# True (moderated moderation)
df_3["duration"] = 20 + 4 * df_3["play_area"]
df_3["duration"] += 10 * df_3["children"]
# Children moderation 이 age 에 따라 다름
modifier = np.where(
df_3["age_group"] == "young", 30,
np.where(df_3["age_group"] == "middle", 20, 5),
)
df_3["duration"] += modifier * df_3["play_area"] * df_3["children"]
df_3["duration"] += np.random.normal(0, 5, n)
# 회귀
m_3 = smf.ols(
"duration ~ play_area * children * age_group",
data=df_3,
).fit()
print(m_3.summary().tables[1])7.5 직관 — 3-way 의 결과
예상 추정:
play_area:children(baseline = young): ~30play_area:children:age_group[T.middle]: ~-10play_area:children:age_group[T.old]: ~-25
해석:
- Young: PA × Children effect = 30 (큼)
- Middle: PA × Children effect = 20 (중간)
- Old: PA × Children effect = 5 (작음)
비즈니스 함의:
- Young 부모 동반 매장 → PA 큰 효과
- Old (조부모) 동반 매장 → PA 작은 효과
- Targeting 을 demographic + life stage 별 정밀화
8 종합 — 분석가의 결정 트리
8.1 다중 Moderator 결정
1. 사전 가설 (도메인 직관)
- 어떤 moderator 들이 있을 가능성?
- 1~3 개 후보
2. Parallel 시도
- 회귀: X * M1 + X * M2
- 각 interaction 의 Bootstrap CI
- 통계적·실용적 유의 점검
3. Moderated Moderation 가설
- 도메인 직관: M1 의 modification 이 M2 에 따라 다름?
- Yes 면 3-way interaction 추가
4. 3-way 회귀
- X * M1 * M2
- Bootstrap CI 의 3-way term
5. 단순화
- 무관 term 제거
- Parsimony 우선
6. Provisional 결과
- 1 차 분석 = 가설
- 2 차 실험 = 검증
- Discovery vs Confirmation 분리
7. Final 보고
- 비즈니스 implications
- CI 의 정직한 communication
- Provisional 한 부분 명시
이 워크플로가 다중 moderator 의 표준.
9 관련 주제
9.1 Ch.11 의 형제 글 (Ch.11 완결)
- E-BUI11-0 Moderation overview — Ch.11 전체 흐름
- E-BUI11-1 세분화 분석 — Segmentation 자세히
- E-BUI11-2 상호작용과 비선형 — Interaction + Quadratic
9.2 이전 챕터
- E-BUI8-3 검정력 분석 — Power simulation 기초
9.3 후속 챕터
- E-BUI12-0 Mediation·IV overview — Ch.12: Mediation
9.4 카테고리 진입점
- Experimentation 학습 로드맵 — 11 Phase × 7 교재 매핑