[Python] 확률 분포 모형

0ㅑ채
|2024. 2. 26. 19:50

1. 확률적 데이터와 확률 변수

1-1) 확률적 데이터

- 결정론적 데이터: 실험, 측정, 조사 등을 통해 어떤 데이터 값을 반복적으로 얻을 때 누가 언제 얻더라도 항상 동일한 값이 나오는 것

- 확률적 데이터: 정확히 예측할 수 없는 값 (ex. 혈압)

- 대다수 데이터는 확률적 데이터다. 여러 조건이나 상황에 따라 데이터 값이 변할 수 있고 측정 시에 발생하는 오차 때문!

 

1-2) 분포

- 확률적 데이터를 살펴보면 어떤 값은 자주 등장하고 어떤 값은 드물게 등장하는데

  어떤 값이 자주 나오고 어떤 값이 드물게 나오는지를 나타내는 정보를 의미

- 범주형 데이터 - 교차분할표, plot으로 확인

- 연속형 데이터 - binning 후 히스토그램 등으로 확인

 

1-3) 분포 추정을 위한 기술 통계값

- 평균, 중앙값, 최빈값

- 편차, 분산, 표준편차, 평균절대편차, 중위값의 중위절대편차

- 범위(Range), 순서통계량(Order Statistics), 백분위수(Percentile), 사분위수(Interquartile Range)

 

1-4) 대칭 분포

- 좌우가 동일한 모양의 분포

- 분포 모양에 따른 평균, 중앙값, 최빈값의 특성

  • 평균을 기준으로 대칭이면, 평균 = 중앙값
  • 대칭 분포이면서 하나의 최대값만을 가지는 단봉분포이면, 최빈값 = 평균
  • 대칭 분포를 비대칭으로 만드는 데이터가 추가되면,   평균 -> 중앙 값 -> 최빈값   순으로 영향

 

1-5) 표본 비대칭도 - 왜도(Skewness)

- 평균과의 거리를 세제곱해서 구한 특징 값

- 표본 비대칭도가 0이면, 분포가 대칭

- 표본 비대칭도가 음수면, 표본 평균에서 왼쪽 값의 표본이 나올 가능성이 더 높음

 

1-6) 표본 첨도(kurtosis)

- 정규 분포보다 중앙에 값이 몰려있는 정도

- 평균과의 거리를 네제곱 해서 구한 특징 값

- 데이터가 중앙에 몰려있는 정도를 정밀하게 비교하는 데 사용

- 정규 분포보다 첨도가 높으면 양수가 되고 정규 분포포다 첨도가 낮으면 음수로 정의

  • 값이 3이면 정규 분포와 동일한 분포
  • 값이 3보다 작으면 정규 분포보다 꼬리가 얇은(넓게 펼쳐짐) 분포
  • 값이 3보다 크면 정규 분포보다 꼬리가 두꺼운(좁고 뾰족) 분포

 

 

1-7) 표본 모멘트

- k 제곱을 한 모멘트를 k차 표본 모멘트

- 평균은 1차 모멘트, 분산이 2차 모멘트, 비대칭도가 3차 모멘트, 첨도가 4차 모멘트

- scipy.stats 패키지의 skew 함수(왜도) kurtosis 함수(첨도),  moment 함수(데이터와 차수) 설정해서 구함

import numpy as np
import scipy as sp

# 샘플 데이터 생성
x = np.random.normal(size = 1000)
print(x)
[ 7.09400466e-01  2.90488628e-02  1.57024913e+00 -2.54960161e+00
  3.28136877e-01  1.22922309e-01  2.48529952e+00 -8.75967514e-01
 -1.67285266e+00  1.24635639e-01 -5.71475047e-01  5.97966093e-02
  1.05283163e+00  1.39524451e-01 -1.60198487e+00  1.34187712e+00
...
print("표본 평균 :", np.mean(x))
print("1차 모멘트 :", sp.stats.moment(x,1))

print("표본 분산 :", np.var(x))
print("2차 모멘트 :", sp.stats.moment(x,2))

print("표본 왜도 :", sp.stats.skew(x))
print("3차 모멘트 :", sp.stats.moment(x,3))

print("표본 첨도 :", sp.stats.kurtosis(x))
print("4차 모멘트 :", sp.stats.moment(x,4))
표본 평균 : 0.013852167418918475
1차 모멘트 : 0.0

표본 분산 : 0.9187724279137195
2차 모멘트 : 0.9187724279137195

표본 왜도 : 0.031172968025046505
3차 모멘트 : 0.02745301735258748

표본 첨도 : -0.11861329022879596
4차 모멘트 : 2.4323017710014816
  • excess kurtosis : 정규 분포의 첨도는 3인데 해석을 편하게 하기 위해서 3을 빼서 0을 만들기도 함
  • 2개의 값이 다른이유는 kurtosis는 4차 모멘트에서 3을 뺸 값이기 때문

 

 

2. 확률 분포 함수

2-1) 확률 질량 함수

