파이썬으로 기술통계 중 모양 통계량의 계산에 대해 알아보자.
개요 기술 통계는 주어진 데이터 세트를 설명하고 요약하는 데 사용되는 통계 기법이며 다음과 같이 크게 세 종류로 나뉜다.
위치 통계량(Measures of Location)
변이 통계량(Measures of Dispersion)
모양 통계량(Measures of Shape)
이 중 모양 통계량에 대해 알아보고자 한다.
모양 통계량의 대표적인 통계량에는 왜도(skewness)와 첨도(kurtosis)가 있다. 이제 각 통계량을 알아보자.
왜도(skewness) 정의
왜도(歪度, skewness): 분포의 비대칭도를 측정하는 지표
수식 왜도($\gamma_1$)는 표준화된(standardized) 확률변수($X$)의 세제곱 평균(3차 적률)과 같다.
$$\gamma_1 := \tilde{\mu}_3 = E\left[\left(\frac{X - \mu}{\sigma}\right)^3\right]$$
특성 왜도 수치에 따른 특성은 다음과 같다.
Skewness = 0: 데이터 분포가 대칭
Skewness > 0: 데이터 분포가 왼쪽(음수쪽)으로 치우쳐짐(right skewed, positively skewed, 오른쪽 꼬리가 긴 분포)
Skewness < 0: 데이터 분포가 오른쪽(양수쪽)으로 치우쳐짐(left skewed, negatively skewed, 왼쪽 꼬리가 긴 분포)
예제 다음의 예제를 통해 분포에 따른 왜도, 히스토그램, 상자수염 그림을 확인해보도록 하자. 여기서는 분포의 모양 뿐만 아니라 상자수염 그림의 상자 위치를 잘 보도록 한다.
첨도(kurtosis) 정의
첨도(尖度, kurtosis): 분포의 중앙부분의 뾰족함 또는 꼬리부분의 길이에 대한 정보를 제공하는 통계량
수식 첨고($k$)는 표준화된(standardized) 확률변수($X$)의 네제곱 평균(4차 적률)과 같다. $$k := \tilde{\mu}_4 = E\left[\left(\frac{X - \mu}{\sigma}\right)^4\right]$$
그런데 왜도처럼 기준을 0으로 잡기 위해 첨도에서 3을 뺀 초과 첨도(excess kurtosis)를 기준으로 계산하는 경우가 많다. $$k_e := \tilde{\mu}_4 = E\left[\left(\frac{X - \mu}{\sigma}\right)^4\right] - 3$$
특성 일반적으로 말하는 첨도는 초과 첨도(excess kurtosis)이며 첨도 수치에 따른 특성은 다음과 같다.
Kurtosis = 0(Mesokurtic): 정규분포와 같은 첨도
Kurtosis > 0(Leptokurtic): 꼬리가 두껍고 정규분포보다 뾰족한 분포
Kurtosis < 0(Platykurtic): 꼬리가 얇고 정규분포보다 완만한 분포
n개의 확률변수 $X_{1}, \dots, X_{n}$ 가 서로 독립이고 같은 분산일 경우 다음의 특성이 성립한다. $$Kurt[X_1 + \dots + X_n ] = \frac{1}{n^2}(Kurt[X_1] + \dots + Kurt[X_n])$$
그리고 첨도가 정의될 수 있다면, 해당 값은 최소 1 이상이다. ※ 상한은 없음
$$Kurt[X] \ge 1$$
예제 다음의 예제를 통해 분포에 따른 첨도, 히스토그램, 상자수염 그림을 확인해보도록 하자. 여기서는 분포의 모양 뿐만 아니라 상자수염 그림의 상자 너비를 잘 보도록 한다.
실습 NumPy NumPy 라이브러리는 왜도와 첨도를 계산하기 위한 함수나 메서드를 지원하지 않는다.
Pandas Pandas 라이브러리는 객체의 .skew()
와 .kurt()
메서드가 각각 왜도와 첨도를 지원하며 첨도의 경우 .kurtosis()
메서드도 있으며 해당 메서드의 연산 결과는 .kurt()
와 같다. 그리고 기본적으로 모집단에 대한 통계량(비편향, 표본이 아닌)을 계산한다.
1 2 3 4 5 6 7 8 9 import pandas as pdser = pd.Series([1 , 5 , 2 , 7 , 2 , 3 , 3 , 10 ]) ser.skew() ser.kurt(), ser.kurtosis()
SciPy SciPy 라이브러리는 skew()
와 kurtosis()
함수가 각각 왜도와 첨도를 지원한다. 그리고 각 함수는 기본적으로 표본에 대한 통계량(편향, 모집단이 아닌)을 계산한다. 만약, 모집단에 대한 통계량을 산출하고자 한다면 bias = False
를 추가하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import numpy as npfrom scipy.stats import skewfrom scipy.stats import kurtosisarr = np.array([1 , 5 , 2 , 7 , 2 , 3 , 3 , 10 ]) skew(arr) kurtosis(arr) skew(arr, bias = False ) kurtosis(arr, bias = False )
그래프 - 왜도 이론부분의 왜도 그래프를 그리기 위한 코드는 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import numpy as npimport matplotlib.pyplot as pltfrom scipy.stats import skewnp.random.seed(123 ) arr_1 = np.random.beta(a = 5 , b = 2 , size = 100000 ) arr_2 = np.random.beta(a = 3 , b = 3 , size = 100000 ) arr_3 = np.random.beta(a = 2 , b = 5 , size = 100000 ) fig, axes = plt.subplots(nrows = 2 , ncols = 3 , figsize = (12 , 3 ), gridspec_kw = {"height_ratios" : [3 , 1 ]}) for i, ax in enumerate(axes.ravel()): ax.grid(color = "#CCCCCC" , linewidth = 0.5 , zorder = 1 ) ax.tick_params(length = 0 ) ax.set_xticklabels([]) ax.set_yticks([]) axes[0 , 0 ].hist(arr_1, bins = 50 , color = "#6FA2E8" , zorder = 2 ) axes[0 , 0 ].set_title("Skewness = " + str(round(skew(arr_1), 1 )), size = 10 ) axes[1 , 0 ].boxplot(arr_1, widths = 0.6 , vert = False ) axes[0 , 1 ].hist(arr_2, bins = 50 , color = "#6FA2E8" , zorder = 2 ) axes[0 , 1 ].set_title("Skewness = " + str(round(abs(skew(arr_2)), 1 )), size = 10 ) axes[1 , 1 ].boxplot(arr_2, widths = 0.6 , vert = False ) axes[0 , 2 ].hist(arr_3, bins = 50 , color = "#6FA2E8" , zorder = 2 ) axes[0 , 2 ].set_title("Skewness = " + str(round(skew(arr_3), 1 )), size = 10 ) axes[1 , 2 ].boxplot(arr_3, widths = 0.6 , vert = False ) for n_col in range(3 ): axes[1 , n_col].set_yticklabels(["" ]) plt.subplots_adjust(hspace = 0.1 , wspace = 0 ) plt.suptitle("Skewness" , size = 12 , y = 1.05 ) plt.show()
그래프 - 첨도 이론부분의 첨도 그래프를 그리기 위한 코드는 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import numpy as npimport matplotlib.pyplot as pltfrom scipy.stats import t, kurtosisnp.random.seed(123 ) arr_1 = t.rvs(10 , size = 100000 ) arr_2 = np.random.normal(loc = 0 , scale = 1 , size = 100000 ) arr_3 = t.rvs(30 , size = 100000 ) + np.random.uniform(-10 , 10 , 100000 ) * 0.2 + np.random.uniform(-5 , 5 , 100000 ) * 0.8 fig, axes = plt.subplots(nrows = 2 , ncols = 3 , figsize = (12 , 3 ), gridspec_kw = {"height_ratios" : [3 , 1 ]}) for i, ax in enumerate(axes.ravel()): ax.grid(color = "#CCCCCC" , linewidth = 0.5 , zorder = 1 ) ax.tick_params(length = 0 ) ax.set_xticklabels([]) ax.set_yticks([]) axes[0 , 0 ].hist(arr_1, bins = 50 , color = "#6FA2E8" , zorder = 2 ) axes[0 , 0 ].set_title("Kurtosis = " + str(round(kurtosis(arr_1), 1 )), size = 10 ) axes[1 , 0 ].boxplot(arr_1, widths = 0.6 , vert = False ) axes[0 , 1 ].hist(arr_2, bins = 50 , color = "#6FA2E8" , zorder = 2 ) axes[0 , 1 ].set_title("Kurtosis = " + str(round(abs(kurtosis(arr_2)), 1 )), size = 10 ) axes[1 , 1 ].boxplot(arr_2, widths = 0.6 , vert = False ) axes[0 , 2 ].hist(arr_3, bins = 50 , color = "#6FA2E8" , zorder = 2 ) axes[0 , 2 ].set_title("Kurtosis = " + str(round(kurtosis(arr_3), 1 )), size = 10 ) axes[1 , 2 ].boxplot(arr_3, widths = 0.6 , vert = False ) for n_col in range(3 ): axes[1 , n_col].set_yticklabels(["" ]) plt.subplots_adjust(hspace = 0.1 , wspace = 0 ) plt.suptitle("Kurtosis" , size = 12 , y = 1.05 ) plt.show()