중앙값비교 (비모수)

  1. 데이터가 서열형(Ordinal)일 때 설문조사 데이터는 1점(매우 불만족)에서 5점(매우 만족)과 같은 리커트 척도(Likert scale)로 이루어진 경우가 대부분이다. 단일 문항의 리커트 척도는 엄밀히 말해 연속형이 아닌 서열형 데이터다. 서열형 데이터는 정규분포 가정이 성립하지 않으므로 비모수 검정인 윌콕슨 부호순위 검정을 수행해야 한다.
scipy.stats.fisher_exact

피셔의 직접 검정을 수행하는 메서드로, 부가적으로 오즈비(조건부 최대우도 추정치)를 함께 반환한다. ADP 실기 시험 환경의 Scipy 버전이 낮을 경우에도 범용적으로 사용할 수 있는 방법이다.

사전검정

구분평균 검정 (모수적 검정)중위수 검정 (비모수적 검정)
대표적인 통계 기법t-검정(t-test), 분산분석(ANOVA)맨-휘트니 U 검정(Mann-Whitney U), 윌콕슨 부호순위 검정 등
관심 있는 중심 척도평균 (Mean)중위수 (Median) 또는 데이터의 순위(Rank)
원자료의 정규성 가정필수 (데이터가 정규분포를 따라야 함)불필요 (분포의 형태를 가정하지 않음, Distribution-free)
이상치(Outlier)의 영향매우 민감함비교적 둔감함 (Robust)
  • 평균 검정이 아니라 중위수 검정을 쓰기로 확정한 시점에서 원자료의 정규성 여부는 별로 중요하지 않다.

    • 그 이유는 중위수 검정이 대표적인 ‘비모수적 검정(Non-parametric test)‘에 해당하며,
    • 비모수적 검정은 데이터가 특정한 확률 분포(예: 정규분포)를 따른다는 가정을 전제하지 않기 때문
  • 검정 방법론: 10명의 고객이 각각의 셰프에게 점수를 매겼고, 두 집단 간에 대응(Paired)된다는 조건이 없으므로 두 표본은 서로 독립이다.

  1. 등분산성 검정 (2s Ind, 3s+ Ind 에서)

Mann-Whitney나 Kruskal-Wallis 검정의 본질은 ‘평균 순위(확률적 우위)‘를 비교하는 것이다.

이 결과를 직관적인 ’중위수의 차이’로 해석하고 싶다면, 비교하는 집단들의 데이터 분포 모양(Shape)과 산포가 같다는 것을 사전검정으로 증명해야 한다.

  • 목적: 각 집단의 분포 형태와 분산이 동일한지 확인
  • 사용 검정: Levene’s Test (정규성이 깨진 데이터이므로 Bartlett 대신 Levene 사용)
  • 통과 조건: 귀무가설(H 0 : 모든 집단의 분산이 동일하다)이 채택되어야 함 (P-value ≥ 0.05).
    • 등분산성 만족 시: “두/세 집단 간 중위수(Median)에 유의미한 차이가 있다.”
    • 등분산성 위배 시: “두/세 집단 간 분포 위치(Mean rank)에 유의미한 차이가 있다.”

  1. 대칭성 확인 (대응표본 및 1표본 일부 (Wilcoxon))
  • 윌콕슨 부호순위 검정(Wilcoxon Signed-rank test)은 단순히 크다/작다의 부호만 보는 Sign test와 달리, 차이값의 ‘크기(순위)‘까지 고려한다. 이를 위해 필요한 숨은 전제조건이 있다.

  • 목적: 짝지어진 차이값(D=X−Y)이나 1표본 데이터의 분포가 중위수를 중심으로 대칭(Symmetric)인지 확인

  • 확인 방법: 엄밀한 통계 검정보다는 주로 히스토그램(Histogram)이나 Boxplot을 통한 시각적 확인, 혹은 왜도(Skewness) 수치

    • 대칭성 만족 시: Wilcoxon Signed-rank test 사용
    • 대칭성 위배 시: 값의 크기를 무시하고 부호(+/-)만 따지는 Sign test(부호 검정)로 후퇴해야 함.

  1. 정규성도 깨지고, 등분산성도 깨진 경우 (비모수적 우회)

