contingency (gof)

elseindependentdependent
1schi2 (gof)
2schi2, 피셔정확Mcnamar
3s+Cochran
검정명용도 (Purpose)사용 상황 (Context)귀무가설 서술 (Text )대립가설 서술 (Text )장점 (Pros)단점 및 한계 (Cons)
카이제곱 적합도 검정 (1s)관측 빈도가 특정 이론적 분포를 따르는지 검정, 단일 범주형 변수의 분포를 특정 이론적 분포와 비교주사위의 공정성 확인, 혈액형 비율 확인 등 단일 집단 분석단일 범주형 변수의 관측 빈도 분포는 특정 이론적 기대 분포와 같다.단일 범주형 변수의 관측 빈도 분포는 특정 이론적 기대 분포와 다르다.계산이 단순하고 직관적임기대 빈도가 너무 낮으면 결과가 왜곡됨
카이제곱 독립성 검정 (2s Indep.)두 범주형 변수 간의 연관성 유무 (독립 여부) 확인, 교차표에서 행과 열이 독립적인지 확인, 하나의 집단에서 표집한 후에 나눠서 확인흡연 여부와 폐암 발생의 상관관계, 성별에 따른 선호도 차이 등두 범주형 변수는 서로 독립이다. (두 변수 간에 연관성이 없다.)두 범주형 변수는 서로 독립이 아니다. (두 변수 간에 연관성이 있다.)범주 수에 제한 없이 다차원 테이블(RxC)에 적용 가능모든 셀의 기대 빈도가 5 이상이어야 신뢰 가능
카이제곱 동질성 검정 (2s Indep.)서로 다른 표본 집단의 변수의 동질성(분포)를 검정, 교차표에서 행간의 분포차이가 존재하는지 확인, 서로 다른 집단에서 따로 표집흡연 여부와 폐암 발생의 상관관계, 성별에 따른 선호도 차이 등비교하는 각 모집단의 범주형 변수 분포(비율)가 모두 동일하다.비교하는 모집단 중 적어도 한 곳의 범주형 변수 분포가 다른 모집단과 다르다.범주 수에 제한 없이 다차원 테이블(RxC)에 적용 가능모든 셀의 기대 빈도가 5 이상이어야 신뢰 가능
피셔의 정확 검정 (Fisher’s Exact)소표본에서의 두 변수 간 독립성 검정2x2 테이블에서 샘플 수가 매우 적거나 특정 셀의 빈도가 0일 때, 2x2 분할표에서 기대 빈도중 적어도 하나가 5보다 작을 때 카이제곱 검정의 대안두 범주형 변수는 서로 독립이다. (연관성이 없다.)두 범주형 변수는 서로 독립이 아니다. (연관성이 있다.)표본 크기에 상관없이 정확한 확률(-value) 계산계산량이 많아 표본이 크거나 테이블이 복잡하면 수행 어려움
맥네머 검정 (McNemar)대응된 두 범주형 데이터의 변화량 검정 (주로 이진반응), 전-후 대응 관계가 있는 데이터의 분포 변화 검정약물 복용 전/후의 증상 유무 변화, 동일 패널의 투표 선호도 변화짝지어진 두 범주형 변수의 주변 확률 분포가 동일하다. (전후 비율 변화가 없다.)짝지어진 두 범주형 변수의 주변 확률 분포가 다르다. (전후 비율 변화가 있다.)동일 대상의 ‘전/후’ 차이를 분석하는 데 특화됨2x2 테이블(이분형 데이터)에만 사용 가능
코크란 Q 검정 (Cochran’s Q)대응 관계가 있는 3개 이상의 대응된 집단 간 비율 차이 검정동일 집단에 대해 3회 이상 반복 측정하거나 3개 이상의 처치 효과 비교3개 이상의 관련된(반복 측정된) 집단 간의 성공 비율이 모두 동일하다.3개 이상의 관련된 집단 중 적어도 한 집단의 성공 비율이 다르다.반복 측정 데이터의 이분형(0/1) 결과 분석에 강력함종속 변수가 반드시 이분형(예/아니오)이어야 함
  • chi2 독립성 검정

    • 귀무가설: 변수간 관련/연관이없다 독립적이다 예측빈도와 기대빈도가 동일하다
  • chi2 동질성 검정

    • 귀무가설: 두개의 그룹이 각 변수에 대하여 서로 분포가 동일하다
    • 독립성 검정과 동일한 코드로 작성 해석의 차이만 존재
  • 피셔의 정확검정

    • 다항계수를 이용하여 직접 확률을 계산하여 검정하는 방식으로 검정통계량 없이 확률을 구한다. 추가로 오즈비를 확인 할 수 있다
