넘파이(Numpy) 소개

넘파이(Numpy)는 파이썬 표준라이브러리를 확장하는 모듈이며, 수치 데이터(numerical data)를 용이하게 생성 및 조작하기 위해 유용하게 사용된다.

이번 장에서는 넘파이 모듈의 기본 사용법을 소개한다.

넘파이 관련 내용은 아래 사이트에서 제동하는 자료를 참고한다.

넘파이 어레이(numpy.array) 객체 소개

파이썬에서 기본적으로 사용할 수 있는 수 관련 객체에는 크게 두 종류가 있다.

  • int형 또는 float형 숫자 객체
  • 튜플(tuple), 리스트(list) 또는 사전(dictrionary)처럼 숫자 객체들을 담을 수 있는 시퀀스 자료형 객체

하지만 이전에 살펴보았듯이 튜플의 경우는 수정이 안되는 등 유연성이 떠어지고, 리스트의 경우는 길이가 몇 만, 몇 십만, 몇 백만 처럼 매우 길어지면 데이터를 조작하는 데에 많은 시간이 소요되는 단점이 있고, 사전의 경우는 조작 속도는 매우 빠르나 키와 키값을 항상 사용해야 해서 문법상 제약이 따른다. 또한 행렬 등 다차원 시퀀스 데이터는 처리하기 힘들다.

넘파이(numpy) 모듈은 언급한 단점을 극복하기 위해 만들어진 라이브러리이며, 다음의 특징을 갖고 있다.

  • 다차원 어레이(multi-dimensional array) 지원
  • 빠른 처리 속도
  • 과학용 수치 계산에 활용 용이

넘파이 모듈을 사용하려면 먼저 numpy 모듈을 추가 설치해야 한다. 하지만 Anaconda 등을 사용하면 이미 설치되어 있고 넘파이 모듈을 임포트하기만 하면 된다. numpy 모듈의 약칭으로 np를 관례적으로 사용한다.


In [1]:
import numpy as np

넘파이 모듈에서 가장 주요한 요소는 어레이(array)이다. 어레이의 사용법은 리스트의 경우와 기본적으로 비슷하다. 먼저 0, 1, 2, 3으로 구성된 어레이를 생성하고자 하면 array 함수를 사용하면 된다.


In [2]:
a = np.array([0, 1, 2, 3])

array 함수는 리스트, 튜플을 입력받아 어레이 객체를 되돌려준다. 리스트와 튜플 어느 것을 써도 리턴값은 동일하다. 위 정의를 튜플을 이용하여 정의해도 결과는 같다.


In [3]:
b = np.array((0, 1, 2, 3))

In [4]:
c = a == b
c


Out[4]:
array([ True,  True,  True,  True], dtype=bool)

주의: 어레이 객체들 사이의 비교는 각 항목별로 실행된다.

a == b의 실행결과는 새로운 어레이이며 각 항목별로 비교한 결과인 불(bool) 값을 갖게 된다.

c의 실행결과를 보면 dtype이라는 키워드 인자가 추가되어 있다. dtype 키워드는 생성된 어레이 객체의 각 항목의 자료형을 갖고 있다.

주의: 어레이 객체의 각 항목들은 모두 동일한 자료형이어야 한다.

리스트, 튜플과는 달리 어레이의 각 항목은 동일한 자료형들로 구성되어 있다.

변수 a에는 int형 숫자들이 들어있다.


In [5]:
a.dtype


Out[5]:
dtype('int64')

변수 b도 int형 숫자들로 구성되어 있다.


In [6]:
b.dtype


Out[6]:
dtype('int64')

반면에 변수 c는 불값(bool)으로 구성되어 있다.


In [7]:
c.dtype


Out[7]:
dtype('bool')

a 또는 b와는 달리 c의 경우 dtype 명시하는 이유는 True, False의 의미가 참, 거짓의 의미 외에 0이 아닌 숫자, 0인 숫자 또한 의미할 수 있기 때문이다. 다음의 경우 dtype 키워드 인자값으로 int를 입력하면 자려형이 달리지는 것을 확인할 수 있다.


In [8]:
d = np.array([ True,  True,  True,  True], dtype=int)
d.dtype


Out[8]:
dtype('int64')

In [9]:
d


Out[9]:
array([1, 1, 1, 1])

array 함수는 dtype 키워드 인자외에 copy, order, subok, ndmin 등의 추가 키워드 인자를 사용할 수 있다. 각 키워드 인자의 활용은 필요할 경우 추가로 설명되어질 것이다.

