for textmining

정규분포 누적분포함수와 중심극한정리

|

이번 글에서는 정규분포(Normal Distribution)중심극한정리(Central Limit Theorem)을 간단한 파이썬 코드 중심으로 살펴보도록 하겠습니다. 이 글은 ‘밑바닥부터 시작하는 데이터과학(조엘 그루스, 인사이트 펴냄)’과 ‘일반통계학(김우철 외, 영지문화사)’ 두 책과 고려대 한성원 교수님 강의를 정리했음을 먼저 밝힙니다. 그럼 시작하겠습니다.

정규분포

정규분포는 가우스(Gauss, 1777-1855)에 의해 제시된 분포로서 일명 가우스분포(Gauss Distribution)라고 불리며 물리학 실험 등에서 오차에 대한 확률분포를 연구하는 과정에서 발견되었다고 합니다. 가우스 이후 이 분포는 여러 학문 분야에서 이용되었으며, 초기의 통계학자들은 모든 자료의 히스토그램이 정규분포의 형태와 유사하지 않으면 비정상적인 자료라고까지 생각하였다고 합니다. 이러한 이유로 이 분포에 ‘정규(normal)’라는 이름이 붙게 된 것입니다.

정규분포는 특성값이 연속적인 무한모집단 분포의 일종으로서 평균이 $μ$이고 표준편차가 $σ$인 경우 정규분포의 확률밀도함수(Probability Density Function)는 다음과 같습니다.

평균, 편차에 따른 분포의 변화

정규분포의 파라메터는 평균과 표준편차입니다. 파라메터가 변하면 분포 또한 바뀌게 되는데요. 아래 그림과 같습니다.

위 그림을 생성하는 데 필요한 파이썬 코드는 다음과 같습니다.

import math
from matplotlib import pyplot as plt
def normal_pdf(x, mu=0, sigma=1):
    sqrt_two_pi = math.sqrt(2 * math.pi)
    return (math.exp(-(x-mu)**2 / 2 / sigma**2) / (sqrt_two_pi * sigma))
xs = [x / 10.0 for x in range(-50,50)]
plt.plot(xs,[normal_pdf(x,sigma=1) for x in xs],'-',label='mu=0,sigma=1')
plt.plot(xs,[normal_pdf(x,sigma=2) for x in xs],'--',label='mu=0,sigma=2')
plt.plot(xs,[normal_pdf(x,sigma=0.5) for x in xs],':',label='mu=0,sigma=0.5')
plt.plot(xs,[normal_pdf(x,mu=-1) for x in xs],'-.',label='mu=-1,sigma=1')
plt.legend()
plt.title('Various Normal pdfs')
plt.show()

정규분포의 누적분포함수

누적분포함수(Cumulative Distribution Function, CDF)는 어떤 확률분포에 대해 확률변수가 특정 값보다 작거나 같은 확률을 나타냅니다. 아래 표는 평균이 0이고 표준편차가 1인 표준정규분포의 누적분포함수를 표로 정리한 것인데요. 빨간색 영역에 해당하는 확률이 바로 CDF에 해당합니다.

표준정규분포의 확률변수를 $Z$라고 할 때 $Z$값(위 표에서 행과 열의 이름에 해당)의 변화에 따른 누적분포함수 값의 변화를 나타낸 그림은 다음과 같습니다. 정규분포의 누적분포함수 또한 평균, 분산이 달라지면 그 모양도 달라지는걸 확인할 수 있습니다.

위 그림을 만드는 데 사용한 파이썬 코드는 다음과 같습니다.

import math
from matplotlib import pyplot as plt
def normal_cdf(x, mu=0, sigma=1):
    return (1 + math.erf((x - mu) / math.sqrt(2) / sigma)) / 2
xs = [x / 10.0 for x in range(-50,50)]
plt.plot(xs,[normal_cdf(x,sigma=1) for x in xs],'-',label='mu=0,sigma=1')
plt.plot(xs,[normal_cdf(x,sigma=2) for x in xs],'--',label='mu=0,sigma=2')
plt.plot(xs,[normal_cdf(x,sigma=0.5) for x in xs],':',label='mu=0,sigma=0.5')
plt.plot(xs,[normal_cdf(x,mu=-1) for x in xs],'-.',label='mu=-1,sigma=1')
plt.legend(loc=4)
plt.title('Various Normal cdfs')
plt.show()

표준정규분포 누적분포함수의 역함수

여러 통계학 문제를 풀다보면 특정 확률에 해당하는 표준정규분포의 $Z$값을 알고 싶은 경우가 많습니다. 예컨대 앞선 예시의 표에서 누적확률이 0.9990에 해당하는 $Z$값 3.09를 찾아보자는 것이죠. 이를 이진검색 기법을 활용해 근사하는 파이썬 코드는 다음과 같습니다.

