NumPy를 사용한 난수 발생

파이썬을 이용하여 난수를 발생시키거나 데이터를 무작위로 섞는 방법에 대해 알아본다.

이런 기능들은 주로 NumPy의 random 서브패키지에서 제공한다.


In [1]:
import numpy as np

시드 설정하기

컴퓨터 프로그램에서 무작위와 관련된 모든 알고리즘은 사실 무작위가 아니라 시작 숫자를 정해 주면 그 다음에는 정해진 알고리즘에 의해 마치 난수처럼 보이는 수열을 생성한다. 다만 출력되는 숫자들 간의 상관관계가 없어 보일 뿐이다.

또한 같은 알고리즘을 여러번 실행하더라도 다른 숫자가 나오도록 시작 숫자는 현재 시간 등을 사용해서 매번 바꿔준다. 이런 시작 숫자를 시드(seed)라고 한다.

따라서 시드를 사람이 수동으로 설정한다면 그 다음에 만들어지는 난수들은 예측할 수 있다.

파이썬에서 시드를 설정하는 명령은 seed이다. 인수로는 0과 같거나 큰 정수를 넣어준다.


In [2]:
np.random.seed(0)

이렇게 시드를 설정한 후 rand 명령으로 5개의 난수를 생성해 보자. 다른 난수 관련 명령어를 실행하지 말고 바로 다음 명령을 실행해야 한다.


In [3]:
np.random.rand(5)


Out[3]:
array([ 0.5488135 ,  0.71518937,  0.60276338,  0.54488318,  0.4236548 ])

몇번 더 난수를 생성한다.


In [4]:
np.random.rand(10)


Out[4]:
array([ 0.64589411,  0.43758721,  0.891773  ,  0.96366276,  0.38344152,
        0.79172504,  0.52889492,  0.56804456,  0.92559664,  0.07103606])

In [5]:
np.random.rand(10)


Out[5]:
array([ 0.0871293 ,  0.0202184 ,  0.83261985,  0.77815675,  0.87001215,
        0.97861834,  0.79915856,  0.46147936,  0.78052918,  0.11827443])

이제 시드를 0으로 재설정하고 다시 난수를 발생시켜 본다.


In [6]:
np.random.seed(0)

In [7]:
np.random.rand(5)


Out[7]:
array([ 0.5488135 ,  0.71518937,  0.60276338,  0.54488318,  0.4236548 ])

In [8]:
np.random.rand(10)


Out[8]:
array([ 0.64589411,  0.43758721,  0.891773  ,  0.96366276,  0.38344152,
        0.79172504,  0.52889492,  0.56804456,  0.92559664,  0.07103606])

In [9]:
np.random.rand(10)


Out[9]:
array([ 0.0871293 ,  0.0202184 ,  0.83261985,  0.77815675,  0.87001215,
        0.97861834,  0.79915856,  0.46147936,  0.78052918,  0.11827443])

아까와 같은 숫자가 나오는 것을 확인할 수 있다.

기존의 데이터의 순서 바꾸기

기존에 있는 데이터의 순서를 바꾸려면 shuffle 명령을 사용한다.


In [10]:
x = np.arange(10)
np.random.shuffle(x)
x


Out[10]:
array([3, 6, 4, 5, 2, 9, 7, 8, 1, 0])

기존의 데이터에서 샘플링하기

이미 있는 데이터 집합에서 일부를 선택하는 것을 샘플링(sampling)이라고 한다. 샘플링에는 choice 명령을 사용한다. choice 명령은 다음과 같은 인수를 가질 수 있다.

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

  • a : 배열이면 원래의 데이터, 정수이면 range(a) 명령으로 데이터 생성
  • size : 정수. 샘플 숫자
  • replace : 불리언. True이면 한번 선택한 데이터를 다시 선택 가능
  • p : 배열. 각 데이터가 선택될 수 있는 확률

In [11]:
np.random.choice(5, 5, replace=False)  # shuffle 명령과 같다.


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

In [12]:
np.random.choice(5, 3, replace=False)  # 3개만 선택


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

In [13]:
np.random.choice(5, 10) # 반복해서 10개 선택


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

In [14]:
np.random.choice(5, 10, p=[0.1, 0, 0.3, 0.6, 0])  # 선택 확률을 다르게 해서 10개 선택


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

난수 생성