이미 비모수 검정 트리(Mann-Whitney, Kruskal-Wallis)에 진입했는데 등분산성(Levene)마저 깨진 상황이다.

이때는 두 가지 선택지가 있다.

  • 선택지 A (그대로 직진하되 해석을 바꿈): Mann-Whitney(2s)나 Kruskal-Wallis(3s+)를 그대로 수행한다.

    • 단, 결과 해석 시 “A와 B의 중위수가 다르다”라고 쓰면 오답이 되며, “A와 B의 분포 위치(평균 순위, Mean Rank)가 다르다” 혹은 “A가 B보다 확률적으로 큰 값을 가질 경향이 있다”라고 방어적으로 해석을 비틀어야 한다.
  • 선택지 B (중위수 검정으로 완전 선회): 분포 모양이 달라도 무조건 ’중위수’를 기준으로 승부를 보고 싶다면, 2표본이든 3표본이든 상관없이 카이제곱 기반의 Mood’s Median Test (scipy.stats.median_test)로 선회하면 된다.

elseindependentdependent
1sWilcoxon, sign
2sMannwhitney, ranksumWilcoxon, sign
3s+KruskalFriedman
Dunn, Nemenyi
조건파이썬 함수귀무가설 ()대립가설 ()귀무가설 서술 (Text )대립가설 서술 (Text )
1swilcoxon, binomtest (scipy)모집단의 중앙값은 특정 기준값과 같다.모집단의 중앙값은 특정 기준값과 다르다.
2s Indmannwhitneyu, ranksums (scipy)두 독립된 모집단의 중앙값은 같다. (분포가 동일하다.)두 독립된 모집단의 중앙값은 다르다. (분포가 다르다.)
2s Depwilcoxon (scipy)짝지어진 두 변수 차이의 중앙값은 0이다.짝지어진 두 변수 차이의 중앙값은 0이 아니다.
3s+ Indkruskal (scipy)적어도 하나는 다르다.세 개 이상의 독립된 모집단의 중앙값은 모두 같다.적어도 한 독립 모집단의 중앙값은 다른 모집단과 다르다.
3s+ Depfriedmanchisquare (scipy)적어도 하나는 다름세 개 이상의 반복 측정된 집단의 중앙값은 모두 같다.적어도 한 측정 시점의 중앙값은 다른 시점과 다르다.
연속성 (무작위성)runstest_1samp (statsmodels)일련의 연속적인 관측값들은 무작위로 추출되었다. (패턴이 없다.)일련의 연속적인 관측값들은 무작위로 추출되지 않았다. (패턴이나 추세가 존재한다.)
  • Run 검정

    • 런검정(Runs Test)은 관측된 데이터 시퀀스가 무작위로 생성되었는지 여부를 검증하는 비모수적 검정 방법
    • 데이터는 이진 형태(예: 성공/실패, 양성/음성 등)이거나 순서 척도를 가짐
  • 부호검정

    • 대응이 있는 두 집단의 중앙값 차이 검정(순위데이터)
    • 두 집단간의 차이를 +,- 표기하고 25개를 기준으로 이항분포, 정규분포를 통해 직접 계산
  • 윌콕슨 검정

    • 대응 관계가 있는 양적데이터의 두 그룹의 차이를 검정하는 비모수 방법론
    • n이 25이하의 경우 전용표를 기준으로 검정값 비교, 25이상의 경우 z검정 이용
  • 맨휘트니 u검정 / 윌콕슨 순위합 검정

    • 윌콕슨 순위합 검정 (Wilcoxon Rank-Sum Test) / 맨-휘트니 U 검정 (Mann-Whitney U Test)으로 불리운다.
    • 대응표본 t 검정의 비모수검정,대응 관계가 없는 2무리 (양적,순위)의 차이를 검정한다
    • 두 그룹의 중앙값이 같은지 비교할 때 사용
    • 소표본(두 집단 모두 20보다 작은 경우)은 U분포를 따른다 use_continuity=True
    • 대표본은 근사적으로 정규분포를 따르고 z검정 이용한다 use_continuity=False
  • 크러스컬 윌리스 검정

    • 대응이 없는 여러 집단의 순서 데이터 검정 / H 검정이라고도 한다.
    • 소표본 (3집단 은 17개 이하, 4집단의 14이하)의 경우 카이제곱 검정의 경우 검출력이 떨어지기에 별도의 검출표 기준으로 산정
    • 단측검정만 존재
  • 프리드먼 검정

    • 대응이 있는 여러 집단의 순서 데이터 검정
    • 대응이 있는 여러 집단의 차이(일원배치 분산분석)의 비모수판
