가설 검정 시에 판다스 crosstab 을 이용하여 카이제곱검정을 진행하였는데 crosstab 의 기본적인 원리가 헷갈려 여기저기서 정보를 가져와서 정리하려고 합니다.
출처: https://suy379.tistory.com/149
교차분석표는 각 범주형 데이터의 개수를 행과 열로 cross해놓은 표를 의미한다.
그렇게 표로 만들었을 때 나오는 값의 개수들의 표이다.
pd.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None,\
margins=False, margins_name='All', dropna=True, normalize=False)
필수 input
index: 행으로 그룹화할 값 (array, series, list)
columns: 열로 그룹화할 값 (array, series, list)
Optional
rownames : 행 이름
colnames : 열 이름
values : 두 행/열에 따라 집계할 값. 반드시 aggfunc와 함께 사용
aggfunc : 집계 함수 ex. mean, sum 등
margins : True로 지정 시 행/열의 소계값이 함께 산출
margins_name : margins = True인 경우, 행/열의 소계를 뽑을 행/열을 지정(디폴트: 'All')
dropna : NaN을 포함하지 않고 반환 (디폴트: True)
normalize : 개수가 아닌 비율로 표시. 옵션은 3가지가 있다
index : 행을 기준으로 비율 표시 (각 행의 합 = 100%)
columns : 열을 기준으로 비율 표시 (각 열의 합 = 100%)
all : 전체 기준으로 비율 표시 (각 데이터들의 합 = 100%)
카이제곱 문제2) 지금껏 A회사의 직급과 연봉은 관련이 없다.
그렇다면 jikwon_jik과 jikwon_pay 간의 관련성 여부를 통계적으로 가설검정하시오.
예제파일 : MariaDB의 jikwon table
jikwon_jik (이사:1, 부장:2, 과장:3, 대리:4, 사원:5)
jikwon_pay (1000 ~2999 :1, 3000 ~4999 :2, 5000 ~6999 :3, 7000 ~ :4)
조건 : NA가 있는 행은 제외한다.
# 외부 DB 연결 생략
conn = MySQLdb.connect(**config)
sql = "select jikwon_jik, jikwon_pay from jikwon"
df = pd.read_sql(sql, conn).dropna(subset=['jikwon_jik', 'jikwon_pay'])
print(df['jikwon_jik'].head(3))
print(df['jikwon_pay'].head(3))
0 이사
1 부장
2 과장
Name: jikwon_jik, dtype: object
0 9900
1 8800
2 7900
Name: jikwon_pay, dtype: int64
jikwon 테이블의 직급과 연봉에 관계를 확인하기 위해 연봉의 범위를 4구간 으로 나누고 crosstab 으로 만들어보려고한다.
# 구간 나눌 기준을 리스트로 작성하였다. 여기서 np.inf 는 무한대를 나타낸다.
pbins = [1000, 3000, 5000, 7000, np.inf]
# pd.cut(right=True) : 기본값(True)은 (값 초과, 이하)로 적용되나
# right=False 파라미터를 넣어주면 (값 이상, 미만) 으로 변경된다.
pay_data = pd.cut(df['jikwon_pay'], bins=pbins, right=False)
print(pay_data)
0 (7000.0, inf]
1 (7000.0, inf]
2 (7000.0, inf]
3 (3000.0, 5000.0]
..... 생략
27 (5000.0, 7000.0]
28 (3000.0, 5000.0]
29 (3000.0, 5000.0]
Name: jikwon_pay, dtype: category
Categories (4, interval[float64, right]):
[(1000.0, 3000.0] < (3000.0, 5000.0] < (5000.0, 7000.0] < (7000.0, inf]]
위의 내용을 value_counts() 함수로 확인
>>> print(pay_data.value_counts())
💻 print(pay_data.value_counts()) 출력
[3000.0, 5000.0) 13
[7000.0, inf) 8
[5000.0, 7000.0) 6
[1000.0, 3000.0) 3
Name: jikwon_pay, dtype: int64
# 인덱스는 직원의 직급을, 컬럼은 연봉컬럼을 cut() 으로 구간 나눈 값을 주었다.
ctab = pd.crosstab(index=df['jikwon_jik'], columns=pay_data)
print(ctab)
jikwon_pay [1000.0, 3000.0) [3000.0, 5000.0) [5000.0, 7000.0) [7000.0, inf)
jikwon_jik
1 0 0 0 1
2 0 0 0 3
3 0 0 2 4
4 0 3 4 0
5 3 10 0 0
# df 는 자유도
chi2, p, df, _ = stats.chi2_contingency(ctab)
print('chi2:{}, p:{}, df:{}'.format(chi2, p, df))
chi2:37.40349394195548, p:0.00019211533885350577, df:12