XY
Wab
Qcd
  • McNamar

    • table 형식이 검정 통계량의 포맷과 동일한지 컬럼 순서 유의
after-A 반응after-B 반응
pre-A 반응ac
pre-B 반응bd
구분카이제곱 검정 (Chi-square Test)맥니마 검정 (McNemar’s Test)
표본의 성격독립 표본 (Independent)대응 표본 (Dependent/Paired)
분석 목적두 변수 간의 독립성 또는 동질성 검정동일 집단의 처치 전후 비율 변화 검정
분할표 구조 (주로 이상 가능)반드시 (동일 변수의 시점 차이)
검정 통계량
핵심 데이터전체 관측 빈도와 기대 빈도의 차이불일치 쌍(Discordant pairs) 의 차이
귀무가설 ()두 변수는 독립적이다 (연관이 없다)전후 상태 변화의 비율이 동일하다
비고기대 빈도 5 미만 셀이 20% 초과 시 피셔 정확 검정 사용표본 수가 적을 경우 연속성 수정(Yates’ correction) 적용
 
import pandas as pd; import numpy as np
import scipy.stats as stats
import statsmodels.api as sm
affairs = sm.datasets.fair.load_pandas(); df = affairs.data
df_crosstab = pd.crosstab(df['occupation'], df['children'])
 
 
 
if '사전체크':
    """
    모든 셀의 기대 빈도는 최소 1 이상이어야 한다.
    기대빈도가 5 미만인 셀이, 전체의 20% 를 넘어서는 안된다
        아니면 collapse 처리
    2x2 (dof=1) 인 경우, 모든 셀의 기대빈도가 5 이상이어야 한다.
        아니면 피셔검정
    """
 
 
if 'chi2':
 
    # f_obs : 관측 / f_exp : 기대
    stats.chisquare(
        f_obs=df_crosstab[0.0]
        , f_exp=np.array([1,1,2,2,2,2])/10*df_crosstab[0.0].sum()
    )  
 
    # H0: 관측빈도와 기대빈도가 갖다. 즉, 데이터는 특정 분포를 따른다.
    # H1: 관측빈도와 기대빈도가 다르다. 즉, 데이터는 특정 분포를 따르지 않는다.
 
if 'chi2_contingency':
    chi2, p, dof, expected = (
        stats.chi2_contingency(df_crosstab, correction=False)
    )
 
    # correction : 예이츠 보정 (연속성 보정) 에 관한 파라미터, default=True
    # 보정을 하지 않으려면(표본수가 큰 경우) False 옵션 넣기
    #  / 기대 도수가 10 미만인 데이터가 존재한다면 True
    # 예이츠 보정을 넣는 경우 검정 통계량이 달라 질 수 있다.
    # 2x2 (dof=1) 에는 입력이 없을 경우 correction=True 옵션 적용
    # / correction=False를 넣어줘야한다
 
 
if '피셔정확검정':
    # 2x2 교차표에서 관측된 빈도와 기대 빈도가 작은 경우에 사용되는 검정 방법
    # 2x2 일 경우에만 작동함
    oddsratio, p_value = stats.fisher_exact(df_crosstab, alternative='two-sided')
 
 
if 'Mcnemar':
    from statsmodels.stats.contingency_tables import mcnemar 
 
    a, b, c, d = 50, 10, 30, 60
    df_Mcnemar = pd.DataFrame([[a, b],[c, d]]
        , index=['전: 호전', '전: 악화']
        , columns=['후: 호전', '후: 악화']
        )
 
    # 상태가 변한 두 셀의 빈도 합(b + c)이 충분히 커야 함
    if b+c>=25:
        # 카이제곱
        exact=False 
    elif b+c< 25:
        # 이항분포, pval 직접 계산
        exact=True
 
    # correction는 연속성 수정 여부 파라미터 
 
    # sm.stats.mcnemar(df_Mcnemar, exact=exact, correction=True)
    sm.stats.mcnemar(df_Mcnemar, exact=exact)
 
    # H0: 처치 전후의 결과에 차이가 없다. 즉, '호전'→'악화' 비율과 '악화'→'호전' 비율이 같다. (pb = pc) 
    # H1: 처치 전후의 결과에 차이가 있다. 즉, '호전'→'악화' 비율과 '악화'→'호전' 비율이 다르다. (pb = pc) 
 
 
