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 |