어레이 활용 예제

어레이 자료형을 실제로 많이 활용되는 경우들은 다음과 같다.

  • 일정 시간 동안 반복적으로 측정되는 실험 및 시뮬레이션 값
  • 음파(sound wave) 등 측정장치를 통해 기록된 신호(signal)
  • 이미지에 사용되는 픽셀값
  • MRI 스캔 등에 사용되는 3-D 측정기록값
  • 등등.

빠른 처리 속도

앞서 언급한 대로 어레이는 리스트보다 빠르게 데이터를 처리한다. 아래의 예제는 1000개의 동일한 숫자들로 이루어진 리스트와 어레이를 조작하는 속도를 비교한 것이다. 어레이를 이용하는 경우가 60배 정도 빠름을 확인할 수 있다.


In [10]:
L = range(1000)

%timeit은 ipython에서 간단한 코드의 실행시간을 테스트하는 기능을 갖는다.

아래 코드는 0부터 999까지의 숫자를 각각 제곱하여 새로운 리스트를 생성하는 리스트 조건제시법 코드이다.


In [11]:
%timeit [i**2 for i in L]


10000 loops, best of 3: 64.6 µs per loop

arange 함수는 range와 동일한 기능을 수행한다. 다만 리스트가 아닌 어레이를 리턴한다.


In [12]:
a = np.arange(1000)

앞서 두 개의 리스트를 비교할 때 처럼 어레이 관련 연산은 기본적으로 각 항목별로 실행된다. 따라사 a ** 2는 각 항목을 제곱하라는 함수를 실행한다.


In [13]:
%timeit a**2


The slowest run took 47.90 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 1.21 µs per loop

Numpy 라이브러리 내에서 특정 함수 찾기

  • 특정 함수의 성질을 알고자 할 경우에는 함수 끝에 물음표('?')를 붙혀 실행하면 된다.

In [14]:
np.array?
  • 특정 성질과 관련된 함수들을 찾고자 할 경우 lookfor 함수를 사용한다. 예를 들어 어레이를 생성하는 기능과 관련된 함수를 찾고자 할 경우 다음과 같이 실행하면 된다.

      np.lookfor('create array')
    
    

    그러면 numpy 모듈 안에 정의된 모든 함수들의 주석(docstring)에 createarray 가 함께 포함되어 있는 함수들의 리스트를 보여준다.


In [15]:
np.lookfor('create array')


Search results for 'create array'
---------------------------------
numpy.array
    Create an array.
numpy.memmap
    Create a memory-map to an array stored in a *binary* file on disk.
numpy.diagflat
    Create a two-dimensional array with the flattened input as a diagonal.
numpy.fromiter
    Create a new 1-dimensional array from an iterable object.
numpy.partition
    Return a partitioned copy of an array.
numpy.ma.diagflat
    Create a two-dimensional array with the flattened input as a diagonal.
numpy.ctypeslib.as_array
    Create a numpy array from a ctypes array or a ctypes POINTER.
numpy.ma.make_mask
    Create a boolean mask from an array.
numpy.ctypeslib.as_ctypes
    Create and return a ctypes object from a numpy array.  Actually
numpy.ma.mrecords.fromarrays
    Creates a mrecarray from a (flat) list of masked arrays.
numpy.lib.format.open_memmap
    Open a .npy file as a memory-mapped array.
numpy.ma.MaskedArray.__new__
    Create a new masked array from scratch.
numpy.lib.arrayterator.Arrayterator
    Buffered iterator for big arrays.
numpy.ma.mrecords.fromtextfile
    Creates a mrecarray from data stored in the file `filename`.
numpy.asarray
    Convert the input to an array.