if 'Cochran':
    from statsmodels.stats.contingency_tables import cochrans_q
 
    df_cochran = pd.DataFrame(
        np.random.choice([0, 1], size=150).reshape([50,3])
        , columns=['UI1','UI2','UI3']
        )
 
    sm.stats.cochrans_q(df_cochran)
 
    # H0: 모든 조건(그룹)에서의 성공 확률(비율)은 모두 동일하다. 
    # UI1,2,3의 과업 성공률은 통계적으로 모두 같다.
    # H1: 적어도 하나의 조건(그룹)에서의 성공 확률(비율)은 다르다. 
    # UI1,2,3의 과업 성공률은 통계적으로 모두 같다.
 
 

다항분포 비율 체크

import numpy as np
from scipy.stats import chisquare
 
# 1. 관측 빈도 (A 샘플 실제 데이터 - 개수)
observed = np.array([90, 70, 40])
 
# 2. 기대 빈도 (A 샘플 총원 200명에 모비율 적용 - 개수)
expected = np.array([100, 60, 40])
 
# 3. 카이제곱 적합도 검정 수행
chi_stat, p_value = chisquare(f_obs=observed, f_exp=expected)
 
print(f"카이제곱 통계량: {chi_stat:.4f}")
print(f"p-value: {p_value:.4f}")
observed_table=pd.crosstab(df4_solve1['cyl'], df4_solve1['gear']).values
 
# stat_obs, _, _, expected_table = stats.chi2_contingency(observed_table.values, correction=False)
stat_obs, _, _, expected_table = stats.chi2_contingency(observed_table, correction=False)
 
row_indices, col_indices = [],[]
 
for r in range(observed_table.shape[0]):
    for c in range(observed_table.shape[1]):
        count = observed_table[r, c]
        row_indices.extend([r] * count)
        col_indices.extend([c] * count)
 
row_indices = np.array(row_indices)
col_indices = np.array(col_indices)
 
 
# 4. 몬테카를로 시뮬레이션 파라미터 설정
n_simulations = 10000
count_greater_equal = 0
 
# 성능 향상을 위해 expected_table을 미리 1D로 펴둠 (주변합이 고정이므로 기대빈도는 변하지 않음)
expected_flat = expected_table.flatten()
n_rows, n_cols = observed_table.shape
 
# 5. 시뮬레이션 수행
np.random.seed(42) # 재현성을 위한 시드 고정
 
for _ in range(n_simulations):
    # 열 배열을 무작위로 섞음 (변수 간의 독립성 강제, 귀무가설 모사)
    np.random.shuffle(col_indices)
    
    # 섞인 배열로 새로운 교차표 생성 (빠른 연산을 위해 bincount 활용)
    # 인덱스를 1차원으로 변환: r * n_cols + c
    flat_indices = row_indices * n_cols + col_indices
    sim_table_flat = np.bincount(flat_indices, minlength=n_rows * n_cols)
    
    # 카이제곱 통계량 수동 계산 (Scipy 호출 오버헤드 제거로 속도 최적화)
    # ZeroDivision 방지를 위해 기대빈도가 0인 경우는 없다고 가정 (현재 데이터는 만족함)
    stat_sim = np.sum((sim_table_flat - expected_flat)**2 / expected_flat)
    
    # 관측된 통계량 이상인지 확인
    if stat_sim >= stat_obs:
        count_greater_equal += 1
 
# 6. 경험적 p-value 계산 (연속성 보정 포함)
p_value_sim = (count_greater_equal + 1) / (n_simulations + 1)
 
print(f"관측된 카이제곱 통계량: {stat_obs:.4f}")
print(f"몬테카를로 시뮬레이션(N={n_simulations}) p-value: {p_value_sim:.6f}")