1. 이원분산분석 (Two-way ANOVA)
1) 개념
- 독립변수가 **2개**일 때, 각각의 효과(주효과)와 두 변수가 함께 작용하는 효과(상호작용효과)까지 동시에 분석하는 방법
- 예시:
- 독립변수 A: 성별(남/여)
- 독립변수 B: 학습방법(온라인/오프라인)
- 종속변수: 시험 점수
- → 성별에 따른 차이? 학습방법에 따른 차이? 성별×학습방법 상호작용까지 분석
2) 기본 가정
- **정규성**: 각 집단 데이터가 정규분포를 따른다.
- **등분산성**: 각 집단의 분산이 비슷해야 한다.
- **독립성**: 각 집단은 서로 영향을 주지 않아야 한다.
3) 가설 설정
- **주효과 (Main effect)**
- $H_0$: 독립변수 A(또는 B)에 따라 종속변수 평균에 차이가 없다.
- $H_a$: 독립변수 A(또는 B)에 따라 종속변수 평균에 차이가 있다.
- **상호작용 효과 (Interaction effect)**
- $H_0$: 독립변수 A와 B 간 상호작용 효과가 없다.
- $H_a$: 독립변수 A와 B 간 상호작용 효과가 있다.
4) 직관적 이해
- 주효과: "남/여 차이만 보는 것" 또는 "학습방법 차이만 보는 것"
- 상호작용효과: "남학생은 오프라인이 더 유리하지만, 여학생은 온라인이 더 유리하다"와 같은 경우
2. 공분산분석 (ANCOVA)
1) 개념
- ANOVA와 회귀분석이 결합된 형태
- 독립변수 효과를 보면서, 종속변수에 영향을 줄 수 있는 외생변수(공변량)의 영향을 제거하는 방법
2) 공변량 (Covariate)
- 종속변수와 관계가 있지만 연구의 초점은 아닌 변수
- 반드시 **연속형 변수**여야 함
- 예시:
- 독립변수: 수업 방식 (토론식 vs 전통식)
- 종속변수: 시험 점수
- 공변량: 사전 점수(Pre-test)
- → 사전 점수의 영향을 통제한 후 수업 방식 효과만 확인
3) 공분산분석의 필요성
- 외생변수가 있으면 독립변수 효과를 제대로 볼 수 없음
- ANCOVA는 이런 영향을 통제하여 순수한 효과만 분석 가능
✅ 정리
- **Two-way ANOVA**:
- 독립변수 2개 → 주효과 + 상호작용 효과 확인
- **ANCOVA**:
- 공변량(외생변수)을 통제 → 독립변수의 순수한 효과 확인
1) 이원분산분석 (Two-way ANOVA) — 실습 확장판
A. 가정 점검 & 효과크기 포함한 전체 코드
# !pip install statsmodels pingouin # 필요시
import numpy as np, pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols
from scipy import stats
# -----------------------------
# 1) 데이터 생성 (사용자 예제 기반)
# -----------------------------
np.random.seed(42)
N = 100
df = pd.DataFrame({
'income': np.random.normal(loc=200, scale=50, size=N) + np.tile([20, -10], N//2),
'gender': np.repeat(['male', 'female'], N//2),
'major' : np.tile(np.repeat(['eng', 'biz'], N//4), 2)
})
# 상호작용 효과(예시)
df.loc[(df['gender']=='male') & (df['major']=='eng'), 'income'] += 30
df.loc[(df['gender']=='female') & (df['major']=='eng'), 'income'] -= 15
# -----------------------------
# 2) 이원분산분석(상호작용 포함)
# -----------------------------
# 균형 설계에 가깝다면 typ=2가 보편적. 불균형 심하면 typ=3 권장.
model = ols('income ~ C(gender) * C(major)', data=df).fit()
anova_table = sm.stats.anova_lm(model, typ=2)
print("=== Two-way ANOVA (Type II) ===")
print(anova_table, "\n")
# -----------------------------
# 3) 가정 점검
# -----------------------------
# (a) 잔차 정규성
w, p_shapiro = stats.shapiro(model.resid)
print(f"[정규성] Shapiro-Wilk p={p_shapiro:.4f} (p>=.05면 정규성 위반 아님)")
# (b) 등분산성 (Levene: 두 요인의 조합 그룹 기준)
groups = df.groupby(['gender','major'])['income'].apply(list).tolist()
lev_stat, p_levene = stats.levene(*groups, center='median')
print(f"[등분산성] Levene p={p_levene:.4f} (p>=.05면 등분산 위반 아님)")
# -----------------------------
# 4) 효과크기 (partial eta-squared)
# -----------------------------
# partial η² = SS_effect / (SS_effect + SS_error)
ss_error = anova_table.loc['Residual','sum_sq']
effects = ['C(gender)', 'C(major)', 'C(gender):C(major)']
for eff in effects:
ss_eff = anova_table.loc[eff, 'sum_sq']
pes = ss_eff / (ss_eff + ss_error)
print(f"[효과크기] {eff} partial eta² = {pes:.3f}")
해석 팁
- `PR(>F) < 0.05` 이면 해당 효과 유의.
- `partial eta²` (대략): 0.01≈작음, 0.06≈중간, 0.14≈큼.
- 가정 p값이 0.05 미만이면(정규성/등분산성 위반) → 변환(로그 등) 또는 견고한 방법/부트스트랩 고려.
공분산분석 (ANCOVA) 실습
1. 데이터 설정
import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols
from scipy import stats
# 가상 데이터 생성
np.random.seed(42)
data_ancova = {
'product_attitude': np.random.normal(loc=5, scale=1, size=90),
'pre_attitude': np.random.uniform(1, 10, size=90),
'ad_option': np.repeat(['A', 'B', 'C'], 30)
}
df_ancova = pd.DataFrame(data_ancova)
# 공변량 효과 추가 (사전태도가 높을수록 제품태도도 높음)
df_ancova['product_attitude'] += 0.5 * df_ancova['pre_attitude']
# 독립변수 효과 추가 (광고대안 B와 C)
df_ancova.loc[df_ancova['ad_option'] == 'B', 'product_attitude'] += 1.5
df_ancova.loc[df_ancova['ad_option'] == 'C', 'product_attitude'] -= 0.5
2. 가설 설정
- **귀무가설(H0)**: 광고대안에 따라 제품태도 평균에 차이가 없다.
- **연구가설(Ha)**: 광고대안에 따라 제품태도 평균에 차이가 있다.
- (공변량 `pre_attitude`는 통제 변수 → 사전 태도의 차이는 제거한 후 광고 효과만 본다)
3. 평행선 가정 검정 (공변량×독립변수 상호작용)
ANCOVA에서는 **공변량의 기울기가 집단별로 평행해야 함** (즉, 공변량×집단 상호작용 효과 없음).
# 평행선 가정 확인: 공변량*광고대안 상호작용 추가
model_check = ols('product_attitude ~ C(ad_option)*pre_attitude', data=df_ancova).fit()
anova_check = sm.stats.anova_lm(model_check, typ=2)
print("=== 평행선 가정 검정 ===")
print(anova_check[['F','PR(>F)']])
- `C(ad_option):pre_attitude`의 p-value ≥ 0.05 → **평행선 가정 만족**
- p-value < 0.05 → 그룹별로 기울기가 달라 ANCOVA 부적절 → 대신 상호작용을 포함한 회귀모형으로 해석
4. 공분산분석 수행
# ANCOVA: 공변량 통제 후 광고대안 효과 확인
model_ancova = ols('product_attitude ~ C(ad_option) + pre_attitude', data=df_ancova).fit()
anova_ancova_table = sm.stats.anova_lm(model_ancova, typ=2)
print("=== ANCOVA 결과 ===")
print(anova_ancova_table)
- `C(ad_option)` p-value < 0.05 → **사전태도를 통제한 후에도 광고대안 간 차이가 있음**
- `pre_attitude` p-value < 0.05 → **사전태도는 제품태도에 유의한 영향**
5. 효과크기 계산 (partial η²)
# partial eta squared 계산
ss_error = anova_ancova_table.loc['Residual','sum_sq']
for eff in ['C(ad_option)', 'pre_attitude']:
ss_eff = anova_ancova_table.loc[eff,'sum_sq']
pes = ss_eff / (ss_eff + ss_error)
print(f"[효과크기] {eff} partial eta² = {pes:.3f}")
- 기준 해석: **0.01≈작음, 0.06≈중간, 0.14≈큼**
6. 정리
- ANCOVA는 **외생변수(사전태도)** 영향을 제거하고, 독립변수(광고대안)의 순수 효과를 확인
- 출력 해석:
- `C(ad_option)` → 광고대안 효과 (통제 후에도 차이가 나는지)
- `pre_attitude` → 사전태도 효과 (공변량)
- **활용 예시**:
- 광고효과 분석 시 사전 인식 통제
- 교육효과 검증 시 사전 성적 통제
- 약물 효과 연구 시 환자 나이/체중 통제