[python] 판다스 crosstab

GROOTY·2023년 5월 4일
0

가설 검정 시에 판다스 crosstab 을 이용하여 카이제곱검정을 진행하였는데 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 으로 만들어보려고한다.

✍️pd.cut() 함수 연봉 값 4구간으로 나누기

# 구간 나눌 기준을 리스트로 작성하였다. 여기서 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

✍️Crosstab 사용

# 인덱스는 직원의 직급을, 컬럼은 연봉컬럼을 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

✍️Crosstab으로 카이제곱 및 p-value 구하기

# df 는 자유도
chi2, p, df, _ = stats.chi2_contingency(ctab)
print('chi2:{}, p:{}, df:{}'.format(chi2, p, df))

💻출력 결과

chi2:37.40349394195548, p:0.00019211533885350577, df:12
profile
개발 시작

0개의 댓글