안녕하세요. 지난 포스팅의 넘파이 알고 쓰자 - 브로드캐스팅에서는 넘파이 객체의 핵심 개념인 브로드캐스팅에 대해서 알아보았습니다. 오늘 포스팅에서는 넘파이 알고 쓰자 - 넘파이 객체 선언의 마지막에 간단하게 설명하고 넘어갔던 랜덤 모듈에 대해서 더 자세하게 설명하도록 하겠습니다.
넘파이의 랜덤 모듈에서는 크게 4가지의 기능으로 나눌 수 있습니다. 각각 랜덤 데이터 추출(Simple Random Data), 순서 바꾸기(Permutation), 확률 분포 생성(Distribution Generator), 랜덤 생성자(Random Generator)입니다.
1. 랜덤 데이터 추출(Simple Random Data)
넘파이에서는 특정 확률 분포를 따르는 넘파이 객체를 생성하는 함수를 보유하고 있습니다. 넘파이 공식 문서에서는 굉장히 많은 종류의 함수가 있지만 여기에서는 자주 쓰이는 대표 함수인 np.random.rand, np.random.randn, np.random.randint, np.random.choice 정도만 소개하도록 하겠습니다. 또한 저는 넘파이 알고 쓰자 - 넘파이 객체 선언에서 np.random.rand, np.random.randn, np.random.randint에 대해서 자세하게 설명했기 때문에 np.random.choice만 설명하도록 하겠습니다. 시간이 짬짬히 날 때마다 공식 문서에서 설명하는 함수들을 추가하여 설명하도록 하겠습니다.
1). np.random.choice(a, size=None, replace=True, p=None)
"a"는 1차원 벡터나 정수가 올 수 있습니다. 1차원 벡터인 경우 1차원 벡터에서 특정 개수의 데이터를 뽑습니다. 정수인 경우 np.arange(a)를 자동으로 실행하여 [0, 1, ..., a - 1]의 1차원 벡터를 생성하여 특정 개수의 데이터를 뽑습니다.
"size"는 정수, 정수형 튜플이 올 수 있습니다. 정수인 경우 해당 개수만큼 a에서 원소를 추출합니다. 만약, 튜플로 입력하게 되면 해당 튜플과 동일한 shape을 가진 행렬을 생성하도록 만듭니다.
"replace"는 boolean 자료형으로 중복 추출의 여부를 의미합니다. 디폴트 값은 True입니다. 동일한 값이 여러번 나올 수 있음을 의미합니다. 만약 False로 정의하면 동일한 값이 나올 수 없고 각 값이 1개씩만 나오게 됩니다.
"p"는 a와 동일한 shape의 1차원 벡터가 들어갈 수 있습니다. 이는 a의 각 요소가 뽑힐 확률을 수동으로 정해주는 파라미터로 디폴트 값으로는 모든 요소에 대한 확률이 동일합니다. p는 확률로 정의되기 때문에 각 요소에 대한 확률의 합은 반드시 1이 되어야합니다.
np.random.choice(5, 10) # array([0, 4, 1, 1, 4, 1, 1, 4, 2, 1])
0~4 사이의 값을 10개씩 중복 추출합니다. 만약, replace를 False로 바꾸면 아래와 같은 오류가 발생합니다.
np.random.choice(5, 10, replace=False)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-25-0308e8360580> in <module>
----> 1 np.random.choice(5, 10, replace=False)
mtrand.pyx in numpy.random.mtrand.RandomState.choice()
ValueError: Cannot take a larger sample than population when 'replace=False'
즉, 중복 추출을 불허하기 때문에 10개의 값을 추출하기 위해서는 최소 10개의 값이 필요하기 때문에 발생하는 오류입니다. 따라서 이 오류를 방지하기 위해서는 아래와 같이 추출하는 값의 개수를 최대 크기보다 낮추거나, 최대 크기를 추출하는 값의 개수 이상으로 높히면 됩니다.
np.random.choice(5, 3, replace=False) # array([2, 4, 0])
np.random.choice(20, 10, replace=False) # array([14, 11, 17, 0, 8, 1, 5, 12, 13, 18])
다음으로 각 요소가 추출될 확률을 수동으로 정해보도록 하겠습니다.
np.random.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0]) # array([3, 0, 2])
확률을 보면 0.1 + 0.0 + 0.3 + 0.6 + 0.0 = 1.0으로 확률의 합이 1입니다. 만약 확률의 합이 1이 아니라면 오류가 발생하게 됩니다.
np.random.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0.1])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-36-601996a15275> in <module>
----> 1 np.random.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0.1]) # array([3, 0, 2])
mtrand.pyx in numpy.random.mtrand.RandomState.choice()
ValueError: probabilities do not sum to 1
하지만, 실제로 각 요소가 확률에 맞게 뽑히는 지 잘 모르겠습니다. 실제로 matplotlib 함수를 이용해서 각 값을 100000번 추출한 뒤 추출된 값의 비율을 계산해보도록 하겠습니다.
a = np.random.choice(5, 100000, p=[0.2, 0.1, 0.2, 0.4, 0.1])
plt.bar(np.arange(0, 5), np.unique(a, return_counts=True)[1]/100000)
2. 순서 바꾸기(Permutation)
넘파이에서는 순서를 바꾸는 함수를 np.random.permutation, np.random.shuffle을 가지고 있습니다.
1). np.random.permutation(x)
"x"는 정수나 임의의 shape을 가지는 넘파이 객체가 들어갈 수 있습니다. 정수인 경우 np.random.choice와 동일하게 자동으로 np.arange(x)를 실행하여 넘파이 배열을 생성한 뒤 배열을 섞습니다.
np.random.permutation(10) # array([1, 3, 0, 6, 5, 9, 8, 2, 4, 7])
np.random.permutation([1, 4, 9, 12, 15]) # array([12, 9, 1, 15, 4])
2). np.random.shuffle(x)
np.random.permutation의 x와 100% 동일한 기능입니다. 이 역시 배열을 섞는 함수입니다.
arr = np.arange(10)
np.random.shuffle(arr)
arr # array([4, 9, 0, 8, 5, 3, 6, 1, 2, 7])
그렇다면 permutation과 shuffle은 어떤 차이가 있을까요? 그것은 "in-place"의 유무입니다. 간단하게 설명하면 permutation 입력으로 받은 객체 x를 복사하여 새로운 객체를 생성한 뒤 섞인 배열을 반환합니다. 이에 비해 shuffle은 객체 x 자체를 섞기 때문에 입력 배열의 순서가 변하게 됩니다.
arr1 = np.arange(10)
arr2 = np.arange(10)
arr3 = np.random.permutation(arr1)
arr4 = np.random.shuffle(arr2)
print(arr1) # [0 1 2 3 4 5 6 7 8 9]
print(arr2) # [9 5 4 1 6 3 7 2 0 8]
print(arr3) # [4 3 0 7 9 2 1 8 5 6]
print(arr4) # None
arr1, arr2를 동일하게 생성한 뒤 각각 permutation 함수와 shuffle 함수를 이용해서 섞어보았습니다. 그 결과는 arr1은 그대로 유지됩니다. 하지만 shuffle에 입력으로 넣은 arr2는 그 값이 변하였습니다. 이와 같이 입력 배열 그 자체에 변화를 주는 함수를 "in-place" 함수라고 부릅니다.
3. 확률 분포 생성(Distribution Generator)
넘파이에서는 수많은 확률 분포가 구현되어 있습니다. 유명한 binomial, exponential, gamma, geometric, normal, ... 이 중에서 이후에 필요한 확률 분포를 이용하면 됩니다!!
4. 랜덤 생성자(Random Generator)
넘파이에서는 랜덤이더라도 고정된 값을 출력할 수 있도록 만들 수 있는 방법이 있습니다. np.random.seed 함수를 이용해서 어디에서든지 한번만 실행되면 동일한 셀(주피터 노트북 기준)에서 몇 번을 실행해도 값이 변하지 않습니다.
'Programming > Python' 카테고리의 다른 글
넘파이 알고 쓰자 - stack, hstack, vstack, dstack, column_stack (7) | 2020.08.26 |
---|---|
넘파이 알고 쓰자 - concatenate (1) | 2020.08.24 |
넘파이 알고 쓰자 - 브로드캐스팅 (0) | 2020.08.19 |
넘파이 알고 쓰자 - 넘파이 인덱싱과 슬라이싱 (0) | 2020.08.18 |
넘파이 알고 쓰자 - 넘파이 기본 연산 (3) | 2020.08.16 |