numpy.ndarray
    ndarray(shape, dtype=float, buffer=None, offset=0,
numpy.recarray
    Construct an ndarray that allows field access using attributes.
numpy.chararray
    chararray(shape, itemsize=1, unicode=False, buffer=None, offset=0,
numpy.pad
    Pads an array.
numpy.sum
    Sum of array elements over a given axis.
numpy.asanyarray
    Convert the input to an ndarray, but pass ndarray subclasses through.
numpy.copy
    Return an array copy of the given object.
numpy.diag
    Extract a diagonal or construct a diagonal array.
numpy.load
    Load arrays or pickled objects from ``.npy``, ``.npz`` or pickled files.
numpy.sort
    Return a sorted copy of an array.
numpy.array_equiv
    Returns True if input arrays are shape consistent and all elements equal.
numpy.dtype
    Create a data type object.
numpy.choose
    Construct an array from an index array and a set of arrays to choose from.
numpy.nditer
    Efficient multi-dimensional iterator object to iterate over arrays.
numpy.swapaxes
    Interchange two axes of an array.
numpy.full_like
    Return a full array with the same shape and type as a given array.
numpy.ones_like
    Return an array of ones with the same shape and type as a given array.
numpy.empty_like
    Return a new array with the same shape and type as a given array.
numpy.zeros_like
    Return an array of zeros with the same shape and type as a given array.
numpy.asarray_chkfinite
    Convert the input to an array, checking for NaNs or Infs.
numpy.diag_indices
    Return the indices to access the main diagonal of an array.
numpy.ma.choose
    Use an index array to construct a new array from a set of choices.
numpy.chararray.tolist
    a.tolist()
numpy.matlib.rand
    Return a matrix of random values with given shape.
numpy.savez_compressed
    Save several arrays into a single file in compressed ``.npz`` format.
numpy.ma.empty_like
    Return a new array with the same shape and type as a given array.
numpy.ma.make_mask_none
    Return a boolean mask of the given shape, filled with False.
numpy.ma.mrecords.fromrecords
    Creates a MaskedRecords from a list of records.
numpy.around
    Evenly round to the given number of decimals.
numpy.source
    Print or write to a file the source code for a Numpy object.
numpy.diagonal
    Return specified diagonals.
numpy.histogram2d
    Compute the bi-dimensional histogram of two data samples.
numpy.fft.ifft
    Compute the one-dimensional inverse discrete Fourier Transform.
numpy.fft.ifftn
    Compute the N-dimensional inverse discrete Fourier Transform.
numpy.busdaycalendar
    A business day calendar object that efficiently stores information
  • 함수 이름의 첫 알파벳을 적은 후 별표(*)와 물음표(?)를 응용하면 관련 함수들을 찾아준다. 예를 들어 con으로 시작하는 넘파이 함수를 찾고자 하면 다음과 같이 실행하면 된다.

In [16]:
np.con*?

어레이 생성하기

수동으로 1차원 어레이 생성하기


In [17]:
a = np.array([0, 1, 2, 3])
a


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

a에는 1차원 어레이가 할당되었다.


In [18]:
a.ndim


Out[18]:
1

생성된 어레의 _모양(shape)_은 shape 인스턴스변수를 이용하여 확인한다.


In [19]:
a.shape


Out[19]:
(4,)

즉, 변수 a에 할당된 어레이 array([0, 1, 2, 3])

  • 1차원 어레이이며,
  • 길이가 4이다. 숫자 4에 추가로 붙은 알파벳 L은 long int를 의미한다. 사용하는 컴퓨터마다 다른 형식을 사용할 수 있다.

위 행렬을 1차원 어레이를 표현한다면 아래와 같은 모양이다.

$$\left [ \begin{matrix} 0 \\ 1 \\ 2 \\ 3 \end{matrix} \right ]$$

즉, 길이가 d인 1차원 어레이는 d x 1 행렬에 대응한다.

주의: ndim, shape는 어레이 클래스에서 선언된 인스턴스변수이다.

클래스에는 기본적으로 변수와 함수가 포함되는데 클래스에서 선언된 변수를 인스턴스변수, 멤버변수, 또는 _속성_이라 부르고, 함수를 _메소드_라 부른다.

어레이 객체와 함께 사용할 수 있는 다른 인스턴스변수와 메소드는 이후에도 필요할 때마다 설명될 예정이다.

수동으로 2, 3차원 어레이 생성하기

예를들어 아래와 같은 2 x 3 행렬을 2차원 어레이로 구현할 수 있다.

$$\left [ \begin{matrix} 0 & 1 & 2 \\ 3 & 4 & 5 \\ 6 & 7 & 8\end{matrix} \right ]$$

In [20]:
b = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
b


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

즉, 앞서 언급된 행렬의 첫째 행이 위 어레이의 첫 번째 리스트로, 그리고 둘째 행이 어레이의 두 번째 리스트에 해당한다.

어레이의 차원은 ndim 인스턴스변수를 이용하여 확인한다.


In [21]:
b.ndim


Out[21]:
2

변수 b에 할당된 어레이의 모양(shape)은 2 x 3 행령에 해당하는 (2, 3)이다.


In [22]:
b.shape


Out[22]:
(3, 3)

len 함수는 첫번째 차원의 길이를 되돌려 준다.


In [23]:
len(b)


Out[23]:
3

연습문제

  • 아래에 묘사된 행렬을 2차원 어레이로 구현하라.
$$\left [ \begin{matrix} 5 & 3 & 1 \\ 2 & 4 & 6 \end{matrix} \right ]$$
  • len() 함수와 np.shape() 함수, 그리고 ndim 인스턴스변수들 사이에 어떤 관계가 있는지 설명하라.

자동으로 어레이 생성하기

앞서 살펴본 수동으로 어레이를 생성하는 방법은 실전에서는 별로 사용하지 않는다. 이제 numpy에서 어레이를 생성하는 다양한 방법을 살펴보도록 하자.

  • 먼저, range와 비슷한 기능을 수행하는 arange를 이용하여 일정하게 변하는 정수들의 어레이를 만들어보자.

In [24]:
a = np.arange(10)   # range(10)과 동일
a


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

In [25]:
b = np.arange(1, 9, 2)   # range(1, 9, 2)와 동일
b


Out[25]:
array([1, 3, 5, 7])
  • 특정 구간을 일정한 크기로 쪼개어 어레이를 생성할 수 있다. 예를 들어 0과 1 구간을 6개의 점으로 균등하게 쪼개어 어레이를 만들려면 아래와 같이 실행하면 된다.

In [26]:
c = np.linspace(0, 1, 6)   # 구간의 시작, 구간의 끝, 점의 개수
c


Out[26]:
array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])
  • 또한 구간의 끝점을 제외하고 6개의 점으로 균등하게 쪼개어 어레이를 만들려면 아래와 같이 실행하면 된다.

