만약 NumPy 라이브러리를 처음 접한다면 NumPy의 기초가 설명되어 있는 이 글을 참고하시길 바랍니다.
이번 글에서는 NumPy의 배열을 조작하는 방법을 조금 더 소개하고, 배열을 수학적으로 활용할 때 필요한 함수들과 메소드들을 소개하겠습니다.
① 1차원 배열을 2차원 배열로 바꾸기
배열에서 일렬로 나열되어 있는 요소들에 축을 추가해서 기존 배열의 dimension을 증가시킬 수 있습니다(1차원 배열은 2차원 배열로, 2차원 배열로 3차원 배열로). 이때 사용할 수 있는 것은 np.newaxis와 np.expand_dims입니다.
>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> a.shape
(6,)
>>> a_row = a[np.newaxis, :]
>>> a_row.shape
(1, 6)
>>> print(a_row)
[[1 2 3 4 5 6]]
>>>
>>> a_col = a[:, np.newaxis]
>>> a_col.shape
(6, 1)
>>> print(a_col)
[[1]
[2]
[3]
[4]
[5]
[6]]
a_row는 np.newaxis를 1차원에 추가해서 a_row.shape가 (1, 6)이 된 것을 확인할 수 있습니다. a_col은 반대로 np.newaxis를 2차원에 추가해서 a_col.shape가 (6, 1)이 되었습니다.
np.expand_dims를 사용해서도 지정한 위치에 축을 추가할 수 있습니다.
>>> b = np.array([1, 2, 3, 4, 5, 6])
>>> b.shape
(6,)
>>> b_row = np.expand_dims(a, axis=0)
>>> b_row.shape
(1, 6)
>>> b_col = np.expand_dims(a, axis=1)
>>> b_col.shape
(6, 1)
② 기존의 배열로부터 새로운 배열 생성하기
i) 2개의 배열을 하나로 합치기
a1과 a2라는 2차원 배열이 존재한다고 합시다:
>>> a1 = np.array([[1, 1],
... [2, 2]])
>>> a2 = np.array([[3, 3],
... [4, 4]])
np.vstack()을 사용해서 세로로 합칠 수 있고, np.hstack()을 사용해서 가로로 합칠 수 있습니다.
>>> np.vstack((a1, a2))
array([[1, 1],
[2, 2],
[3, 3],
[4, 4]])
>>> np.hstack((a1, a2))
array([[1, 1, 3, 3],
[2, 2, 4, 4]])
ii) 하나의 배열을 여러개의 작은 배열로 나누기
다음과 같은 배열 x가 있다고 합시다:
>>> x = np.arange(1, 13).reshape(2, 6)
>>> x
array([[ 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12]])
np.hsplit()을 사용해서 리턴할 배열의 개수나, 배열을 나누는 열의 위치를 지정할 수 있습니다. 전자는 정수의 형태로 숫자를 입력하면 되고, 후자는 튜플의 형태로 원하는 위치를 입력하면 됩니다.
>>> np.hsplit(x, 3)
[array([[1, 2],
[7, 8]]), array([[ 3, 4],
[ 9, 10]]), array([[ 5, 6],
[11, 12]])]
>>> np.hsplit(x, (3, 4))
[array([[1, 2, 3],
[7, 8, 9]]), array([[ 4],
[10]]), array([[ 5, 6],
[11, 12]])]
iii) 배열 복사하기
view 메소드를 사용하면 기존의 배열과 동일하게 보이는 새로운 배열을 만들 수 있습니다.
>>> a = np.array([[0, 1, 2, 3],
[4, 5, 6, 7]])
>>> b = a.view()
>>> b
array([[0, 1, 2, 3],
[4, 5, 6, 7]])
그런데 view 메소드를 사용해서 복사된 새로운 배열 값을 변경하면 원본의 값도 변경됩니다. 즉, 얕은 복사가 된 것입니다.
>>> b[1][1] = 99
>>> b
array([[ 0, 1, 2, 3],
[ 4, 99, 6, 7]])
>>> a
array([[ 0, 1, 2, 3],
[ 4, 99, 6, 7]])
앞서 i)와 ii)에서 소개한 함수들도 view 메소드와 마찬가지로 얕은 복사가 됩니다. 깊은 복사를 하기 위해서는 copy 메소드를 사용하면 됩니다. copy 메소드의 사용방법은 view 메소드와 동일합니다. * slicing과 indexing을 사용해서 새로운 배열을 생성하는 방법은 리스트와 동일하기 때문에 따로 설명을 추가하지는 않았습니다.
* 조심해야 할 점은 리스트의 경우 깊은 복사가 되는 반면, 배열의 경우 얕은 복사가 된다는 차이가 있다는 것입니다.
③ 배열 연산
앞서, 배열에 축을 추가하거나, 기존의 데이터들을 통해 새로운 배열을 만드는 것은 연산을 위해 사전에 데이터를 조작하는 과정에 필요한 것입니다.
NumPy의 배열은 기본적으로 사칙연산을 모두 지원합니다. data1과 data2 배열이 다음과 같이 있다고 합시다:
>>> data1 = np.array([1, 2])
>>> data2 = np.ones(2, dtype=int)
사칙연산 기호를 사용해서 배열 간의 사칙연산을 진행할 수 있습니다.
>>> data1 + data2
array([2, 3])
>>> data1 - data2
array([0, 1])
>>> data1 * data2
array([1, 4])
>>> data1 / data2
array([1., 1.])
배열 안의 요소들 사이에서의 사칙연산도 가능합니다.
ex) sum 메소드는 n차원 배열에서 요소들의 합을 리턴합니다. 이때, 축을 지정하면 그 축을 기준으로 요소들을 합쳐 새로운 배열을 생성합니다. axis에 숫자를 매기는 순서는 인덱스 접근 순서와 같습니다.
>>> a = np.array([[[ 1, 2],
[ 3, 4]],
[[ 5, 6],
[ 7, 8]],
[[ 9, 10],
[11, 12]]])
>>> a.sum(axis=0)
array([[15, 18],
[21, 24]])
>>> a.sum(axis=1)
array([[ 4, 6],
[12, 14],
[20, 22]])
>>> a.sum(axis=2)
array([[ 3, 7],
[11, 15],
[19, 23]])
④ 브로드캐스팅
브로드캐스팅은 배열과 숫자와의 연산, 또는 크기가 서로 다른 배열 간의 연산을 지원하는 개념입니다. 행렬의 연산과 동일하게 진행된다고 생각하면 됩니다. 스칼라와 행렬 곱셈, 크기가 맞는 서로 다른 행렬의 연산도 가능합니다.
>>> data1 = np.array([1.0, 2.0])
>>> data * 1.6
array([1.6, 3.2])
>>> data2 = np.array([[1, 2],
[3, 4],
[5, 6]])
>>> data3 = data2.copy()
>>> data2 * data3
array([[ 1, 4],
[ 9, 16],
[25, 36]])
⑤ 배열의 형태 바꾸기
[Python] NumPy 라이브러리 기초 1에서 설명한 reshape 메소드를 사용하지 않고 배열의 형태를 바꾸는 방법들을 소개하겠습니다.
▶ 전치행렬
먼저 배열을 행렬로 보는 관점을 유지했을 때, transpose 메소드와 T를 통해 전치행렬의 개념을 가져올 수 있습니다.
>>> arr = np.arange(6).reshape((2, 3))
>>> arr
array([[0, 1, 2],
[3, 4, 5]])
>>> arr.transpose()
array([[0, 3],
[1, 4],
[2, 5]])
>>> arr.T
array([[0, 3],
[1, 4],
[2, 5]])
▶ 배열 반전하기
배열을 반전해서 요소들의 순서를 뒤집기 위해서 np.flip() 함수를 사용할 수 있습니다.
>>> arr = np.array([[1, 2, 3], [4, 5, 6]])
>>> reversed_arr = np.flip(arr)
>>> reversed_arr
array([[6, 5, 4],
[3, 2, 1]])
▶ n차원 배열을 1차원 배열로 만들기
n차원 배열을 1차원 배열로, 즉, 요소들을 순서에 맞춰서 일렬로 나열하기 위해서는 np.flatten()와 np.ravel() 함수를 사용할 수 있습니다.
>>> arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
>>> arr.flatten()
array([1, 2, 3, 4, 5, 6, 7, 8])
>>> arr.ravel()
array([1, 2, 3, 4, 5, 6, 7, 8])
두 함수의 차이는 np.flatten()의 경우 깊은 복사를 사용하고, np.ravel()은 얕은 복사를 사용한다는 것입니다. (np.ravel()로 새롭게 만들어진 배열의 값을 변경한다면 원본 배열의 값도 변경됩니다.)
'데이터 분석 > 기초' 카테고리의 다른 글
[Python] Pandas 라이브러리 기초 2 (0) | 2023.02.03 |
---|---|
[Python] Pandas 라이브러리 기초 1 (0) | 2023.01.24 |
[Python] NumPy 라이브러리 기초 1 (0) | 2022.11.21 |
[Python] 모듈과 패키지 (0) | 2022.10.22 |
[Python] 함수(Function)와 메서드(Method)의 차이 (0) | 2022.10.09 |