- 어떤 사건의 확률 값을 이용해서 다른 사건의 확률 값 계산 가능

- 유한개의 사건이 존재하는 경우 각 단순 사건에 대한 확률만 정의하는 함수

- 단순 사건은 서로 교집합을 가지지 않는 사건 (사건이 이산적인 데이터)

  • 주사위에서 1, 2, 3, 4, 5, 6은 다른사건과 교집합을 가지지 않으므로 단순 사건, 이러한 단순 사건의 확률 합은 1
  • 확률 질량 함수는 각 사건이 발생할 수 있는 확률을 계산해주는 함수
    • 범주형이면 각 사건의 확률을 명확하게 나타낼 수 있지만 연속형은 구간이 필요하다. 
    • 연속형 데이터에서는 2개의 데이터 사이에 무한대의 데이터가 존재할 수 있기 때문에 하나의 값에 해당하는 확률은 0임
    • 둥그런 원반에 화살을 쏴서 특정 각도에 해당하는 확률을 계산
      원반에서 0 ~ 180 도 사이에 위치할 확률은? 1/2

- 소문자 p로 표시

 

# 확률 질량 함수 출력

#주사위의 각 눈을 나타내는 수 - 사건
x = np.arange(1, 7)

# 조작된 주사위의 확률질량함수
y = np.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.5])

plt.stem(x, y)

 

 

2-2) 확률 밀도(분포) 함수 (Probability Density Function, pdf)

- 표본 수가 무한대(ex.연속형 데이터)인 경우 확률 질량 함수로 정의할 수 없음

    • 회전하는 원반에 화살을 쏘고 화살이 박힌 위치의 각도를 결정하는 문제에서 정확하게 0도가 될 확률은 ?
    • 이런 경우 구간을 이용해서 확률 정의
      각도가 0도 보다 크거나 같고 30보다 작은 경우의 확률은 정의가 가능 -> 동일한 가능성을 가진 사건이 12개(유한개)로 정의 가능 

- 확률 밀도 함수는 구간에 대한 확률을 정의한 함수. pdf

 

2-3) 누적 밀도(분포) 함수( Cumulative Distribution Function - cdf )

- 구간을 설정하기 위해 2개의 값이 필요한 확률 밀도 함수와 달리 하나의 값만으로 사건을 정의하는 방법이다.
- 시작점의 위치를 음의 무한대로 설정하고 하나의 값을 무조건 종료점으로 판정해서 확률을 정의한 함수

  • 음의 무한대에 대한 누적분포함수 값 = 0
  • 양의 무한대에 대한 누적분포함수 값 = 1
  • 입력이 크면 누적분포함수의 값은 같거나 커짐

- 0에서 시작해서 천천히 증가하면서 1로 다가가는 형태

- 단조 증가 설질에 의해 절대로 내려가지는 않음

 

 

 

3. scipy

- 수치 해석 기능을 제공하는 패키지

- stats 서브 패키지에서 확률 분포 분석을 위한 다양한 기능 제공

- 확률 분포 클래스 

  •  

 

3-1) 사용법

- 정규 분포 객체 생성

  • 모수 2개 (loc - 기대값·평균, scale - 표준편차)를 가지고 생성
  • rv = scipy.stats.norm( loc=1, scale=2 )  #평균이 1이고 표준편차가 2인 정규분포객체 생성

 

3-2) 확률 분포 객체의 함수

- pmf: probability mass function 확률 질량 함수 (이 사건이 발생할 확률)

- pdf: probability density function 확률 밀도 함수 (구간에 대한 확률값)

- cdf: cumulative distribution function 누적 분포 함수 (0에서부터 x까지의 확률)

----------------------어떤 특정한 사건의 확률

- ppf: percent point function 누적 분포 함수의 역함수 

- sf: survival function = 생존 함수 (1-누적분포함수)   (x부터 끝까지의 확률)

- isf: inverse survival function 생존 함수의 역함수

- rvs: random variable sampling 랜덤 표본 생성

----------------------- 확률을 통해 사건을 발견

 

# 확률 밀도 함수

xx = np.linspace(-8, 8, 100)

rv = sp.stats.norm(loc=1, scale=2)

pdf = rv.pdf(xx)
plt.plot(xx, pdf)

 

 

3-5) 정규 분포 객체의 누적 분포 함수

xx = np.linspace(-8, 8, 100)

rv = sp.stats.norm(loc=1, scale=2)

pdf = rv.cdf(xx)
plt.plot(xx, cdf)

 

 

 

 

 

4. 확률 분포 모형

4-1) 베르누이 분포

- 베르누이 시행

  • 결과가 두가지 중 하나로만 나오는 실험이나 시행
  • ex. 동전을 던져서 앞면이나 뒷면이 나오는 경우