In [27]:
d = np.linspace(0, 1, 6, endpoint=False)
d


Out[27]:
array([ 0.        ,  0.16666667,  0.33333333,  0.5       ,  0.66666667,
        0.83333333])
  • 1로 채워진 어레이 구현하기

In [28]:
e = np.ones((3, 3)) # 인자: 원하는 모양(shape)의 튜플값
e


Out[28]:
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
  • 0으로 채워진 어레이 구현하기

In [29]:
e = np.zeros((2, 2)) # 인자: 원하는 모양(shape)의 튜플값
e


Out[29]:
array([[ 0.,  0.],
       [ 0.,  0.]])
  • 선형대수에서 중요한 역할을 수행하는 단위행렬(unit matrix)은 대각선은 1로 채우고 나머지는 모두 0으로 채워진다. 단위행렬에 해당하는 어레이를 구해보자.

In [30]:
e = np.eye(3)  # 인자:원하는 모양(shape)의 어레이의 길이(len)
e


Out[30]:
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
  • 리수트가 주어졌을 경우 리스트의 항목들을 대각선 값으로 갖는 어레이를 구현해 보자.

In [31]:
e1 = np.diag((1, 2, 3, 4)) # 인자: 튜플, 리스트 또는 1차원 어레이
e1


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

In [32]:
e2 = np.diag([1, 2, 3, 4]) # 인자: 튜플, 리스트 또는 1차원 어레이
e2


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

In [33]:
e3 = np.diag(np.arange(1,5)) # 인자: 튜플, 리스트 또는 1차원 어레이
e3


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

In [34]:
e1 == e2


Out[34]:
array([[ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]], dtype=bool)

In [35]:
e2 == e3


Out[35]:
array([[ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]], dtype=bool)
  • 난수(random number)들로 이루어진 어레이를 구할 수 있다. numpy.random 모듈에 있는 rand, randn 함수를 활용할 수 있다.

    • numpy.random.rand 함수: 0과 1사이에서 난수를 구한다. 방식은 균등분포를 사용한다.
    • numpy.random.randn 함수: 가우시안(Gaussian) 정규분포 방식을 사용하여 난수를 구한다.

    각 함수의 인자는 원하는 모양(shape)에 해당하는 튜플이다. 단, numpy.ones 또는 numpy.zeros 함수들과는 달리 괄호를 사용하지 않는다.


In [36]:
f = np.random.rand(4) 
f


Out[36]:
array([ 0.87944938,  0.99688606,  0.26486627,  0.34792906])

In [37]:
f1 = np.random.rand(2, 3)
f1