import numpy as np
from statsmodels.sandbox.stats.runs import runstest_1samp
# 데이터에 이상치가 많아 통계가 왜곡되면 median
# z_stat, p_value = runstest_1samp(data, cutoff='median', correction=True)
z_stat, p_value = runstest_1samp(data, cutoff='mean', correction=True)
# 귀무가설(H0): 데이터가 무작위로 배열되어 있다.
# 대립가설(H1): 데이터가 무작위가 아니다 (일정한 패턴이나 경향성이 존재한다).
from statsmodels.sandbox.stats.runs import runstest_1samp, runstest_2samp
from statsmodels.stats.descriptivestats import sign_test   
 
# sign_test(samp, mu0=0) / 1표본만 가능
 
import scipy.stats as stats
 
stats.wilcoxon
# alternative 파라미터를 통해 양측 검정임을 명시
# 과거 버전에 따라 단측/양측 검정의 기본값(Default) 파라미터가 다름
stats.mannwhitneyu( x, y, use_continuity=True, alternative='two-sided', axis=0, method='auto')
# stats.kruskal(*args, nan_policy='propagate')
stats.friedmanchisquare
import pandas as pd
import scipy.stats as stats
 
# Y공장은 지수분포 (정규성 위배 유도)
group_X = stats.norm.rvs(loc=10, scale=2, size=30)
group_Y = stats.expon.rvs(scale=10, size=35)
group_Z = stats.norm.rvs(loc=12, scale=2, size=40)
 
 
if '정규성 체크':
    stats.shapiro(group_X)
    stats.shapiro(group_Y)
    stats.shapiro(group_Z)
 
 
if '등분산 체크':
    # 정규성을 만족하지 않을 가능성이 높으므로 
    # center='median'(기본값)을 사용하는 Levene 검정 수행
    stat_lev, p_val_lev = stats.levene(group_X, group_Y, group_Z)
 
 
if '결론':
    # 정규성이 하나라도 위배되면 크루스칼-왈리스 검정 수행
    # (단, 등분산성이 위배되었다면 중위수 차이보다는 값의 전반적인 분포 차이로 해석해야 함 주의)
    # "정규성 가정이 위배되었으므로, 
    # One-way ANOVA 대신 Kruskal-Wallis 검정을 수행하는 것이 타당하다."
 

차이가 0인 관측치 제거

비모수 검정에서 차이가 0인 관측치(Ties)를 제외하는 이유는?

  • 해당 데이터가 검정의 목적에 대해 아무런 정보(Information)를 제공하지 않기 때문

    • 특히 부호검정(Sign Test) 이나
    • 윌콕슨 부호순위 검정(Wilcoxon Signed-Rank Test) 에서

1. 검정통계량의 이분성 (Binomial Logic)

  • 부호검정의 귀무가설 H0는 “차이의 중앙값이 0이다”

    • 이는 각 관측치가 양수(+)일 확률과 음수(-)일 확률이 각각 0.5로 동일함을 전제로 한다.
  • 문제점: 차이가 0인 값은 양수도 음수도 아니므로, 이항분포 의 ‘성공’ 혹은 ‘실패’ 어디에도 속할 수 없다. 따라서 0인 값을 분석에 포함하면 이항분포 모델 자체가 성립하지 않게 된다.

  • 해결: 이를 해결하기 위해 0인 데이터를 제외하고 유효한 샘플 수 을 기준으로 검정을 수행하는 것이 표준적인 방법이다.