- 베르누이 확률 변수

  • 베르누이 시행 결과를 0 또는 1로 바꾼 것
    • -1이나 1로 표기하기도 함
  • 이산 확률 변수다. 두가지 중에 하나의 경우만 나올 수 있기 때문.

- 베르누이 분포는 1이 나올 확률을 의미하는 모수를 가지게 됨

  • 0이 나올 확률은   1 - (1이 나올 확률)  을 하면 나오기 때문에 별도로 정의하지 않는다.

- scipy.stats.bernoulli 클래스로 구현

  • p 인수로 분포 모수 설정
  • 기술 통계값들은 discribe() 함수 이용, 결과는 DescribeResult 타입으로 리턴.
    • 이 안에 데이터 개수, 최대, 최소, 평균, 분산, 왜도, 첨도를 순차적으로 저장
  • 이산 확률 변수를 이용하기 때문에 확률질량함수 pmf 함수를 사용 가능
# 확률변수 0.6으로 설정 
rv = sp.stats.bernoulli(0.6)
rv
#나올 수 있는 경우
xx = [0, 1]

#확률 질량 함수
plt.bar(xx, rv.pmf(xx))
#X축 수정
plt.xticks([0,1], ['x=0', 'x=1'])

 

 

- 시뮬레이션 (샘플링)

  • random_state는 seed 설정
  • seed를 고정시키면 동일한 데이터가 샘플링되고
  • seed를 고정하지 않으면 현재 시간 값을 seed로 설정하기 때문에 무작위로 샘플링
    • 머신러닝에서는 일반적으로 seed 고정
    • 동일한 데이터를 샘플링해서 비교해야 모델 또는 알고리즘 간 비교가 가능하기 때문
#샘플 데이터 생성
x = rv.rvs(1000, random_state = 42)
sns.countplot(x=x)

 

 

- 이론적인 모델과 시뮬레이션 한 결과 비교

  • 정확히는 주장과 비교
  • 여러차례 시뮬레이션을 한 결과가 비교를 해서 주장이 어느정도 타당성을 갖는지 확인
y = np.bincount(x, minlength=2) / float(len(x))
print(y)
[0.387   0.613]

 

#결과 비교

df = pd.DataFrame({'이론':rv.pmf(xx), '시뮬레이션':y})
df.index = [0, 1]
df

 

# 시뮬레이션 결과

result = sp.stats.describe(x)
print(result)
DescribeResult(
nobs=1000, 
minmax=(0, 1), 
mean=0.613, 
variance=0.23746846846846847, 
skewness=-0.46400506298458166, 
kurtosis=-1.7846993015246748)

 

 

 

4-2) 이항 분포

- 베르누이 시행을 N번 반복할 때, 어떤 경우에는 N번 모두 1이 나오고, 어떤 경우에는 1이 한번도 안나올 수 있는데

이때 1이 나온 횟수를 X라고 하면 X는 0부터 N까지를 가지는 확률 변수다. 이를 이항분포를 따르는 확률변수라고 한다.

- 표본 데이터가 1개이면 베르누이 분포, 표본 데이터가 여러 개면 이항 분포

- scipy.stats 서브 패키지의 binom 클래스로 구현

- rv = sp.stats.binom( 전체 시행 횟수, 1이 나올 확률 )

rv = sp.stats.binom(10 ,0.5) #1이 나올 확률은 0.5이고 10번 수행

xx = np.arange(11)
plt.bar(xx, rv.pmf(xx), align = "center")

 

# 시뮬레이션

x = rv.rvs(1000) # 1000번 시뮬레이션
sns.countplot(x=x)
print(x[x > 7])
[8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9 8 8 8 8 8 8 8 8 8 8
 8 8 8 8 8 8 9 8 9 8]

 

# 앞 뒷면 확률이 동일한 동전을 100번 던졌을 때 앞면이 60회 이상 나올 확률

  • 실험횟수: n
  • 확률: p
  • 발생횟수: k
#pmf 이용 : 0 ~ 59까지 나올 확률을 모두 더한 후 1에서 확률 빼기

#cdf 이용 : 0번부터 나올 확률의 누적 확률
p = sp.stats.binom.cdf(n=100, p=0.5, k=59)
print(1-p)

#sf 이용 : 생존 함수는 1-cdf의 값을 가지는 함수
p = sp.stats.binom.sf(n=100, p=0.5, k=59)
print(p)
0.02844396682049044
0.028443966820490392

 

# 앞면이 20~60번 나올 확률

#cdf 이용 : 0번부터 나올 확률의 누적 확률
p1 = sp.stats.binom.cdf(n=100, p=0.5, k=19)
p2 = sp.stats.binom.cdf(n=100, p=0.5, k=60)
print(p2 - p1)
0.9823998997560095

 

 