NumPy의 random 서브패키지에는 난수를 생성하는 명령이 4가지가 있다.

  • rand: 0부터 1사이의 균일 분포
  • randn: 가우시안 표준 정규 분포
  • randint: 균일 분포의 정수 난수

rand 명령은 0부터 1사이에서 균일한 확률 분포로 실수 난수를 생성한다. 숫자 인수는 생성할 난수의 크기이다. 여러개의 인수를 넣으면 해당 크기를 가진 행렬을 생성한다.


In [15]:
np.random.rand(10)


Out[15]:
array([ 0.95894927,  0.65279032,  0.63505887,  0.99529957,  0.58185033,
        0.41436859,  0.4746975 ,  0.6235101 ,  0.33800761,  0.67475232])

In [16]:
np.random.rand(3, 5)


Out[16]:
array([[ 0.31720174,  0.77834548,  0.94957105,  0.66252687,  0.01357164],
       [ 0.6228461 ,  0.67365963,  0.971945  ,  0.87819347,  0.50962438],
       [ 0.05571469,  0.45115921,  0.01998767,  0.44171092,  0.97958673]])

randn 명령은 기댓값이 0이고 표준편차가 1인 가우시안 표준 정규 분포를 따르는 난수를 생성한다. 인수 사용법은 rand 명령과 같다.


In [17]:
np.random.randn(10)


Out[17]:
array([-0.30237513, -2.2244036 ,  0.72400636,  0.35900276,  1.07612104,
        0.19214083,  0.85292596,  0.01835718,  0.42830357,  0.99627783])

In [18]:
np.random.randn(3, 5)


Out[18]:
array([[-0.49114966,  0.71267817,  1.11334035, -2.15367459, -0.41611148],
       [-1.07089699,  0.22113881, -1.12305712, -1.05075796,  1.01207905],
       [ 1.54371643, -0.40211489,  0.8647491 , -0.22686923,  0.81116027]])

randint 명령은 다음과 같은 인수를 가진다.

numpy.random.randint(low, high=None, size=None)

만약 high를 입력하지 않으면 0과 low사이의 숫자를, high를 입력하면 lowhigh는 사이의 숫자를 출력한다. size는 난수의 숫자이다.


In [19]:
np.random.randint(10, size=10)


Out[19]:
array([0, 4, 3, 6, 9, 8, 0, 8, 5, 9])

In [20]:
np.random.randint(10, 20, size=10)


Out[20]:
array([10, 19, 16, 15, 13, 11, 18, 10, 14, 19])

In [21]:
np.random.randint(10, 20, size=(3,5))


Out[21]:
array([[16, 15, 17, 18, 18],
       [19, 12, 18, 16, 16],
       [19, 11, 16, 18, 18]])

정수 난수 통계

이렇게 발생시킨 난수가 실수값이면 히스토그램 등을 사용하여 분석하면 된다.

만약 난수가 정수값이면 unique 명령이나 bincount 명령으로 데이터 값을 분석할 수 있다. 이 명렬들은 random 서브패키지가 아니라 NumPy 바로 아래에 포함된 명령이다.

unique 명령은 데이터에서 중복된 값을 제거하고 중복되지 않는 값의 리스트를 출력한다. return_counts 인수를 True 로 설정하면 각 값을 가진 데이터 갯수도 출력한다.


In [22]:
np.unique([11, 11, 2, 2, 34, 34])


Out[22]:
array([ 2, 11, 34])

In [23]:
a = np.array(['a', 'b', 'b', 'c', 'a'])
index, count = np.unique(a, return_counts=True)

In [24]:
index


Out[24]:
array(['a', 'b', 'c'], 
      dtype='|S1')

In [25]:
count


Out[25]:
array([2, 2, 1])

그러나 unique 값은 데이터에 존재하는 값에 대해서만 갯수를 세므로 나올 수 있음에도 불구하고 데이터가 하나도 없는 값에 대한 정보를 주지 않는다.

따라서 데이터가 주사위를 던졌을 때 나오는 수처럼 특정 범위안의 수인 경우에는 bincountminlength 인수를 설정하여 쓰는 것이 더 편리하다. 이 때는 0 부터 minlength - 1 까지의 숫자에 대해 각각 카운트를 한다. 데이터가 없을 경우에는 카운트 값이 0이 된다.


In [26]:
np.bincount([1, 1, 2, 2, 2, 3], minlength=6)


Out[26]:
array([0, 2, 3, 1, 0, 0])