2. 연속성 가정 (Continuity Assumption)

  • 비모수 검정의 많은 이론적 배경은 기저의 확률분포가 **연속적(Continuous)**이라고 가정한다.

    • 연속확률변수에서 특정 값(예: 0)이 정확하게 나타날 확률 은 이론적으로 0이다.
    • 실제 데이터에서 0이 나타나는 것은 측정의 정밀도 한계로 인한 결과로 간주한다.
    • 따라서 이 ‘모호한’ 데이터를 제외하는 것이 모델의 수학적 엄밀성을 유지하는 방법이다.

3. 검정의 보수성 및 편향 방지

  • 만약 0인 값을 강제로 양수나 음수 중 한쪽으로 배정한다면?
    • 이는 분석가의 주관이 개입되어 1종 오류2종 오류를 인위적으로 조절하는 결과를 초래한다.
    • 0을 모두 귀무가설을 지지하는 증거로 사용하거나 반대로 사용하면 검정 결과가 심각하게 왜곡될 수 있다.

4. 예외: 0을 포함하는 방법 (Pratt’s Method)

  • 모든 통계학자가 0을 버리는 것에 동의하는 것은 아니다.
  • Pratt(1959) 은 0을 포함하여 순위를 매긴 뒤, 나중에 0에 해당하는 순위를 제거하고 검정통계량을 계산하는 방법을 제안했다.
  • 하지만 주로 사용하는 scipy나 statsmodels의 기본 알고리즘(Wilcoxon, Sign test)은 관례에 따라 0을 제거하는 방식을 채택하고 있다.
 
 
 
import scikit_posthocs as sp
 
# 3개의 독립 그룹 데이터
g1 = [23, 25, 21, 26, 22]
g2 = [30, 32, 28, 35, 33]
g3 = [18, 15, 19, 20, 16]
 
# 리스트로 묶어서 입력
data = [g1, g2, g3]
 
# 던의 검정 수행 (본페로니 교정 적용)
# p_adjust에 'bonferroni', 'holm', 'fdr_bh' 등 지정 가능
result_dunn = sp.posthoc_dunn(data, p_adjust='bonferroni')
 
print("Dunn's Test p-value Matrix:")
print(result_dunn)
 
 
 
import numpy as np
import scikit_posthocs as sp
 
# 대응 표본 데이터 (행: 5명의 피험자, 열: 3번의 측정 시점)
# 이전 예시 데이터를 전치(Transpose)한 형태
data = np.array([
    [23, 28, 35],  # 피험자 1
    [25, 27, 33],  # 피험자 2
    [21, 24, 30],  # 피험자 3
    [26, 29, 36],  # 피험자 4
    [22, 25, 31]   # 피험자 5
])
 
# 네메니 검정 수행
result_nemenyi = sp.posthoc_nemenyi_friedman(data)
 
print("Nemenyi Test p-value Matrix:")
print(result_nemenyi)
 
 
 
import pandas as pd
import pingouin as pg
import numpy as np
 
# Pingouin 분석을 위한 Long-format DataFrame 변환
df = pd.DataFrame({
    'value': np.concatenate([group1, group2, group3]),
    'group': ['G1']*5 + ['G2']*5 + ['G3']*5,
    'subject': list(range(1, 6)) * 3  # 대응표본(Friedman) 분석 시 피험자 식별자 필수
})
 
# [대안 A] 크루스칼-왈리스 사후검정 (독립, 비모수)
posthoc_kw = pg.pairwise_tests(data=df, dv='value', between='group', 
                               parametric=False, padjust='bonf')
print("\n=== Pingouin: Kruskal-Wallis Post-hoc ===")
print(posthoc_kw[['A', 'B', 'p-unc', 'p-corr', 'p-adjust', 'Reject']])
 
# [대안 B] 프리드먼 사후검정 (대응, 비모수)
posthoc_fr = pg.pairwise_tests(data=df, dv='value', within='group', subject='subject', 
                               parametric=False, padjust='bonf')
print("\n=== Pingouin: Friedman Post-hoc ===")
print(posthoc_fr[['A', 'B', 'p-unc', 'p-corr', 'p-adjust', 'Reject']])