- 퍼센트 포인트 함수 ppf 활용: 모수로 구하고자 하는 확률(p) 대입

  • 원하는 지점의 확률 q
  • 비율을 설정해서 횟수를 구해주는 함수
  • 동전을 던졌을 때 n회 이상 나올 확률이 80% 넘는 지점은?
p = sp.stats.binom.ppf(n=100, p=0.5, q=0.8)
print(p)

p1 = sp.stats.binom.cdf(n=100, p=0.5, k=p)
p2 = sp.stats.binom.cdf(n=100, p=0.5, k=p+1)

print(p1)
print(p2)
54.0
7.888609052210118e-31
7.967495142732219e-29

 

 

- 베르누이 분포와 이항분포 활용 - 스팸 메일 필터

  • 현재까지 모든 메일을 확인해서 스팸으로 가정할 수 있는 필터의 확률을 구함
  • 베르누이 분포를 이용!
  • 각 메일을 단어 단위로 원핫 인코딩 수행
  • 각 단어가 메일에 포함된 개수와 스팸 메일에 포함된 개수의 비율 계산
  • 현재 메일이 온 경우 메일에 포함된 내용들을 원핫 인코딩 해서 기존에 만들어진 단어들의 스팸 비율 확인해서 판정 
감성분석, 욕설 방지 등에 사용
utf8mb4 : 글자 + 이모티콘까지

 

 

4-3) 카테고리 분포

- 이항 분포가 성공인지 실패인지를 구별하는 거라면

  카테고리 분포는 2개 이상의 경우 중 하나가 나오는 경우

  대표적인 경우가 주사위 (k=6)

- 스칼라 값으로 표현하는 경우가 많지만 확률 변수에서는 카테고리를 원핫인코딩해서 표현

 

각 열로부터 각 베르누이 확률 분포의 모수 추정값



순서가 명확한 의미를 가지면 라벨인코딩, 순서가 의미없이 독립적이면 원핫인코딩

원핫인코딩을 하는 이유:
서로 독립적인 데이터들의 거리를 동일하게 맞추기 위해!
그렇지 않으면 편향이 될 가능성이 높다.

 

- scipy에서는 카테고리 분포 클래스 제공 X

  • 다항분포를 위한 multinomial 클래스에서 시행횟수를 1로 설정하면 카테고리 분포가 됨

- 베르누이 분포가 이진 분류 문제에 사용된 것처럼 카테고리 분포는 다중 분류 클래스에 이용

  • 모수는 원래 카테고리 개수와 카테고리별 확률이 되어야 하는데 scipy를 이용할 때는 1과 각 카테고리 확률의 vector가 됨
  • 시뮬레이션에 사용되는 데이터는 원핫인코딩 된 데이터
  • 원핫 인코딩은 pandas의 get_dummies()를 이용하거나 sklearn.preprocessing 패키지의 Encoder 클래스를 이용해서 수행 가능
#카테고리 별 확률
mu = [0.1, 0.1, 0.1, 0.1, 0.1, 0.5]

#카테고리 분포 인스턴스
rv = sp.stats.multinomial(1, mu)

#데이터 생성
xx = np.arange(1, 7)

#원핫 인코딩
xx_ohe = pd.get_dummies(xx)
#확률 질량 함수 출력
plt.bar(xx, rv.pmf(xx_ohe.values))
  • 통계는 scikit-learn을 이용하는 전처리
  • 머신러닝에는 numpy.ndarray가 기본 자료형
# 시뮬레이션
X = rv.rvs(100)
print(X)
[[0 0 0 0 0 1]
 [0 1 0 0 0 0]
 [0 0 0 0 0 1]
 [0 0 0 0 0 1]
 [0 0 0 0 0 1]
 [1 0 0 0 0 0]
#확인해보면 6이 굉장히 많다.

 

 

 

4-4) 다항 분포

- 베르누이 시행을 여러번 한 결과가 이항분포, 카테고리 시행을 여러번 한 결과가 다항분포

- scipy.stats.multinomial(시행횟수, 확률) 이용해서 인스턴스 생성

- 시뮬레이션을 하게 되면 각 카테고리의 횟수가 리턴됨

## 다항분포 - 카테고리 분포를 여러번 수행한 분포
rv = sp.stats.multinomial(100, [1/6, 1/6, 1/6, 1/6, 1/6, 1/6])

#1000번 시뮬레이션
X = rv.rvs(1000)
print(X[:5])
[[23 16 14 11 18 18]
 [24 15 17 18 13 13]
 [21 18 15 16 16 14]
 [13 12 24 12 19 20]
 [17 16 14 11 19 23]]

 

# 시각화

df = pd.DataFrame(X).stack().reset_index()
df.columns =['시도', '클래스', '데이터개수']
df.head(12)
  • 시행 횟수가 많으면 swarmplot 보다는 violinplot이 낫다.
sns.swarmplot(x='클래스', y='데이터개수', data=df)
sns.violinplot(x='클래스', y='데이터개수', data=df)

 

 

4-5) 균일 분포

