데이터 분석(Data Analysis)/쉽게 배우는 파이썬 데이터 분석

[다섯째마당_데이터 과학의 세계] 14 통계 분석 기법을 이용한 가설 검정

Kim MyeongOk 2024. 1. 1. 12:24

다섯째마당_14_통계분석기법을이용한가설검정__쉽게배우는파이썬데이터분석.hwp
0.72MB

통계 분석 기법을 이용해 가설을 검정하는 방법을 알아봄.


14-1 가설 검정이란?

기술 통계와 추론 통계

통계 분석은 기술 통계와 추론 통계로 나눌 수 있음. 데이터를 요약해 설명하는 통계 분석 기법을 기술 통계(descriptive statistics)라고 함. 예를 들어 사람들이 받는 월급을 집계해 전체 월급 평균을 구한다면 이는 '기술 통계 분석'.

추론 통계(inferential statistics)는 단순히 숫자를 요약하는 것을 넘어 어떤 값이 발생할 확률을 계산하는 통계 분석 기법임. 예를 들어 데이터에서 성별에 따라 월급에 차이가 있는 것으로 나타났을 때, 이런 차이가 우연히 발생할 확률을 계산함. 만약 이런 차이가 우연히 나타날 확률이 작다면 성별에 따른 월급 차이가 통계적으로 유의하다(statistically significant)고 결론 내림. 반대로 이런 차이가 우연히 나타날 확률이 크다면 성별에 따른 월급 차이가 통계적으로 유의하지 않다(not statistically significant)고 결론 내림.

일반적으로 통계 분석을 수행했다는 것은 추론 통계를 이용해 가설 검정을 했다는 의미임. 기술 통계 분석에서 집단 간 차이가 있는 것으로 나타났더라도 이는 우연에 의한 차이일 수 있음. 데이터를 이용해 신뢰할 수 있는 결론을 내리려면 유의확률을 계산하는 통계적 가설 검정 절차를 거쳐야 함.

 

통계적 가설 검정

유의확률을 이용해 가설을 검정하는 방법을 통계적 가설 검정(statistical hypothesis test)이라고 함. 유의확률(significance probability, p-value)은 실제로는 집단 간 차이가 없는데 우연히 차이가 있는 데이터가 추출될 확률을 의미함.

통계 분석을 한 결과 유의확률이 크게 나타났다면 '집단 간 차이가 통계적으로 유의하지 않다'고 해석함. 이는 실제로 차이가 없더라도 우연에 의해 이런 정도의 차이가 관찰될 가능성이 크다는 의미임. 반대로 유의확률이 작다면 '집단 간 차이가 통계적으로 유의하다'고 해석함. 이는 실제로 차이가 없는데 우연히 이런 정도의 차이가 관찰될 가능성이 작다, 즉 우연이라고 보기 힘들다는 의미임.

이 장에서는 통계적 가설 검정 기법 중에서 두 집단의 평균에 차이가 있는지 검정하는 t 검정과 두 변수가 관련이 있는지 검정하는 상관분석을 알아보겠음.


14-2 t 검정 두 집단의 평균 비교하기

t 검정(t-test)은 두 집단의 평균에 통계적으로 유의한 차이가 있는지 알아볼 때 사용하는 통계 분석 기법임. t 검정을 하는 방법을 알아보겠음.

 

[Do it! 실습] compact 자동차와 suv 자동차의 도시 연비 t 검정

mpg 데이터를 이용해 'compact' 자동차와 'suv' 자동차의 도시 연비 차이가 통계적으로 유의한지 알아보겠음.

먼저 mpg 데이터를 불러와 category'compact'인 자동차와 'suv'인 자동차의 빈도와 cty 평균을 구하겠음. 출력 결과를 보면 도시 연비 평균이 'compact'20, 'suv'13이므로, 'suv'보다 'compact'가 더 높음.

import pandas as pd
mpg = pd.read_csv('mpg.csv')

