3.3. 행렬#

행렬(array)은 수학에서 나오는 행렬(matrix) 형태의 자료를 컴퓨터에 저장하기 위한 자료 형식이다.

이 과목에서는 행렬 형식보다 데이터프레임 형식의 자료를 주로 이용한다. 이 장에서는 라이브러리 numpy 에서 제공하는 행렬의 간단한 사용법만 살펴볼것이다.

행렬 형식은 라이브러리 numpy 를 불러서 사용할 수 있다. 다음은 numpy 라이브러리를 np 로 지칭하여 사용하겠다는 명령이다.

import numpy as np

행렬 형식의 자료는 차원(dimension)이 있다. 이 과목에서는 1차원 행렬과 2차원 행렬만 배워 볼 것이다.

일단 숫자 1,2,3,4,5,6 으로 구성된 1차원 행렬은 다음과 같이 정의한다. np 로 지정된 numpy 라이브러리에서 함수 array 를 사용한다. 괄호 () 안에 숫자를 리스트로 묶어서 넣어주면 된다.

a = np.array([1,2,3,4,5,6])
a
array([1, 2, 3, 4, 5, 6])

행렬과 리스트(list)의 차이점은 행렬은 리스트와 달리 같은 형식의 자료만 모아 놓을 수 있다.

예를 들어 다음과 같이 문자와 숫자를 함께 저장하는 행렬은 정의가 안된다. 아래 명령을 실행시키면 숫자가 모두 문자열로 변환된 행렬이 생성되는 것을 알 수 있다.

np.array([1, 2, 'a'])
array(['1', '2', 'a'], dtype='<U21')

행렬의 원소는 다음과 같이 순서의 위치를 괄호 [] 안에 넣어서 불러낼 수 있다. 위치의 순서가 0부터 시작하는 것에 유의하자.

a[0]
1
a[4]
5
a[6]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-6-84c1e027215d> in <module>
----> 1 a[6]

IndexError: index 6 is out of bounds for axis 0 with size 6

하나의 원소가 아닌 여러 개의 원소들은 리스트를 이용하여 다음과 같이 선택할 수 있다.

a[[0,1,2]]
array([1, 2, 3])
a[[0,3,5]]
array([1, 4, 6])

아래 코드에서 0:3 은 일정하게 감소하거나 증가하는 숫자들에 대한 형식이며 이를 범위(range) 형식이라고 부른다. 이런 범위 형식는 문자열의 일부를 추출할 때 사용하였다.

start:end

0:30 으로 시작해서 3-1=2 로 끝나는 정수의 범위을 의미한다. 범위가 end 로 끝나지 않고 end-1 로 끝나는 것에 유의하자.

a[0:3]
array([1, 2, 3])

이제 numpy 에서 일정한 크기로 증가하거나 감소하는 수들로 이루어진 행렬을 한번에 만드는 함수 arange 를 사용해보자.

numpy 라이브러리의 함수 arange()는 괄호 안에 3가지 인자(argument)를 넣어서 생성된 범위 형식에 따라서 행렬을 만들어 준다.

arange(start, stop, step)
  • start: 시작하는 수

  • stop: 끝나는 수

  • step: 증가하는 수 (음수도 가능)

만약 첫 번째 인자만 있는 경우 0으로 시작해서 start-1 로 끝나는 정수의 행렬을 만들어 준다.

np.arange(6)
array([0, 1, 2, 3, 4, 5])

만약 첫 번째와 두 번째 인자만 있는 경우 start 으로 시작해서 end-1 로 끝나는 정수의 행렬을 만들어 준다.

np.arange(1,10)
array([1, 2, 3, 4, 5, 6, 7, 8, 9])

세 번째 인자는 증가하는 수를 지정한다.

np.arange(0, 10, 3)
array([0, 3, 6, 9])

함수 arange() 는 정수 뿐만 아니라 부동소숫점 형태의 행렬도 생성할 수 있다

np.arange(0, 1, 0.1)
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
np.arange(10, -10, -2)
array([10,  8,  6,  4,  2,  0, -2, -4, -6, -8])

이제 2차원 행열을 만들어 보자. 다음과 같은 행이 2개이고 열이 4개인 2차원 행렬은 다음 코드로 만들 수 있다.

\[\begin{split} \begin{pmatrix} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \end{pmatrix} \end{split}\]

아래에서 reshape(r,c) 메소드는 1차원 행렬을 행의 수가 r, 열의 수가 r 인 행렬로 만드는 기능을 한다.

b = np.arange(1,9).reshape(2, 4)
b
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])

위의 결과를 활용하면 동일한 2차원 행렬을 다음과 같이 만들 수 있다.

c = np.array( [ [1, 2, 3, 4], [5, 6, 7, 8] ])
c
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])

2차원 행렬의 원소는 1차원과 유사하게 일부를 지정하여 볼 수 있다.

b[0,0]
1
b[0,0:4]
array([1, 2, 3, 4])
b[0:2, 1:3]
array([[2, 3],
       [6, 7]])

행렬 형식은 범위가 정의된 범위에서 벗어나도 오류가 나오지 않고 주어진 범위에 포함되는 부분만 슬라이싱해준다.

이러한 현상을 유용하게 이용할 수도 있겠지만 언제나 유효한 범위를 사용하는갓이 좋다.

b[1:3, 1:3]
array([[6, 7]])

행렬을 슬라이싱하는 경우 한 개의 행, 여러 개의 열을 선택하면 우리의 직관과 다르게 결과가 나오니 주의하자.

일단 규칙은 행의 수가 1 이거나 열의 수가 1이면 결과는 1차원 행렬이 된다.

c = b[0:2,1]
c
array([2, 6])
c.ndim
1
d = b[1, 0:3]
d
array([5, 6, 7])
d.ndim
1