- 모든 확률 변수에 대해 균일한 분포를 갖는 모형

- scipy.stats.uniform 

  • 버스의 배차 간격이 일정한 경우
  • 시간표를 모르고 버스 정류장에 나갔을 때 평균 대기 시간은?

- Rvs(loc=0, scale =1, size=1, random_state=None)

  • Loc : 기댓값, 평균
  • Scale : 범위라고도 하는데 표준 편차
  • Size : 개수
  • Random_state : seed값
  • seed값은 여기서 설정하지 않고 numpy를 이용해서 미리 설정한 후 사용해도 됩니다.

- 균일 분포는 동일한 크기의 구간에 대한 확률이 거의 비슷하지만, 2개의 균일분포 합은 그렇지 않다.

 

# 마을에 다니는 버스의 주기는 1시간일 때, 버스 시간표를 모르는 사람이 정류장에 갔을 때 대기 시간이 7분 이내일 확률

  • 균일분포
  • 편차(scale): 60
result = sp.stats.uniform.cdf(scale=60, x=7)
print(result)
0.11666666666666667

 

# 대기 시간이 5~20분 이내일 확률

result1 = sp.stats.uniform.cdf(scale=60, x=20)
result2 = sp.stats.uniform.cdf(scale=60, x=5)
print(result1 - result2)
0.25

 

# 50분 이상일 확률

result1 = sp.stats.uniform.cdf(scale=60, x=50)
result2 = sp.stats.uniform.sf(scale=60, x=50)
print(1- result1)
print(result2)
0.16666666666666663
0.16666666666666663
  • 연속형 분포이기 때문에 50을 0으로 잡고 설정!

 

# 시간이 m분 이내일 확률이 73%가 되는 지점은?

result = sp.stats.uniform.ppf(scale=60, q=0.73)
result
43.8

 

 

4-6) 정규분포

- 가우시안 정규분포 (Gaussian normal distribution) 또는 정규 분포

- 자연 현상에서 나타나는 숫자를 확률 모형으로 나타낼 때 가장 많이 사용되는 모형

- 데이터가 평균을 기준으로 좌우 대칭

- 평균을 기준으로 표준 편차 좌우 1배 내에 약 68% 그리고 좌우 2배 안에 약 95% 정도의 데이터가 분포된 경우

- 평균이 0이고 표준편차가 1인 경우를 특별히 표준 정규 분포라고 함

- scipy.stats.norm 

mu = 0
std = 1

#평균이 0이고 표준편차가 1인 정규분포 객체 생성
rv = sp.stats.norm(mu, std)

#시각화 할 구간 설정
xx = np.linspace(-10, 10, 200)

plt.plot(xx, rv.pdf(xx))
plt.ylabel('확률')
plt.title('정규 분포 곡선')
  • -2 ~ 2 사이가 표준편차의 약 4배

# 붓꽃 데이터에서 꽃잎 길이에 대한 히스토그램 - 정규분포와 유사한 모양

from sklearn.datasets import load_iris
setosa_sepal_length = load_iris().data[:50, 2]
array([1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.6, 1.4,
       1.1, 1.2, 1.5, 1.3, 1.4, 1.7, 1.5, 1.7, 1.5, 1. , 1.7, 1.9, 1.6,
       1.6, 1.5, 1.4, 1.6, 1.6, 1.5, 1.5, 1.4, 1.5, 1.2, 1.3, 1.4, 1.3,
       1.5, 1.3, 1.3, 1.3, 1.6, 1.9, 1.4, 1.6, 1.4, 1.5, 1.4])
sns.histplot(setosa_sepal_length, kde=True)
plt.tight_layout()
  • kde: 분포 곡선을 그려준다.

 

- finance-datareader 패키지

  • 사용이 간편하고 다양한 시계열 데이터 수집해서 DaraFrame 생성
pip install finance-datareader

 

#한국 거래소 상장 종목 전체 가져오기

import FinanceDataReader as fdr

df_krx = fdr.StockListing('KRX')
df_krx.head()

 

#한국 코스피 지수 가져오기

import FinanceDataReader as fdr

#한국 코스피는 KS11, 시작날짜, 종료날짜(안쓰면 오늘날짜)
kospi = fdr.DataReader('KS11', '2011-01-01')
print(kospi.head())
                          Open     High      Low       Close     Volume   Change  UpDown  \
Date                                                                        
2011-01-03  2063.69  2070.09  2054.83  2070.08  354083359   0.009       1   
2011-01-04  2074.56  2085.14  2069.12  2085.14  415906517   0.007       1   
2011-01-05  2083.10  2087.14  2076.92  2082.55  386064920  -0.001       2   
2011-01-06  2094.35  2096.65  2066.10  2077.61  407829146  -0.002       2   
2011-01-07  2073.68  2086.20  2068.66  2086.20  335558949   0.004       1   
     
                     Comp         Amount            MarCap  