# 기술 통계 분석
mpg.query('category in ["compact", "suv"]') \
    .groupby('category', as_index = False) \
    .agg(n = ('category', 'count'),
         mean_cty = ('cty', 'mean'))

 

앞에서 구한 평균 차이가 통계적으로 유의한지 t 검정을 수행해 알아보겠음. 먼저 mpg에서 category'compact'인 행과 'suv'인 행의 cty를 추출해 각각 변수에 할당함.

compact = mpg.query('category == "compact"')['cty']
suv     = mpg.query('category == "suv"')['cty']

 

scipy 패키지의 ttest_ind()를 이용하면 t 검정을 할 수 있음. 앞에서 추출한 두 변수를 ttest_ind()에 나열하면 됨.

t 검정은 비교하는 집단의 분산(값이 퍼져있는 정보)이 같은지 여부에 따라 적용되는 공식이 다름. 여기서는 집단 간 분산이 같다고 가정하고 equal_var = True를 입력하겠음.

# t-test
from scipy import stats
stats.ttest_ind(compact, suv, equal_var = True)

# output: TtestResult(statistic=11.917282584324107, pvalue=2.3909550904711282e-21, df=107.0)

 

출력 결과에서 'pvalue'가 유의확률을 의미함. 일반적으로 유의확률 5%를 판단 기준으로 삼고, p-value0.05 미만이면 '집단 간 차이가 통계적으로 유의하가'고 해석함. 실제로는 차이가 없는데 이런 정도의 차이가 우연히 관찰될 확률이 5%보다 작다면, 이 차이를 우연이라고 보기 어렵다고 결론 내리는 것임.

'pvalue=2.3909550904711282e-21'은 유의확률이 '2.3909550904711282 앞에 021개 있는 값(2.3909550904711282 × 1021)'보다 작다는 의미임. p-value0.05보다 작기 때문에 이 분석 결과는 'compactsuv간 평균 도시 연비 차이가 통계적으로 유의하다'고 결론 내일 수 있음.

 

[Do it! 실습] 일반 휘발유와 고급 휘발유의 도시 연비 t 검정

이번에는 일반 휘발유(regular)를 사용하는 자동차와 고급 휘발유(premium)를 사용하는 자동차의 도시 연비 차이가 통계적으로 유의한지 알아보겠음. 두 연료를 사용하는 자동차의 빈도와 cty 평균을 구한 뒤 t 검정을 하겠음.

# 기술 통계 분석
mpg.query('fl in ["r", "p"]') \
    .groupby('fl', as_index = False) \
    .agg(n = ('fl', 'count'),
         mean = ('cty', 'mean'))

regular = mpg.query('fl == "r"')['cty']
premium = mpg.query('fl == "p"')['cty']

# t-test
stats.ttest_ind(regular, premium, equal_var = True)

# output: TtestResult(statistic=-1.066182514588919, pvalue=0.28752051088667036, df=218.0)

 

출력 결과를 보면 p-value0.05보다 큰 0.2875.... 실제로는 차이가 없는데 우연에 의해 이런 정도의 차이가 관찰될 확률이 28.75%라는 의미임. 따라서 '일반 휘발유와 고급 휘발유를 사용하는 자동차의 도시 연비 차이가 통계적으로 유의하지 않다'고 결론 내릴 수 있음. 고급 휘발유 자동차의 도시 연비 평균이 0.6 정도 높지만 이런 정도의 차이는 우연히 발생했을 가능성이 크다고 해석하는 것임.


14-3 상관분석 두 변수의 관계 분석하기

상관분석(correlation analysis)은 두 연속 변수가 서로 관련이 있는지 검정하는 통계 분석 기법임. 상관분석을 통해 도출한 상관계수(correlation coefficient)를 보면 두 변수가 얼마나 관련되어 있는지, 관련성의 정도를 파악할 수 있음. 상관계수는 0~1 사이의 값을 지니며 1에 가까울수록 관련성이 크다는 것을 의미함. 상관계수가 양수면 정비례, 음수면 반비례 관계를 의미함.

 