Out[37]:
array([[ 0.47651474,  0.37403808,  0.62014532],
       [ 0.05074769,  0.04194931,  0.57250503]])

In [38]:
g = np.random.randn(4) 
g


Out[38]:
array([-0.10619318,  0.55775992, -0.95435902,  0.70275379])

In [39]:
g1 = np.random.randn(1, 2)
g1


Out[39]:
array([[ 1.01103717,  0.13653793]])
  • 주의

    난수 관련 함수들이 생성하는 난수는 사실 정말로 무작위로 생성되지는 않는다. 각 프로그래밍 언어마다 난수를 만드는 방식이 정해져 있고, 정해진 방식에 맞추어 이미 난수표를 갖고 있다. 즉, rand 함수가 호출되는 순서에 따라 사실 동일한 숫자를 생성하지만 사람 눈에는 무작위 숫자들로 보이는 것 뿐이다.

    이런 현상을 해결하기 위해 시드(seed) 값을 사용하여 생성되는 난수의 순서를 보다 무작위적으로 보여지게 할 수 있다. 예를 들어 시드값을 0을로 하면 매번 동일한 난수들이 생성되는 것을 아래와 같이 확인할 수 있다.


In [40]:
np.random.seed(0) ; np.random.rand(4)


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

In [41]:
np.random.seed(0) ; np.random.rand(4)


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

시드값을 변경하면 생성되는 난수들을 정말로 무작위처럼 보이게 할 수 있다.


In [42]:
np.random.seed(1234) ; np.random.rand(4)


Out[42]:
array([ 0.19151945,  0.62210877,  0.43772774,  0.78535858])

In [43]:
np.random.seed(2000) ; np.random.rand(4)


Out[43]:
array([ 0.57051729,  0.56452876,  0.48844183,  0.33647775])

연습문제

np.empty 함수의 정의 및 활용법을 살펴 본 후 어떻게 활용하면 좋을지를 설명하라.

어레이에서 사용되는 기본 자료형

어레이 객체에서 사용되는 항목들의 자료형을 알아보고자 한다. dtype 인스턴스변수에 저장되는 자료형값들이다.

앞선 예제에서 2., 3. 등등 정수에 점이 사용되는 경우를 본 적이 있는데, 이는 어레이의 자료형이 int가 아닌 float임을 보여주려 하기 때문이다.


In [44]:
a = np.array([1, 2, 3])
a.dtype


Out[44]:
dtype('int64')

위 어레이의 dtypefloat로 바꾸고자 한다면 dtype 키워드 인자를 사용해야 한다.


In [45]:
b = np.array([1, 2, 3], dtype=float)
b.dtype


Out[45]:
dtype('float64')

새로 생성된 어레이의 모양도 다르다.


In [46]:
b


Out[46]:
array([ 1.,  2.,  3.])

물론 애초부터 float 자료형으로 선언할 수 있다.


In [47]:
a = np.array([1., 2, 3.])
a.dtype


Out[47]:
dtype('float64')

어레이가 기본적으로 사용하는 자료형은 float이다.


In [48]:
a = np.ones((3,3))
a.dtype


Out[48]:
dtype('float64')

np.ones 함수의 기본 정의를 확인하면 보다 정확히 알 수 있다.


In [49]:
np.ones?

intfloat 이외에도 다음의 자료형들이 어레이에서 지원된다.

  • complex: 복소수 자료형

      In [58]: d = np.array([1+2j, 3+4j, 5+6j]).dtype
      Out[58]: dtype('complex128')
  • bool: 불리언 자료형

      In [59]: e = np.array([True, False, False, True]).dtype
      Out[59]: dtype(’bool’)
  • S: 문자열 자료형

      In [60]: f = np.array([’Bonjour’, ’Hello’]).dtype
      Out[60]: dtype(’S7’)

등등.

주의:

int32, int64, float64, S8, S12 자료형 이름 뒤에 숫자가 사용되기도 한다. 이는 어레이에서 사용되는 각각의 항목의 값들이 가질 수 있는 최대 크기를 의미한다. 즉, 사용된 정수들이 32비트로 모두 담을 수 있는지, 또는 사용된 문자열들의 길이가 최대 8바이트 또는 12바이트 인지를 나타낸다.

예를 들어 아래 문자열로 이루어진 어레이에서 가장 긴 문자열이 4바이트이기 때문에, dtype이 'S4'이다.


In [50]:
np.array(['ab', 'abc', 'abcd']).dtype


Out[50]:
dtype('S4')