Date                                                
2011-01-03  19.08  5941470649203  1153204185044950  
2011-01-04  15.06  7799740341295  1163384106084700  
2011-01-05  -2.59  8609366845145  1162008605708700  
2011-01-06  -4.94  8804441828186  1159300878918060  
2011-01-07   8.59  7675864506306  1164242711087020  

 

# 현대 자동차 주가 지수 가져오기

#현대 자동차 주가 데이터 가져오기 (종복번호, 날짜)
df = fdr.DataReader('005380', '2011')
df.head(10)

 

# 나스닥 지수의 일자별 차이 확인

data = pd.DataFrame()

data['IXIC'] = fdr.DataReader('IXIC', '2011-01-01')['Close']

#결측치 제거
data = data.dropna()

#시각화
data.plot(legend=False)
  • 시간이 지나면서 증가
#일차별 차이값 구하기
daily_returns = data.pct_change().dropna()

#평균과 표준 편차 구하기 
mean = daily_returns.mean().values[0]
std = daily_returns.std().values[0]
print("평균 일간수익률: {:3.2f}%".format(mean * 100))
print("평균 일간변동성: {:3.2f}%".format(std * 100))
평균 일간수익률: 0.06%
평균 일간변동성: 1.29%
sns.histplot(daily_returns, kde=False)
ymin, ymax = plt.ylim()

plt.vlines(x=mean, ymin=0, ymax=ymax, ls="--") #가운데에 수직선
plt.ylim(0, ymax)
plt.title("나스닥 지수의 일간수익률 분포")
plt.xlabel("일간수익률")
kde = False
kde = True

 

 

 

- 로그 정규 분포

  • 데이터에 로그를 한 값 또는 변화율이 정규 분포가 되는 분포
  • 주가의 수익률이 정규 분포라면 주가 자체는 로그 정규 분포(log-normal distribution)
  • 로그 정규 분포를 가지는 데이터는 기본적으로 항상 양수라서 로그 변환을 수행한 다음 사용하는 것이 일반적
    • 머신러닝이나 딥러닝에 사용하는 경우 로그 변환 후 사용하기도 함
np.random.seed(0)
mu = 1
rv = sp.stats.norm(loc=mu)
x1 = rv.rvs(1000)

#정규 분포 데이터를 이용한 로그 정규 분포 데이터 생성
#시작하는 부분에 데이터가 치우침
#타겟 데이터가 한쪽으로 몰려있는 경우 로그 변환 고려
s = 0.5
x2 = np.exp(s * x1)

fig, ax = plt.subplots(1, 2)
sns.histplot(x1, kde=False, ax=ax[0])
ax[0].set_title("정규분포")
sns.histplot(x2, kde=False, ax=ax[1])
ax[1].set_title("로그정규분포")
plt.tight_layout()
plt.show()

 

- Q-Q (Quntile-Quantile)  Plot

  • 정규분포는 연속확률분포 중 가장 널리 사용되는 확률분포 => 정규분포인지 확인하는 것이 중요
  • 분석할 표본 데이터의 분포와 정규 분포의 분포 형태를 비교해서, 표본 데이터가 정규 분포를 따르는지 검사하는 간단한 시각적 도구
  • 정규 분포를 따르는 데이터를 Q-Q plot으로 그리면 대각선 방향의 직선 모양으로 만들어짐
  • scipy.stats.probplot(x, plot=plt)
#평균이 0이고 표준편차가 1인 정규 분포 객체로 데이터 샘플링
rv = sp.stats.norm(0, 1)
x = rv.rvs(20)

#랜덤하게 데이터 추출
# x = np.random.rand(100)

sp.stats.probplot(x, plot=plt)
plt.show()
정규분포
랜덤추출

 

- 중심 극한 정리

  • 모집단이 정규 분포가 아니더라도 표본 크기가 충분하고 데이터가 정규성을 크게 이탈하지 않는다면 여러 표본에서 추출한 평균은 종 모양의 정규 곡선을 따른다.
  • N 개의 임의의 분포로부터 얻은 표본의 평균은  N 이 증가할수록 기댓값이  μ , 분산이  σ2/N 인 정규 분포로 수렴
#여러개의 표본에서 추출한 데이터를 합치면 정규 분포와 유사해짐
xx = np.linspace(-2, 2, 100)

for i, N in enumerate([1, 2, 10]):
    X = np.random.rand(5000, N)

    Xbar = (X.mean(axis=1) - 0.5) * np.sqrt(12 * N)
    sp.stats.probplot(Xbar, plot=plt)
  • enumerate 함수: iterable 객체를 받아서 순회하고 (인덱스, 데이터)로 리턴 - 몇번째 데이터인지 알 수 있음
enumerate([1, 2, 10])
enumerate([1, 2, 5, 10, 20])
  • 점점 가까워진다.

중심극한정리

 

 

4-7) 스튜던트 T분포