[Do it! 실습] 실업자 수와 개인 소비 지출의 상관관계

economics 데이터를 이용해서 unemploy(실업자 수)pce(개인 소비 지출) 간에 상관관계가 있는지 알아보겠음.

 

1. 상관관계 구하기

상관관계는 df.corr()을 이용해 구할 수 있음. economics 데이터를 불러와 unemploy, pce 변수를 추출한 다음 corr()을 이용해 상관행렬을 만들겠음.

# economics 데이터 불러오기
economics = pd.read_csv('economics.csv')

# 상관행렬 만들기
economics[['unemploy', 'pce']].corr()

 

출력 결과는 unemploypce의 상관관계를 나타낸 행렬임. 행과 열에 똑같은 변수가 나열되어 대칭이므로 왼쪽 아해와 오른쪽 위에 표현된 상관계수가 같음. 상관계수가 양수 0.61이므로, 실업자 수와 개인 소비 지출은 한 변수가 증가하면 다른 변수가 증가하는 정비례 관계라는 것을 알 수 있음.

 

2. 유의확률 구하기

df.corr()을 이용하면 상관계수를 알 수 있지만 유의확률은 알 수 없음. 유의확률은 scipy 패키지의 stats.pearsonr()을 이용해 구할 수 있음. stats.pearsonr()에 분석할 변수를 나열하면 상관계수와 유의확률을 출력함.

# 상관분석
stats.pearsonr(economics['unemploy'], economics['pce'])

# output: PearsonRResult(statistic=0.6145176141932079, pvalue=6.773527303289964e-61)

 

출력 결과에서 첫 번째 값이 상관관계, 두 번째 값이 유의확률을 의미함. 유의확율이 0.05 미만이므로, 실업자 수와 개인 소비 지출의 상관관계가 통계적으로 유의하다고 결론 내릴 수 있음.

 

[Do it! 실습] 상관관계 히트맵 만들기

여러 변수의 관련성을 한꺼번에 알아보고 싶을 때 모든 변수의 상관관계를 나타낸 상관행렬(correlation matrix)을 만들면 편리함. 상관행렬을 보면 어떤 변수끼리 관련이 크고 적은지 한눈에 파악할 수 있음.

 

1. 상관행렬 만들기

mtcars 데이터를 불러와 상관행렬을 만들겠음. mtcars는 자동차 32종의 11개 변수를 담고 있음.

tip) mtcars 데이터 출처: bit.ly/easypy_141

mtcars = pd.read_csv('mtcars.csv')
mtcars.head()

car_cor = mtcars.corr()    # 상관행렬 만들기
car_cor = round(car_cor, 2)# 소수점 둘째 자리까지 반올림
car_cor

 

출력된 상관행렬을 보면 mtcars의 변수들이 서로 얼마나 관련되는지 알 수 있다.

mpg(연비) 행과 cyl(실린더 수) 열이 교차되는 부분을 보면 상관계수가 0.85이므로, 연비가 높을수록 실린더 수가 적은 경향이 있음.

cyl(실린더 수)wt(무게)의 상관계수는 0.78이므로, 실린더 수가 많을수록 자동차가 무거운 경향이 있음.

 

2. 히트맵 만들기

여러 변수로 상관행렬을 만들면 출력된 값이 너무 많아서 관심있는 변수들의 관계를 파악하기 어려움. 이럴 때 값의 크기를 색깔로 표현한 히트맵(heatmap)을 만들면 변수들의 관계를 쉽게 파악할 수 있음. seaborn 패키지의 heatmap()을 이용하면 상관행렬로 히트맵을 만들 수 있음.

import matplotlib.pyplot as plt
plt.rcParams.update({'figure.dpi' :120,             # 해상도 설정
                     'figure.figsize' : [7.5, 5.5]})# 가로 세로 크기 설정
