How to Think Probabilistically with Discrete Distributions
Photo by Bruno Bueno on Pexels
통계적 생각(Probabilistic Thinking)
100명의 올림픽 선수들의 평균 몸무게를 본다고 하자. 이 데이터 단독으로 모든 올림픽 선수들의 평균 몸무게에 대한 유효한 추정을 할 수 있을까? 여러분은 아마도 "데이터가 좀 더 필요하다"라고 할 것이고 여러분이 맞을 것이다. 실제로 관심잇는 그룹 전체를 나타내는 충분한 데이터를 갖을 수 없다. 여러분은 항상 샘플로 작업한다.
그러나 작음 샘플만을 가지고 있다고 세상이 끝이 아니며 포기해야 한다는 의미도 아니다. 또한 계속 진행하며 더 많은 데이터를 수집한다는 의미도 아니다. 왜냐하면 데이터 수집은 비용이 높고 시간도 소비하기 때문이다.
해결책으로 추론(inference)라는 전체 통계분야가 있다. 통계추론(statistics inference)은 통계이론으로 강력해지며 데이터 과학자가 주어진 샘플로 작업하여 전체 모집단(population)에 대한 예측을 할 수 있게 한다.
예를 들면, 100명의 올림픽 선수의 평균 몸무게가 약 72라고 해보자.
1896년 이후 올림픽 경기에 20만명 이상의 선수가 있었다. 따라서 우리는 모집단(이 경우에는 모든 올림픽 선수)의 평균 몸무게에 대한 잘 아는 추측을 만들 수 없다. 마냑 다른 100명의 선수 정보를 수집하고 몸무게를 도식화한 경우
유사한 데이터셋과 유사한 평균을 얻는다. 이젠 전체 선수의 평균 몸무게에 대해 이야기할 준비가 되었는가? 얼마나 많이 매번 더욱더 어려워지는 더 많은 데이터를 수집하였는지는 중요하지 않다. 우리는 결코 충분한 데이터를 갖을 수 없다. 그러나 파이썬의 통계도구를 사용하여 우리가 원하는 만큰 많이 데이터 획들을 시뮬레이션 할 수 있다.
올림픽 선수의 몸무게 데이터를 100번 시뮬레이션하고 결과를 도식화해보자.
시뮬레이션을 사용하여 자신있게 모든 올림픽 선수의 평균 몸무게는 아마(probably) 약 71이나 72라고 말할 수 있다.
통계적 생각은 통계적 추론의 핵심이고 이 글에서 실제 시나리오를 모델링하기 위해 사용되는 이산확률분포(discrete probability distribution)에 대해 알아본다.
기초 확률 복습(Basic Probability Refresher)
분포를 알아보기 전에 확률 기초에 대해 알아보자.
확률은 무엇인가 발생하는 것의 가능성(likelihood)을 나타낸다.
통계학 창시자들은 확률게임(game of chance)에 대한 확률을 연구했다. 따라서 많은 사람들처럼 우리도 동전 던지기의 기본 예제로 시작한다.
동전을 던지면 단 2가지 결과만이 있을 수 있다. 동전이 앞면이 되거나 뒷면이 된다. 동전의 양면 모두가 동일한 비중(균일한 동전)이기 때문에 동전이 앞면 또는 뒷면이 나오는 확률은 50% 또는 0.5라고 할 수 있다.
과학적 표기법으로 무엇인가 발생하는 확률은 다음과 같이 표기된다.
"동전 앞면(또는 뒷면)이 나오는 확률은 0.5이다"라고 읽는다. 또다른 예로는
암일 확률이 1%
다른 맥락에서 자주 나오는 확률에 관한 몇가지 다른 용어가 있다.
- Experiment(실험) : 관측 가능한 결과를 갖는 활동
- Trials(시행) : 실험의 반복
- Outcome(결과) : 각 시행의 결과
- Sample space(표본공간) : 모든 가능한 결과의 집합(set)
- Event(사건) : 표본공간의 하위집합(subset)
예를 들면, 주사위 굴리기는 실험이다. 여러번 주사위를 굴리는 것은 동일한 실험의 시행으로 간주된다. 주사위 굴리기의 가능한 결과는 1, 2 등이 될 수 있다. 모든 가능한 결과의 집합(set), 이 경우에는 1, 2, 3, 4, 5, 6은 표본공간이다. 사건은 실험의 어떤 결과로 생각될 수 있다. 예를 들면 주사위가 6이 나오는 것이다.
또한 여사상(complimentary event)이라는 용어도 있다. 예를 들면 동전이 앞면이 나오면 그 여사상은 뒷면이다. 주사위가 5가 나오면 여사상은 주사위의 다른 5면이 될 수 있다.
여사상의 확률은 1에서 주어진 결과의 확률을 빼서 구할 수 있다.
주사위가 5가 나오는 확률은 1/6이지만 1에서 다른면의 확률을 더한값을 빼서 구할 수도 있다.
확률분포(Probability Distribution)
확률분포를 이해하기 위해 동전던지기 예제로 계속 진행해 보자. 확률변수(random variable) X가 하나의 균일한 동전 던지기의 결과를 저장하기 위해 사용되었다면 X는 앞면에 대해 0.5, 뒷면에 대해 0.5인 분포를 갖는다.
확률분포는 실험에 대해 각 가능한 결과의 확률을 기록한다.
프로그래밍으로 분포를 다루고 있다면 보통 집합(set) 또는 배열(array)로 제공된다. (pandas series 또는 numpy arrays) 동전 던지기에 예제에 대해서는 다음과 같이 된다.
이런 방식으로 확률변수의 확률분포를 구성할 수 있다.
주머니에 10개의 공이 있는 또다른 예제를 보자. 6개는 흰색, 3개는 검정색, 1개는 빨간색이다. 주머니에서 무작위로 공을 선택하면 3가지 결과만이 존재한다. 따라서 분포는 3개의 요소를 갖는다. X(확률변수)로 실험의 결과를 표시하고 분포를 만들어 보자.
대규모 실험에서는 이런 방법으로 빠르게 각각의 가능한 결과를 기록하는 것이 불가능하다는 것을 눈치챘을 것이다. 예를 들면, 실험이 100명의 학생에 대해 주업중 수학 시험이라면 큰 문제에 빠질 것이다. 고맘게도 통계학에서 확률분포를 훨씬 쉽게 표시하고 생성하는 함수가 있다.
확률질량함수(Probability Mass Function, PMF)
확률밀도함수를 알아보기 전에 이산(discrete)분포가 무엇을 의미하는지 정의하자. 설명적 통계학에서 이산 데이터는 셀수 있는 것 즉, 정수값(integer value)로 기록되는 값을 포함한다. 10번의 동전던지기중 앞면이 나온 횟수, 수업중 시험에서 얻은 점수, 축구경기에서 골수는 모두 이산 데이터의 예이다.
이산 결과를 갖는 실험을 위해 사용된 확률변수(random variable)는 이산확률분포를 갖는다.
주사위 굴릭 또한 결과가 2.5가 나올 수 없기 때문에 이산 결과만을 갖는다. 확률변수의 타입과 분포를 구분하는 것은 매우 중요하다.
이산분포의 대응 상대는 연속된 값을 다룰 때 사용되는 연속확률분포(continuous probability distribution)이다.(소수점을 갖는 측정된 데이터) 연속확률분포에 대해서는 이 글에서는 다루지 않는다.
모든 분포는 그 자신의 확률 함수와 함께 나온다. 보통 이산확률분포는 확률질량함수(PMF)를 사용한다. 지난 단락에서 대규모 실험의 각 가능한 결과를 작성하는 것은 불가능하다는 것을 보았다. PMFs를 사용하여 그 절차(process)를 만들고 표시할 수 있다.
수업중 시험 실험으로 되돌아가서 우선, 실험의 결과를 취하여 바로 반환하는 함수 f를 정의한다.
확률에서 확률변수는 대문자로 표시되고 확률변수가 취할 수 있는 가능한 값은 소문자로 표시된다는 것을 기억하자. 수업중 테스트의 결과를 확률변수 X에 할당하면 가능한 테스트 점수는 소문자 x가 될 수 있다. 이 정보를 사용하여 어떤 시험 점수를 얻을 가능성을 표시할 수 있다.
이제 PMF를 정의해 보자.
확률질량함수(PMF, Probability Mass Function)은 임의의 실험에 대한 이산 결과를 취하여 각 결과의 가능성을 계산한다.
각 테스트 점수의 확률을 반환하도록 앞선 함수를 변경하면 다음과 같이 된다.
그러면 이것이 일반적인 이산확률분포의 PMF이다. 이름 f가 임의적이기 때문에 더 적합한 이름으로 바꿀 수 있다.
실제로 여러분은 직접 저 함수를 계산할 필요가 없고 소프트웨어가 이를 계산하도록 한다. 파이썬에서 어떠한 분포의 PMF는 empiricaldist 라이브러리로 계산할 수 있다. 이 라이브러리를 선치하고 import하려면 아래 코드를 사용한다.
from empiricaldist import Pmf # pip install empiricaldist
주사위 굴리기의 PMF를 시각화보자.
outcomes = [1, 2, 3, 4, 5, 6]
pmf 서브모듈은 일련의 결과를 취하여 PMF를 계산하는 from_seq 메소드를 포함한다.
pmf_die = Pmf.from_seq(outcomes)
>>> pmf_die
결과는 인덱스에 유일한 결과와 probs.아래 확률을 갖는 pandas.Series이다. 만약 두면이 6인 결함이 있는 주사위라면 PMF는 다음과 같을 것이다.
defective_die = [1, 2, 3, 4, 6, 6]
pmf_defective = Pmf.from_seq(defective_die)
>>> pmf_defective
더 많은 일련의 예제로 student marks dataset을 사용한다.
marks = pd.read_csv('data/student_performance.csv')
>>> marks.head()
>>> marks.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 gender 1000 non-null object
1 race/ethnicity 1000 non-null object
2 parental level of education 1000 non-null object
3 lunch 1000 non-null object
4 test preparation course 1000 non-null object
5 math score 1000 non-null int64
6 reading score 1000 non-null int64
7 writing score 1000 non-null int64
dtypes: int64(3), object(5)
memory usage: 62.6+ KB
수학 시험점수가 우리의 실험이었고 PMF를 계산해보자.
pmf_math = Pmf.from_seq(marks['math score'])
>>> pmf_math
예상된 것처럼 결과는 모든 유일한 수학점수의 확률을 포함한다. 예를 들면, 1000점 중에서 시험점수를 무작위로 선택한다면 100점은 0.7% 확률이다.(위 표의 맨 마지막 줄)
PMFs의 이점은 시각화할때 나타날 수 있다. 선그래프 또는 산점도를 사용할 수 있다.
# Create figure and axes objects
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 6))
# Plot as a line plot
ax1.plot(pmf_math)
ax1.set(title='PMF of Math Scores Using a Lineplot',
xlabel='Score', ylabel='Probability')
# Plot as a scatterplot
ax2.plot(pmf_math, marker='.', linestyle='none')
ax2.set(title='PMF of Math Scores Using a Scatterplot',
xlabel='Score', ylabel='Probability')
plt.show();
분포의 퍼짐(spread)를 보기에 선그래프(또는 히스토그램)가 더 나은 선택인것 같다.
베르누이 시행(Bernoulli Trials)
확률과 통계에서 베르누이 시행은 "성공"과 "실패" 정확히 두가지 결과를 갖는 실험이다. 예를 들면, 동전던지기가 베르누이 시행의 한 예이다. 의학적 실험의 결과도 양성(positive)과 음성(negative) 어느한쪽을 갖기 때문에 베르누이 시행으로 간주될 수 있다.
"성공"과 "실패" 용어는 단지 베르누이 시행의 희망하는 결과에 대한 레이블일 뿐이다. 우리가 동전 앞면이 나오는 것을 선호한다면 앞면이 성공으로 간주되고 반대로도 마찬가지이다.
베르누이 시행을 시뮬레이션하려면 numpy를 import해야 한다.
import numpy as np
이후 부분에서는 유용한 함수의 범위를 갖는 의사난수(pseudo-random numbers)를 생성하는 넘파이의 random 메소드를 주로 사용한다. 일정한 코드 예제를 만들기 위해 임의의 상태를 seed 할 수 있다.
>>> np.random.seed(2021)
두가지 결과를 갖는 어떤 실험을 시뮬레이션하는 것, 우리는 0과 1사이 임의의 숫자를 뽑는 np.random.random함수를 사용한다. 간격(interval)내 각 숫자는 선택될 동일한 확률을 갖는다.
if np.random.random() >= 0.5:
print('Heads')
else:
print('Tails')
Heads
동전 던지기 예제에서 0.5로 성공 활률을 설정한다. 이는 임의적인 선택이다. 편향된 동전(앞,뒤가 나올 확률이 동일하지 않은 동전)을 던지면 성공의 확률은 앞면이 나올 확률에 따라 다를 것이다.
암 진단을 시뮬레이션한다면 성공의 확률은 암은 희귀한 질병이기 때문에 90%일 것이다.
p_success = 0.1 # threshold for cancer
if np.random.random() > p_success:
print("No cancer")
else:
print("Cancerous")
No cancer
또한 베르누이 확률변수(random Bernoulli variable)가 있다. 변수로 베르누이 시행의 결과를 저장하는 것은 변수가 베르누이 변수로 바뀐다. 예를 들면, X가 동전 던지기의 결과를 저장하는데 사용되었다면 X는 베르누이 변수가 된다.(확률에서 확률변수는 대문자로 표시된다.)
흥미로둔 것은 단일 베르누이 시행이 베르누이 분포로써도 간주될 수 있다는 것이다. 위키에 따르면 베르누이 분포는 성공 $p$의 확률로 값과 성공 $q = 1 - p$의 확률로 0의 값을 갖는 확률변수의 분포이다.
베르누이 분포의 PMF 식은 다음과 같다.
베르누이 분포의 확률질량함수(PMF)는 x와 p 두개의 파라미터를 갖는다. x는 1 또는 1일 될 수 있다. p는 성공 확률을 인코딩한다.
이항분포(binomial Distribution)
또하른 유명하면서 중요한 분포는 이항분포이다. 우리는 여전히 예/아니오 또는 성공/실패를 묻는 실험을 다룰 것이다. 사실 이항분포는 베르누이 분포의 확대 버전이다.
이항분포는 p의 성공확률을 가진 n번 베르누이 시행에서 성공의 합이다. 길고 복잡하니 예제로 쪼게어 알아보자.
우리의 실험이 3번의 동전 던지기 중 3번 앞면이 나올 확률을 아는 것이라고 하자. 이번 동전은 앞면이 나올 확률이 0.3으로 치우쳐져 있다. 이 실험에서 각 시행은 한번의 시행에서 앞면을 얻는 것이 다음 시행의 결과에 영향이 없다는 것을 의미하는 독립(independent)이다.
실험의 모든 가능한 결과를 목록으로 만들기 위해 진리표(truth table)라는 것을 사용한다. 3번의 동전 던지기에 대한 진리표는 다음과 같다.
모든 8가지 결과 중, 첫번째만이 조건을 만족한다.
앞면이 나올 확률이 0.3인것에 주의하자. 시행이 이번 예제처럼 독립이라면 결합확률(joint probability)은 곱셈 법칙 : $0.3 \times 0.3 \times 0.3 = 0.027$으로 구할 수 있다. 따라서 편향이 있는 동전을 3번 던져 3번 앞면이 나올 확률은 2.7%이다.
다시 한번, 어떻게 이것을 일반화할까? 즉, p의 성공확률을 가진 n번의 독립적인 베르누이 시행중 k번 성공하는 확률을 어떻게 찾을 수 있을까?
예를 들면, 우리는 p가 0.45인 동전을 30회 던져 10번 앞면이 나올 확률이 무엇인지 알고 싶을 수도 있다. 이를 시각적으로 다시 보자.
n번의 시행중 p의 확률로 정확하게 k번 성공과 1-p의 확률로 정확하게 n-k 실패를 원한다. 그러나 30번 동전 던지기를 하면 10번 앞면이 나오는 1가지 이상 방법이 있다. 처음 앞면을 얻고 나머지는 뒷면 또는 처음 5개의 앞면 얻고 뒷면 일부와 나머지 앞면, 또는 앞면, 뒷면의 순서일 수도 있다. 대규모 실험에 대한 진리표 작성은 불가능하다 그러나 조합(combinatorics)을 사용하여 30번 동전 던지기중 10번이 앞면이 나오는 방법의 수를 계산할 수 있고 그 식은 다음과 같다.
위 식은 n번 시행 중 k번 성공을 얻는 조합식이다. 30번중 10번 앞면을 얻는 방법의 수를 구하면 다음과 같다.
이제 30번 동전을 던져 10번 앞면이 나오는 방법의 수를 구했다. 조합과 베르누이 분포에 대한 두 식을 조합하여 이 확률을 계산할 수 있다.
결과를 보면 $p=0.45$로 30번 동전을 던져 10번 앞면이 나올 확률은 0.066이다. 이식을 다시 보자.
위 식은 이항분포의 확률밀도함수(PMF)의 식이기도 하다.
코드로 가서 이항분포를 시뮬레이션하기 위해 넘파이의 np.random.binomial 함수를 사용한다.
>>> np.random.binomial(n=50, p=0.65)
28
여러분은 위 코드를 n은 베르누이 시행의 수이고 p는 각 시행에서 성공 확률이다라고 해석할 수 있다. 위 실험에서 50번의 심험중 28번 성공하였다. 이 실험은 수학 시험에서 통과/실패, 몇몇 의학적 테스트에서 양성/음성 또는 최근 50명의 신생아중 남자/여자의 수가 될 수 있다.
np.random.binomial은 n회 시행과 p의 확률로 단일 실험만을 시뮬레이션한다. 실험을 반복하기 위한 인자(argument)인 size를 사용할 수 있다.
>>> np.random.binomial(n=10, p=0.77, size=10)
array([ 8, 6, 8, 7, 9, 8, 7, 9, 8, 10])
에를 들면, 실험을 아주 아주 많이 시행하고 성공의 평균수를 찾아 p=0.45인 동전 던지기 30번중 10번이 앞면인 확률을 알 수 있다.
# Repeat flipping 30 coins 10k times
b_dist = np.random.binomial(n=30, p=0.45, size=10000)
>>> b_dist
array([14, 15, 16, ..., 16, 14, 9])
우선 실험을 10,000회 반복하고 정확히 10번 앞면이 몇번이나 나오는지 찾는다.
# Record how many of them are equal to 10
n_10 = np.sum(b_dist == 10)
>>> n_10
667
확률을 구하기 위해 n_10을 10,000으로 나눌 수 있다.
>>> n_10 / 10000
0.0667
보이는 것처럼 결과는 이상분포의 PMF식을 사용하여 찾은 값에 아주 가깝다. 이는 단지 값을 찾는 더 간단한 방법일 뿐이다.
결론
이 글에서는 가장 중요한 두가지 이산확률분포를 알아보았다. 이항분포와 매우 유사한 푸아송(Poisson)분포같은 다른 것도 있다. 통계적 추론을 한다면 어떻게 데이터 획득하는지를 식별하는 것이 기본이다. 그러면 주어진 샘플로 전체 모집단에 대한 잘 알려진 추축을 할 수 있다.
이 절차에서 실험의 형태, 분포 그리고 표본공간을 식별하는 것이 중요하다. 이항 분포를 식별하는 것은 때때로 어렵다. 따라서 실험이 이항인지 알아내기 위한 4개의 질문으로 이 글을 마친다.( statisticshowto.com에서 파생됨.)
- 고정된 시행 횟수가 있는가?
- (성공/실패) 단 2가지 가능한 결과만 있는가?
- 각 시행에 대해 결과가 독립인가?
- 성공확률이 각 시행에서 동일한가?