- 현실의 데이터는 정규 분포와 거의 유사하지만, 양 끝단의 데이터가 정규 분포에 비해 극단적 현상이 더 자주 발생

- 분포의 모양을 볼 때 양 끝이 정규 분포보다 두껍기 때문에 fat tail 현상이라 한다.

  • 금융 시장에서는 black swan이라 한다.
#S&P 500, 나스닥(Nasdaq), 다우존스(Dow-Jones), 니케이255(Nikkei255) 
symbols=[
    'S&P500',
    'IXIC',
    'DJI',
    'N225', 
    'KS11'
]

#주가 가져오기
data = pd.DataFrame()
for sym in symbols:
    data[sym] = fdr.DataReader(sym, '2011-01-01')["Close"]
data = data.dropna()
data
# 단위 축소시켜주기
(data / data.iloc[0] * 100).plot()

plt.ylabel("날짜")
plt.ylabel("주가 수익률")
plt.show()

 

#t 분포 - 수익률

#로그화
log_returns = np.log(data / data.shift(1)) #shift: 오른쪽으로 한칸 밀기
log_returns.hist(bins=50)
  • 정규분포와 다른 점은 Q-Q 플롯을 통해 확인

#Q-Q 플롯

#두ㅕ
for i, sym in enumerate(symbols):
    ax = plt.subplot(2, 3, i+1)
    sp.stats.probplot(log_returns[sym].dropna(), plot=ax)
plt.tight_layout()

 

 

- 확률 밀도 함수나 누적 분포 함수를 사용하기 위해서는 t 클래스 이용

  • 매개변수는 df, loc(기대값), scale(표준편차)
    • df는 자유도, ddof는 제약조건
    • 자유도는 통계적 추정을 할 때 표본 자료 중 모집단에 대한 정보를 주는 독립적인 자료의 수(추정해야 할 미지수)의 개수를 내가 가진 정보의 수에서 뺀 값
    • 표준편차나 분산을 구할 때 데이터의 개수가 아닌 n-1이 되는 이유는 표준 편차나 분산을 구하기 위해서는 평균이 전제가 되어야 하기 때문. 하지만 실제 모수의 평균은 미지수이므로 평균을 가정해야 한다. 그럼 이 가정은 제약조건이 된다.
    • 실제로는 데이터의 개수가 많아지면 자유도는 의미가 없음

자유도가 커질수록 정규 분포에 가깝다

 

- t 통계량

  • 정규 분포의 표본을 표준편차로 나눠 정규화한 z 통계량은 항상 정규 분포
  • z 통계량을 구하려면 표준편차를 알아야 하지만, 현실적으로 모수의 표준편차를 정확히 알 수 없기 때문에 표본에서 측정한 표본 표준편차를 이용해서 정규화 수행
  • 정규 분포로부터 얻은 N개의 표본에서 계산한 표본 평균을 표본 평균 편차로 정규화한 값이 t 통계량
  • 분포를 알 수 없는 샘플 데이터만 주어진 경우에 확률이나 값을 추정할 때 사용
    • 기온을 측정했는데 특정 기온이 확률적으로 몇%에 해당하는지 알고자 하는 경우
  • scipy.stats.t.분포함수(x=score, loc=mean, scale=std, df=df)
scailing - 값을 줄이는 것
표준화(standardiztion) - 열의 값을 어떤 특정 범위(0~1, -1~1)로 변경하는 것
정규화(normalization) - 행의 값을 어떤 특정 범위로 변경하는 것 -1로 맞추는 직업

원핫인코딩이 정규화
data = np.array([23, 30,18, 33, 28, 33, 34, 38, 29, 31])

#자유도
df = len(data)-1

#평균
d_mean = data.mean()

#표준 편차
d_std = data.std(ddof=1) #모집단의 표준편차가 아니라 샘플 데이터의 표준편차이므로 평균을 가정해야 함 - 1 설정
#섬씨 25도일 때 상위 몇 %에 속하는 온도일까?
score = 25

p = sp.stats.t.cdf(x=score,loc=d_mean,scale=d_std,df=df)
print(f"{score}는 상위 {(1-p)*100:.2f}%로 예측한다.")
p2 = sp.stats.t.sf(x=score,loc=d_mean,scale=d_std,df=df)
print(f"{score}는 상위 {p2*100:.2f}%로 예측한다.")
25는 상위 78.31%로 예측한다.
25는 상위 78.31%로 예측한다.

 

 

4-8) 카이 제곱 분포

- 정규 분포를 따르는 확률 변수 X의 N개 개의 표본 x1,⋯,xN 의 합(또는 평균) 표본 분산으로 정규화하면 스튜던트 t분포를 따른다. N개의 표본을 제곱해서 더하면 양수만을 갖는 분포 생성 - 카이제곱분포

- scipy.stats.chi2 