# 히트맵 만들기
import seaborn as sns
sns.heatmap(car_cor,
            annot = True,  # 상관계수 표시
            cmap = 'RdBu') # 컬러맵

 

히트맵은 상관관계가 클수록 살자 색깔을 진하게 표현하고, 상관관계가 양수면 파란색, 음수면 빨간색 계열로 표현함. 상자 색깔을 보면 상관관계의 정도와 방향을 쉽게 파악할 수 있음.

 

3. 대각 행렬 제거하기

상관행렬은 행과 열에 같은 변수가 나열되므로 앞에서 만든 히트맵은 대각선 기준으로 왼쪽 아래와 오른쪽 위의 값이 대칭하여 중복됨. sns.heatmap()mask를 이용해 중복된 부분을 제거하겠음.

 

(1) mask 만들기

np.zeros_like()를 이용해 상관행렬의 행과 열의 수만큼 0으로 채운 배열(array)을 만듦.

# mask 만들기
import numpy as np
mask = np.zeros_like(car_cor)
mask

 

배열의 오른쪽 위 인덱스(index)를 구하는 np.triu_indices_from()를 활용해 mask의 오른쪽 위 대각 행렬을 1로 바꿈.

# 오른쪽 위 대각 행렬을 1로 바꾸기
mask[np.triu_indices_from(mask)] = 1
mask

 

(2) 히트맵에 mask 적용하기

sns.heatmap()mask를 적용함. 출력된 히트맵을 보면 mask1에 해당하는 위치의 값이 제거되어 왼쪽 아래의 상관계수만 표현됨.

# 히트맵 만들기
sns.heatmap(car_cor,
            annot = True,  # 상관계수 표시
            cmap = 'RdBu', # 컬러맵
            mask = mask)   # mask 적용

 

(3) 빈 행과 열 제거하기

앞에서 만든 히트맵의 왼쪽 위 mpg행과 오른쪽 아래 carb열에는 아무 값도 표현되어 있지 않음. 행과 열의 변수가 같아서 상관계수가 항상 1이 되는 위치이므로 값을 표현하지 않은 것임. 히트맵의 빈 행과 열을 재거하려면 mask와 상관행렬의 첫 번째 행과 마지막 열을 제거한 다음 히트맵을 만들면 됨.

mask_new = mask[1:, :-1]       # mask 첫 번째 행, 마지막 열 제거
cor_new = car_cor.iloc[1:, :-1]# 상관행렬 첫 번째 행, 마지막 열 제거

# 히트맵 만들기
sns.heatmap(data = cor_new,
            annot = True,    # 상관계수 표시
            cmap = 'RdBu',   # 컬러맵
            mask = mask_new) # mask 적용

tip) []iloc[]를 이용해 데이터를 추출하는 방법은 16장에서 자세히 다룸.

 

파라미터를 몇 가지 추가해 히트맵을 보기 좋게 수정하겠음. 출력된 히트맵을 보면 변수 간의 관계를 쉽게 알 수 있음.

# 히트맵 만들기
sns.heatmap(data = cor_new,
            annot = True,              # 상관계수 표시
            cmap = 'RdBu',             # 컬러맵
            mask = mask_new,           # mask 적용
            linewidths = .5,           # 경계 구분선 추가
            vmax = 1,                  # 가장 진한 파란색으로 표현할 최댓값
            vmin = -1,                 # 가장 진한 빨간색으로 표현할 최솟값
            cbar_kws = {"shrink": .5}) # 범례 크기 줄이기

 

{알아 두면 좋아요} 히트맵 모양 바꾸기

sns.heatmap()의 파라미터를 이용하면 히트맵의 모양을 다양하게 바꿀 수 있음. seaborn 공식 문서를 참고하기 바람.

- seaborn.heatmap: bit.ly/easypy_142

 

 

 

 

- 14장 END -