이번 포스팅에서는 Python numpy 모듈의 random.choice() 메소드를 사용하여 임의(무작위, 확률) 추출 (random sampling)하는 방법을 소개하겠습니다. 


numpy.random.choice() 메소드의 기본 Syntax는 아래와 같습니다. 각 parameter별로 예를 들어서 설명을 해보겠습니다. 


numpy.random.choice(a, size=None, replace=True, p=None)


이때 표본을 추출할 모집단에 해당하는 a 는 1-D array-like 또는 np.arange(n) 의 정수가 됩니다. 





  (1) 1부터 5까지의 정수 모집단에서 3개의 균등확률표본을 복원추출 하기

  (Generate a uniform random sample from np.arange(5) of size 3)


표본이 뽑힐 확률 p를 명시적으로 지정해주지 않으면 모든 원소가 뽑힐 확률이 동일한 (즉, p=1/N) 균등확률분포를 가정하고 표본이 추출됩니다. 

그리고, 복원추출(replacement)이 기본 설정값이므로 똑같은 값이 2번 이상 표본으로 뽑힐 수도 있습니다. 



import numpy as np


np.random.choice(5, 3) # with replacement

Out[3]: array([4, 0, 0])

 


참고로, 위의 np.random.choice(5, 3) 코드는 np.random.randint(0,5,3) 과 동일합니다. 



균등확률분포로 부터 임의 추출이므로 매번 표본으로 뽑히는 값이 바뀌게 됩니다. 위와 코드는 같지만 추출된 표본은 다르지요?



np.random.choice(5, 3) # sampled with different values

Out[4]: array([2, 0, 0])

 



복원추출을 하게 되면 1~5 의 정수를 가지는 모집단에서 5개를 초과하는 표본을 뽑는데 문제가 없습니다. 



np.random.choice(5, 10) # with replacement

Out[5]: array([2, 2, 4, 2, 4, 0, 0, 4, 3, 2])

 




 (2) 1~5의 정수 모집단에서 비균등 확률 p 로 3개 원소 임의 표본 복원추출 하기

    (Generate a non-uniform random sample from np.arange(5) of size 3:)


만약 모집단 내 각 원소별로 표본으로 뽑힐 확률 p를 알고 있다면, 혹은 명시적으로 지정을 해주고 싶다면 모수 p에 표본이 추출될 확률을 입력해주면 됩니다. 



p = [0.1, 0, 0.3, 0.6, 0]

np.random.choice(5, 3, p=p)

Out[7]: array([2, 3, 3])

 



만약 표본을 추출할 모집단(a)의 원소 개수(n)과 각 원소별 표본이 뽑힐 확률(p)의 원소 개수가 서로 다를 경우 ValueError 가 발생합니다. (아래 예에서는 확률 p에는 3개가, 모집단 a는 5개 원소로서 서로 다름)



# ValueError: 'a' and 'p' must have same size

p = [0.1, 0, 0.3]           # size 3

np.random.choice(5, 3, p=p) # size 5


ValueError: 'a' and 'p' must have same size

 



만약 모집단 원소별 표본으로 뽑힐 확률 p의 전체 합이 1 이 아니거나 0~1사이 값이 아닌 경우도 ValueError가 발생합니다. 왜냐하면, 확률의 정의 상 (1) 각 사건별 확률의 전체 합은 1이고, (2) 각 사건별 확률은 0~1 사이의 실수를 가져야 하기 때문입니다. 



# ValueError: probabilities do not sum to 1

p = [0.4, 0, 0.3, 0.6, 0]   # sum to 1.3 (not 1)

np.random.choice(5, 3, p=p)

ValueError: probabilities do not sum to 1



# ValueError: probabilities are not non-negative

p = [-0.4, 0, 0.3, 1.6, 0]   

np.random.choice(5, 3, p=p)


ValueError: probabilities are not non-negative





  (3) 1~5 정수 모집단에서 3개의 균등확률표본을 비복원추출(non-replacement) 하기

  (Generate a uniform random sample from np.arange(5) of size 3 

   without replacement)


옵션 중에서 replace=False 로 설정을 해주면 비복원추출(without replacement)을 합니다. 즉, 모집단에서 표본을 추출할 때 각 원소를 딱 한번만 추출하기 때문에 동일한 원소가 2번 이상 뽑히는 일은 없습니다. (default 옵션은 replace=True 임)



# Generate a uniform random sample from np.arange(5) of size 3 without replacement:

np.random.choice(5, 3, replace=False)

Out[11]: array([3, 2, 1])

 


참고로 위의 np.random.choice(5, 3, p=p) 코드는 np.random.permutation(np.arange(5))[:3] 과 동일합니다. 



비복원추출을 할 때는 한가지 조심해야 할 것이 있는데요, 모집단의 원소 개수보다 많은 수의 샘플을 비복원추출(replace=False)하려고 하면 ValueError가 발생합니다. (아래 예처럼, 5개의 원소를 가진 모집단에서 10개 표본을 비복원(즉, 중복 없이) 추출할 수는 없겠지요!)



# ValueError: Cannot take a larger sample than population when 'replace=False'

np.random.choice(5, 10, replace=False)


ValueError: Cannot take a larger sample than population when 'replace=False'

 



위의 (2)번에서 표본추출확률 p 를 설정하는 것과, 이번 (3)번의 비복원추출(replace=False)을 함께 설정해줌으로써 비균등확률(non-uniform random sample)로 비복원추출(without replacement) 샘플링도 물론 가능합니다. 



# Generate a non-uniform random sample from np.arange(5) of size 3 without replacement:

p = [0.1, 0, 0.3, 0.6, 0]

np.random.choice(5, 3, replace=False, p=p)

Out[13]: array([3, 0, 2])

 




  (4) 정수 대신에 임의의 배열처럼 생긴 객체의 원소를 확률추출하기

  (Any of the above can be repeated with an arbitrary array-like instead of just integers.)


np.random.choice(a, size=None, replace=True, p=None) 의 syntax에서 a 부분에 정수 배열 (np.arange(n)) 말고도 1-D array-like 객체를 대상으로 할 수도 있습니다. 아래 예에서는 과일 이름을 원소로 가지는 리스트로부터 비복원 비균등 확률 표본추출을 해보았습니다. 



# Any of the above can be repeated with an arbitrary array-like instead of just integers.

fruits = ['apple', 'banana', 'cherries', 'durian', 'grapes', 'lemon', 'mango']

p = [0.1, 0, 0.2, 0.5, 0.1, 0.05, 0.05]

np.random.choice(fruits, 3, p=p, replace=False)

Out[14]: array(['cherries', 'lemon', 'durian'], dtype='<U8')

 




  (5) 초기값 설정을 통한 재현가능성 확보

  (setting seed number for reproducibility)


np.random.seed() 로 초기값(seed value)을 설정해주면 매번 똑같은 확률표본을 추출할 수 있습니다. 만약 재현가능성(reproducibility)이 필요한 경우라면 초기값을 설정해주세요. 



# set seed number for reproducibility

np.random.seed(1004)

np.random.choice(5, 3)

Out[15]: array([2, 3, 3])


np.random.seed(1004)

np.random.choice(5, 3)

Out[16]: array([2, 3, 3])




참고로, 기계학습을 할 때 train set, test set 을 무작위로 분할하는 여러가지 방법은 https://rfriend.tistory.com/519 를 참고하시기 바랍니다. 


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


* Reference: https://docs.scipy.org/doc//numpy-1.10.4/reference/generated/numpy.random.choice.html



Posted by R Friend Rfriend

댓글을 달아 주세요