- 제곱 합을 구하는 표본의 수가 2보다 커지면 0 근처의 값이 가장 많이 발생할 것 같지만, 실제로는 0보다 큰 어떤 수가 흔하게 발생

 

4-9) F 분포

- 카이 제곱 분포를 따르는 독립적인 두개의 확률변수의 확률 변수 표본을 각각 x1, x2라고 할 때 이를 각각 자유도  N1, N2로 나눈 뒤 비율을 구하면 F분포

- t 분포의 표본 값을 제곱한 값이 F 분포

  • N1과 N2의 값이 같을 경우:  1 근처 값이 가장 많이 발생할 것 같지만 실제는 1이 아닌 다른 수가 조금 더 흔하게 발생
  • N1과 N2의 값이 커지면:  1 근처의 값이 많이 발생

- scipy.stats.f

 

 

------------------------------------------------------------------------------------------------여기까지 정규분포 통계량 분포

  • 스튜던트 t 분포: 추정된 가중치에 대한 확률분포
  • 카이제곱 분포: 오차 제곱 합에 대한 확률분포
  • F분포: 비교 대상이 되는 선형 모형의 오차 제곱 합에 대한 비율의 확률 분포

 

 

4-10) 푸아송 분포

- 단위 시간 안에 어떤 사건이 몇번 일어날 것인지를 표현하는 이산확률분포

- 푸아송이 민사사건과 형사사건 재판에서 확률에 관한 연구 및 일반적인 확률계산 법칙에 대한 서문에서 최초로 사용

- scipy.stats.poisson.rvs()

  • μ: 모양 매개 변수로 사용
  • 방정식의 λ(람다): 단위 시간당 발생하는 사건의 개수 설정
  • loc: 분포 이동
  • zmrl: 분포에서 임의의 변량 수 결정
  • random_state 인수 포함: 재현성 유지

- 예시

  • 어떤 식당에 주말 오후 동안 시간당 평균 20명의 손님이 방문한다고 할 때, 다음주 주말 오후에 30분 동안 5명의 손님이 방문할 확률은?
    • 시간당 손님은 20명이므로 30분당 기대값은 10명

 

# 단위 시간당 사건이 10개가 발생하는 경우

data_poisson = sp.stats.poisson.rvs(mu=10, size=1000)

sns.histplot(data_poisson, bins=30, kde=True)

 

# 2022년 기준으로 한 시간 평균 신생아 수는 682 명인 경우에 한 시간에 650명 이하의 신생아를 낳을 확률은?

# p = sp.stats.poisson.cdf(mu=682, k=650)

x=range(500,800)
y = sp.stats.poisson.pmf(mu=682,k=x)
plt.plot(x,y,alpha=0.7,label='pmf')

x2 = range(500,630)
y2 = sp.stats.poisson.pmf(mu=682,k=x2)
plt.fill_between(x2,y2,color='r',label='x<630') #630명 미만일 때 확률(면적)

#630명 미만
x3 = range(630,651)
y3 = sp.stats.poisson.pmf(mu=682,k=x3)
plt.fill_between(x3,y3,color='c',label='630<x<=651') #630명 이상 650명 이하일 때 확률(면적)
plt.xticks([500,630,650,682,800])
plt.title("")
plt.show()
# 0.11328673711427531

 

#시간당 500M 정도의 트래픽을 소모하는데 99% 정도를 처리하고자 할 때 필요한 트래픽은?

#help(sp.stats.poisson.ppf)
p = sp.stats.poisson.ppf(mu=500, q=0.99)
print(p)
630.0

 

 

4-11) 지수분포

- 푸아송 분포: 사건이 독립적일 때 일정 시간 동안 발생하는 사건의 횟수

- 지수 분포: 다음 사건이 일어날 때까지 대기 시간

- scipy.stats.expon

  • scale: 시간 설정
  • loc: 사건의 발생 횟수
  • 사건의 대기 시간이 일정한 형태로 줄어들거나 늘어나는 경우 weibull distribution 이용

# 스마트폰의 배터리 수명은 평균 24시간인 경우, 20시간 이내에 소진할 확률은?

#  sp.stats.expon.pdf(scale=24,x=x)

x = range(0,100)
y = sp.stats.expon.pdf(scale=24,x=x)
for i in x:
  print(f'{i:02d}시간 {y[i]:.2f}', end=' ')
  if i%10==9:
    print()
    
# 시각화
plt.plot(x,y)
plt.xlabel("time")
plt.ylabel("probability")
# 0.5654017914929218

 

 

 

 

 

 

 

 

 

 

 

 

 

'Python' 카테고리의 다른 글

[Python] 벡터 연산에서 기억할 부분  (0) 2024.02.27
[Python] 샘플링 _ 표본 추출  (1) 2024.02.26
[Python] 기술통계  (0) 2024.02.22
[Python] 이미지 데이터 다루기  (0) 2024.02.21
[Python] 확률  (0) 2024.02.21