[Python NumPy] 무작위 표본 추출, 난수 만들기 (random sampling, random number generation)
Python 분석과 프로그래밍/Python 데이터 전처리 2017. 1. 21. 22:39이번 포스팅에서는 시간과 비용 문제로 전수 조사를 못하므로 표본 조사를 해야 할 때, 기계학습 할 때 데이터셋을 훈련용/검증용/테스트용으로 샘플링 할 때, 또는 다양한 확률 분포로 부터 데이터를 무작위로 생성해서 시뮬레이션(simulation) 할 때 사용할 수 있는 무작위 난수 만들기(generating random numbers, random sampling)에 대해서 알아보겠습니다.
Python NumPy는 매우 빠르고(! 아주 빠름!!) 효율적으로 무작위 샘플을 만들 수 있는 numpy.random 모듈을 제공합니다.
NumPy 를 불러오고, 정규분포(np.random.normal)로 부터 개수가 5개(size=5)인 무작위 샘플을 만들어보겠습니다. 무작위 샘플 추출을 할 때마다 값이 달라짐을 알 수 있습니다.
In [1]: import numpy as np In [2]: np.random.normal(size=5) Out[2]: array([-0.02030555, 0.38279633, -1.02369692, 1.48083476, -0.44058273]) In [3]: np.random.normal(size=5) # array with different random numbers Out[3]: array([ 1.11942454, -1.03486318, 1.69015608, -0.43601241, -1.52195043])
|
먼저, seed와 size 모수 설정하는 것부터 소개합니다.
seed : 난수 생성 초기값 부여 |
난수 생성 할 때 마다 값이 달라지는 것이 아니라, 누가, 언제 하든지 간에 똑같은 난수 생성을 원한다면 (즉, 재현가능성, reproducibility) seed 번호를 지정해주면 됩니다.
# seed : setting the seed number for random number generation for reproducibility In [4]: np.random.seed(seed=100) In [5]: np.random.normal(size=5) Out[5]: array([-1.74976547, 0.3426804 , 1.1530358 , -0.25243604, 0.98132079])
In [6]: np.random.seed(seed=100) In [7]: np.random.normal(size=5) # 위의 결과랑 똑같음 Out[7]: array([-1.74976547, 0.3426804 , 1.1530358 , -0.25243604, 0.98132079])
|
size : 샘플 생성(추출) 개수 및 array shape 설정 |
다차원의 array 형태로 무작위 샘플을 생성할 수 있다는 것도 NumPy random 모듈의 장점입니다.
# size : int or tuple of ints for setting the shape of nandom number array In [8]: np.random.normal(size=2)Out[8]: array([ 0.51421884, 0.22117967]) In [9]: np.random.normal(size=(2, 3)) Out[9]: array([[-1.07004333, -0.18949583, 0.25500144], [-0.45802699, 0.43516349, -0.58359505]]) In [10]: np.random.normal(size=(2, 3, 4)) Out[10]: array([[[ 0.81684707, 0.67272081, -0.10441114, -0.53128038], [ 1.02973269, -0.43813562, -1.11831825, 1.61898166], [ 1.54160517, -0.25187914, -0.84243574, 0.18451869]], [[ 0.9370822 , 0.73100034, 1.36155613, -0.32623806], [ 0.05567601, 0.22239961, -1.443217 , -0.75635231], [ 0.81645401, 0.75044476, -0.45594693, 1.18962227]]])
|
다양한 확률 분포로부터 난수를 생성해보겠습니다. 먼저, 정수를 뽑는 이산형 확률 분포(discrete probability distribution)인 (1-1) 이항분포, (1-2) 초기하분포, (1-3) 포아송분포로 부터 무작위 추출하는 방법을 알아보겠습니다.
각 확률분포에 대한 설명까지 곁들이면 포스팅이 너무 길어지므로 참고할 수 있는 포스팅 링크를 걸어놓는 것으로 갈음합니다.
- 이항분포 (Binomial Distribution) : http://rfriend.tistory.com/99
- 초기하분포 (Hypergeometric distribution) : http://rfriend.tistory.com/100
- 포아송 분포 (Poisson Distribution) : http://rfriend.tistory.com/101
(1-1) 이항분포로 부터 무작위 표본 추출 (Random sampling from Binomial Distribution) : np.random.binomial(n, p, size) |
앞(head) 또는 뒤(tail) (n=1) 가 나올 확률이 각 50%(p=0.5)인 동전 던지기를 20번(size=20) 해보았습니다.
# (1) 이산형 확률 분포 (Discrete Probability Distribution)
In [11]: np.random.binomial(n=1, p=0.5, size=20) Out[11]: array([0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1]) In [12]: sum(np.random.binomial(n=1, p=0.5, size=100) == 1)/100 Out[12]: 0.46999999999999997
|
(1-2) 초기하분포에서 무작위 표보 추출 (Random sampling from Hypergeometric distribution) : np.random.hypergeometric(ngood, nbad, nsample, size) |
good 이 5개, bad 가 20개인 모집단에서 5개의 샘플을 무작위로 비복원추출(random sampling without replacement) 하는 것을 100번 시뮬레이션 한 후에, 도수분포표를 구해서, 막대그래프로 나타내보겠습니다.
# (1-2) 초기하분포 (Hypergeometric distribution)
In [13]: np.random.seed(seed=100) In [14]: rand_hyp = np.random.hypergeometric(ngood=5, nbad=20, nsample=5, size=100) In [15]: rand_hyp Out[15]: array([1, 1, 1, 1, 1, 1, 0, 3, 0, 1, 2, 0, 1, 1, 2, 0, 1, 1, 0, 0, 0, 3, 2,
In [16]: unique, counts = np.unique(rand_hyp, return_counts=True) In [17]: np.asarray((unique, counts)).T Out[17]: array([[ 0, 27],
# bar plot In [18]: import matplotlib.pyplot as plt In [19]: plt.bar(unique, counts, width=0.5, color="blue", align='center') Out[19]: <Container object of 4 artists>
|
(1-3) 포아송분포로 부터 무작위 표본 추출 : np.random.poisson(lam, size) (random sampling from Poisson distribution) |
일정한 단위 시간, 혹은 공간에서 무작위로 발생하는 사건의 평균 회수인 λ(lambda)가 20인 포아송 분포로 부터 100개의 난수를 만들어보겠습니다. 그 후에 도수를 계산하고, 막대그래프로 분포를 그려보겠습니다.
# (1-3) 포아송 분포 (Poisson Distribution)
In [20]: np.random.seed(seed=100) In [21]: rand_pois = np.random.poisson(lam=20, size=100) In [22]: rand_pois Out[22]: array([21, 19, 22, 14, 26, 15, 25, 25, 19, 25, 15, 24, 21, 13, 26, 23, 21, In [23]: unique, counts = np.unique(rand_pois, return_counts=True) In [24]: np.asarray((unique, counts)).T Out[24]: array([[ 9, 1], In [25]: plt.bar(unique, counts, width=0.5, color="red", align='center') Out[25]: <Container object of 21 artists>
|
다음으로 연속형 확률분포(continuous probability distribution)인 (2-1) 정규분포, (2-2) t-분포, (2-3) 균등분포, (2-4) F 분포, (2-5) 카이제곱분포로부터 난수를 생성하는 방법을 소개합니다.
각 분포별 이론 설명은 아래의 포스팅 링크를 참조하세요.
- 정규분포 (Normal Distribution) : http://rfriend.tistory.com/102
- t-분포 (Student's t-distribution) : http://rfriend.tistory.com/110
- 균등분포 (Uniform Distribution) : http://rfriend.tistory.com/106
- F-분포(F-distribution) : http://rfriend.tistory.com/111
- 카이제곱분포 (Chisq-distribution) : http://rfriend.tistory.com/112
(2-1) 정규분포로부터 무작위 표본 추출 : np.random.normal(loc, scale, size) (random sampling from Normal Distribution) |
평균이 '0', 표준편차가 '3'인 정규분포로 부터 난수 100개를 생성해보고, 히스토그램을 그려서 분포를 bin 구간별 빈도(frequency)와 표준화한 비율(normalized percentage)로 살펴보겠습니다.
# (2) 연속형 확률분포 (continuous probability distribution)
In [26]: np.random.seed(100) In [27]: mu, sigma = 0.0, 3.0 In [28]: rand_norm = np.random.normal(mu, sigma, size=100) In [29]: rand_norm Out[29]: array([-5.24929642, 1.02804121, 3.45910741, -0.75730811, 2.94396236, In [30]: import matplotlib.pyplot as plt
In [31]: count, bins, ignored = plt.hist(rand_norm, normed=False)
# histogram with normalized percentage
|
(2-2) t-분포로 부터 무작위 표본 추출 : np.random.standard_t(df, size) (Random sampling from t-distribution) |
자유도(degrees of freedom)가 '3'인 t-분포로부터 100개의 난수를 생성하고, 히스토그램을 그려보겠습니다.
# (2-2) t-분포 (Student's t-distribution)로부터 난수 생성
In [33]: np.random.seed(100) In [34]: rand_t = np.random.standard_t(df=3, size=100) In [35]: rand_t Out[35]: array([-1.70633623, 0.61010003, 0.45753218, -0.85709656, -0.42990712,
# histogram In [36]: import matplotlib.pyplot as plt In [37]: count, bins, ignored = plt.hist(rand_t, bins=20, normed=True)
|
(2-3) 균등분포로 부터 무작위 표본 추출 : np.random.uniform(low, high, size) (random sampling from Uniform distribution) |
최소값이 '0', 최대값이 '10'인 구간에서의 균등분포에서 100개의 난수를 만들어 보고, 히스토그램을 그려서 분포를 확인해보겠습니다.
# (2-3) 균등분포 (Uniform Distribution)로 부터 난수 생성 In [38]: np.random.seed(100) In [39]: rand_unif = np.random.uniform(low=0.0, high=10.0, size=100) In [40]: rand_unif Out[40]: array([ 5.43404942, 2.78369385, 4.24517591, 8.44776132, 0.04718856,
# histogram
In [41]: import matplotlib.pyplot as plt In [42]: count, bins, ignored = plt.hist(rand_unif, bins=10, normed=True)
|
이산형 균등분포에서 정수형 무작위 표본 추출 : np.random.randint(low, high, size) (Random INTEGERS from discrete uniform distribution) |
이산형 균등분포(discrete uniform distribution)으로 부터 정수형(integers)의 난수를 만들고 싶으면 np.random.randint 를 사용하면 됩니다. 모수 설정은 np.random.uniform()과 같은데요, high에 설정하는 값은 미포함(not include, but exclude)이므로 만약 0부터 10까지 (즉, 10도 포함하는) 정수형 난수를 만들고 싶으면 high=10+1 처럼 '+1'을 해주면 됩니다.
# np.random.randint : Discrete uniform distribution, yielding integers In [43]: np.random.seed(100) In [44]: rand_int = np.random.randint(low=0, high=10+1, size=100) In [45]: rand_int Out[45]: array([ 8, 8, 3, 7, 7, 0, 10, 4, 2, 5, 2, 2, 2, 1, 0, 8, 4,
# histogram In [46]: import matplotlib.pyplot as plt In [47]: count, bins, ignored = plt.hist(rand_int, bins=10, normed=True)
|
(2-4) F-분포로 부터 무작위 표본 추출 : np.random.f(dfnum, dfden, size) (Random sampling from F-distribution) |
자유도1이 '5', 자유도2가 '10'인 F-분포로 부터 100개의 난수를 만들고, 히스토그램을 그려서 분포를 확인해보겠습니다.
# (2-4) F-분포 (F-distribution)으로부터 난수 생성
In [48]: np.random.seed(100) In [49]: rand_f = np.random.f(dfnum=5, dfden=10, size=100) In [50]: rand_f Out[50]: array([ 0.17509245, 1.34830314, 0.7250835 , 0.55013536, 1.49183341,
# histogram In [51]: import matplotlib.pyplot as plt In [52]: count, bins, ignored = plt.hist(rand_f, bins=20, normed=True)
|
(2-5) 카이제곱분포로 부터 무작위 표본 추출 : np.random.chisquare(df, size) (Random sampling from Chisq-distribution) |
자유도(degrees of freedom)가 '2'인 카이제곱분포로부터 100개의 난수를 생성하고, 히스토그램을 그려서 분포를 확인해보겠습니다.
# (2-5) 카이제곱분포 (Chisq-distribution)로 부터 난수 생성 In [53]: np.random.seed(100) In [54]: rand_chisq = np.random.chisquare(df=2, size=100) In [55]: rand_chisq Out[55]: array([ 1.56791674e+00, 6.52483769e-01, 1.10509323e+00,
# histogram In [56]: import matplotlib.pyplot as plt In [57]: count, bins, ignored = plt.hist(rand_chisq, bins=20, normed=True)
|
많은 도움 되었기를 바랍니다.
pandas DataFrame 에 대한 무작위 표본 추출 방법은 https://rfriend.tistory.com/602 를 참고하세요.