# 정규분포 누적분포함수의 역함수
def inverse_normal_cdf(p, mu=0, sigma=1, tolerance=0.00001):
    '''이진검색을 사용해서 역함수 근사'''

    # 표준정규분포가 아니라면 표준정규분포로 변환
    if mu != 0 or sigma != 1:
        return mu + sigma * inverse_normal_cdf(p, tolerance=tolerance)

    low_z, low_p = -10.0, 0 # normal_cdf(-10)는 0에 근접
    hi_z, hi_p = 10.0, 1 # normal_cdf(10)는 1에 근접

    while hi_z - low_z > tolerance:
        mid_z = (low_z + hi_z) / 2 # 중간 값
        mid_p = normal_cdf(mid_z) # 중간 값의 누적분포 값을 계산
        if mid_p < p:
            # 중간 값이 너무 작다면 더 큰 값들을 검색
            low_z, low_p = mid_z, mid_p
        elif mid_p > p:
            # 중간 값이 너무 크다면 더 작은 값들을 검색
            hi_z, hi_p = mid_z, mid_p
        else:
            break

    return mid_z

‘inverse_normal_cdf(p=0.9990)’을 실행하면 ‘3.090238571166992’라는 값이 반환됩니다.

중심극한정리

모집단의 분포가 정규분포를 따를 경우에는 모집단에서 뽑은 표본 또한 정규분포를 따릅니다. 모집단 평균이 $μ$이고 표준편차가 $σ$, 표본의 크기가 $n$일 때 다음이 성립합니다.

모집단의 분포가 정규분포가 아닌 경우에는 이 사실이 성립하지 않습니다. 그러나 표본의 크기 $n$이 충분히 클 때에는 정규분포를 따르지 않는 임의의 모집단으로부터의 표본이라 하더라도 그 분포가 정규분포에 가깝다는 사실이 알려져 있으며 이것을 중심극한정리라고 합니다. 다시 말해 모집단의 분포가 어떤 형태이든 간에 표본의 크기가 충분히 크기만 하면 해당 표본이 근사적으로 정규분포를 따른다는 것입니다.

중심극한정리 예시

보다 쉽게 이해하기 위해 이항분포(Binomial Distribution)를 예시로 설명해보겠습니다.

입시에서 합격과 불합격, 스포츠 경기에서 승리와 패배 같이 어떤 실험이 두 가지 가능한 결과만을 가질 경우 이를 베르누이시행(Bernoulli)이라고 합니다. 예를 들어 동전을 던지는 실험은 그 결과가 앞면, 또는 뒷면인 베르누이시행이 됩니다.

성공확률이 $p$인 베르누이시행을 $n$번 반복시행할 때 성공횟수 $X$의 분포를 이항분포라고 합니다. 이때 이항분포의 평균과 분산은 각각 $np$, $np(1-p)$가 되는데요. 중심극한정리는 $n$이 적당히 크다면 $X$가 정규분포를 따르지 않지만 표본의 분포가 평균이 $np$이고 분산이 $np(1-p)$인 정규분포와 유사해진다는 점을 알려줍니다.

아래 그림은 성공확률이 0.5인 베르누이시행을 100번 반복시행했을 때 성공횟수의 분포를 히스토그램으로 그린 것입니다. 실선은 평균이 50, 분산이 25인 정규분포를 그 확률밀도함수로부터 도출한 것입니다. 두 모양이 비슷한 것을 알 수 있습니다.

위 그림을 만드는 데 쓴 파이썬 코드는 다음과 같습니다.

import math
import random
from collections import Counter
from matplotlib import pyplot as plt
def bernoulli_trial(p):
    return 1 if random.random() < p else 0
def binomial(n, p):
    return sum(bernoulli_trial(p) for _ in range(n))
def make_hist(p, n, num_points):
    data = [binomial(n,p) for _ in range(num_points)]

    # 이항분포의 표본을 막대 그래프로 표현
    histrogram = Counter(data)
    plt.bar([x - 0.4 for x in histrogram.keys()],
            [v / num_points for v in histrogram.values()],
            0.8,
            color='0.75')
    mu = p * n
    sigma = math.sqrt(n * p * (1 - p))

    # 근사된 정규분포를 라인 차트로 표현
    xs = range(min(data), max(data) + 1)
    ys = [normal_cdf(i + 0.5, mu, sigma) - normal_cdf(i - 0.5, mu, sigma) for i in xs]
    plt.plot(xs,ys)
    plt.title("Binomial Distribution vs. Normal Approximation")
    plt.show()

Comments