본문 바로가기
데이터 분석/데이터 전처리

[Numpy] 3. 배열 연산(Operation)

by 부자 꽁냥이 2021. 9. 21.

반갑습니다~  꽁냥이입니다~!! 저번 포스팅에서는 Numpy 배열의 Slicing 방법을 알아보았는데요.

 

[Numpy] 2. Numpy 배열 Slicing

 

[Numpy] 2. Numpy 배열 Slicing

안녕하세요~ 꽁냥이에요. 요즘 Numpy에 대한 글을 포스팅하고 있는데요. [Numpy] 1. Numpy 배열 생성하기 [Numpy] 1. Numpy 배열 생성하기 안녕하세요~ 꽁냥이에요. Numpy 모듈은 배열의 조작 및 연산에 최적

zephyrus1111.tistory.com

 

이번 포스팅에서는 배열의 연산에 대해서 알아보겠습니다. 여기서 다루는 내용은 다음과 같습니다.

 

1. 사칙 연산 및 행렬 곱

2. 삽입, 삭제, 수정, 결합


   1. 사칙 연산 및 행렬 곱

1.1 사칙 연산

먼저 배열 간 사칙 연산에 대해서 알아보겠습니다. 배열 간 사칙 연산은 모양이 똑같은 배열끼리 가능하며 원소별로 사칙 연산이 적용됩니다.

 

a = np.array([1,2,5])
b = np.array([3,1,2])
a+b 
# array([4, 3, 7])
a-b 
# array([-2,  1,  3])
a*b 
# array([ 3,  2, 10])
a/b 
# array([0.33333333, 2.        , 2.5       ])

 

아래 그림은 배열 간 덧셈 처리 방식이 원소별로 이루어지는 것을 나타낸 것입니다.

 

 

만약 배열의 모양이 똑같지 않은 경우 사칙 연산을 적용하게 되면 아래와 같이 "operands could not be broadcast together with shapes ~" 메시지를 출력하면서 ValueError를 발생시킵니다.

 

 

이번엔 배열과 상수(스칼라)의 사칙연산을 알아보겠습니다. 아래 코드를 살펴보세요~

 

a = np.array([3,7,4])
a+1 
#array([4, 8, 5])
a-3
# array([0, 4, 1])
a*3
# array([ 9, 21, 12])
a/2
# array([1.5, 3.5, 2. ])

 

배열과 상수의 사칙연산은 특별한 처리 방식을 갖고 있는데요. 아래 그림과 같이 주어진 배열과 같은 모양의 1로 이루어진 배열을 만든 뒤 상수를 곱한 배열을 만들어줍니다. 이러한 과정을 Broadcasting이라고 합니다. 그런 다음 원소별로 사칙연산이 이루어집니다.

 


1.2 행렬 곱

행렬은 2차원 배열로 볼 수 있으며 행렬 곱은 앞의 행렬의 열의 개수와 뒤의 행렬의 행의 개수가 일치할 때 연산이 가능합니다. 

 

행렬 곱 실행을 위해서 먼저 아래와 같이 2개의 행렬을 생성하고 shape를 이용하여 행렬의 모양을 확인합니다.

 

A = np.array([[1,2],[1,3],[2,2]])
B = np.array([[1,1],[-1,1]])

A.shape
# (3, 2) 3X2 행렬
B.shape
# (2, 2) 2X2 행렬

 

행렬 A의 칼럼 개수(2개)와 행렬 B의 행의 개수(2개)가 일치하므로 행렬 곱 AB가 가능합니다. 행렬 곱은 numpy 모듈의 matmul이나 배열 객체가 가지고 있는 dot 메서드를 이용하여 계산할 수 있습니다.

 

np.matmul(A,B) ## 또는 A.dot(B)
# array([[-1,  3],
#        [-2,  4],
#        [ 0,  4]])

 

꽁냥이는 개인적으로 matmul보다는 dot을 주로 이용합니다. 이게 더 편하더라고요~^0^

 

만약 곱셈이 정의가 안 되는(열 개수와 행 개수가 불일치하는 경우)데 행렬 곱을 수행하려고 하면 아래와 같이 "shape ~ not aligned ~"와 같은 메시지와 함께 ValueError를 발생시킵니다.

 

 

참고로 matmul이나 dot은 2차원 배열뿐만 아니라 1차원 배열끼리의 행렬 곱도 계산해줍니다. 엄밀히 말하면 이 경우 벡터의 내적(Inner Product)을 계산해줍니다.

 

a = np.array([1,2,3])
b = np.array([1,-1,0])

np.matmul(a,b)
# -1
a.dot(b)
# -1

   2. 삽입, 삭제, 수정, 결합

2.1 삽입

- 1차원 배열 원소 및 배열 삽입 -

먼저 1차원 배열에서 특정 위치에 원소와 배열을 삽입하는 방법에 대해서 알아볼게요. Numpy에서 삽입과 관련된 연산은 insert를 주로 이용하는데요. 사용 방법은 다음과 같습니다.


np.insert(배열, 삽입 위치, 삽입할 원소 또는 배열)


아래 코드는 배열의 특정 위치에 원소와 배열을 삽입하는 코드입니다. 금방 이해되실 거예요.

 

a = np.array([11,2,5,6]) 
np.insert(a,1,-9) # 배열 a의 두번째 위치에 -9를 삽입한다.
# array([11, -9,  2,  5,  6])

np.insert(a,0,[1,3]) # 배열 a의 첫 번째 위치에 배열 [1,3]을 삽입한다.
# array([ 1,  3, 11,  2,  5,  6])

- 2차원 배열 행 삽입 & 열 삽입 - 

이번엔 2차원 배열에서 행이나 열을 삽입해보겠습니다. 여기에서도 insert를 사용하는데 1차원 배열 때와 다른 것은 axis를 지정해서 행을 삽입할지 열을 삽입할지를 결정해줘야 한다는 점이에요. axis = 0은 행을 삽입한다는 뜻이며 axis = 1은 열을 삽입한다는 뜻입니다.

 

A = np.array([[1,2],[3,3],[2,4]])
np.insert(A, 1, [-1,-1], axis=0) ## 두번째 행에 배열 삽입.
# array([[ 1,  2],
#        [-1, -1],
#        [ 3,  3],
#        [ 2,  4]])

np.insert(A, 2, [-1,-1,3], axis=1) ## 세 번째 열에 배열 삽입
# array([[ 1,  2, -1],
#        [ 3,  3, -1],
#        [ 2,  4,  3]])

 


2.2 삭제

- 1차원 배열 -

1차원 배열에서 원소를 삭제하는 방법에 대해 알아보겠습니다. 삭제는 delete를 이용하며 기본적인 사용 방법은 다음과 같습니다.


np.delete(배열, 삭제 위치)


 

이제 코드를 통해 살펴볼게요. 금방 이해되실 거예요. 삭제 위치는 하나의 숫자뿐만 아니라 삭제하고 싶은 위치를 리스트 형태로 넣을 수 있어요.

 

a = np.array([11,2,5,6])
np.delete(a,0) ## 첫 번째 원소 삭제
# array([2, 5, 6])
np.delete(a,[0,2]) ## 첫 번째 & 세 번째 원소 삭제
# array([2, 6])
np.delete(a,range(2)) ## 첫 번째 부터 두 번째 까지 모든 원소 삭제
# array([5, 6])

- 2차원 배열 행 삭제, 열 삭제 -

아래 코드는 행렬에서 특정 위치의 열과 행을 삭제하는 코드입니다. 원리는 1차원 배열에서 살펴봤던 것과 비슷합니다. 다만 insert와 비슷하게 axis를 설정하여 행을 삭제(axis=0)할지 열을 삭제(axis=1)할지 결정해야 합니다.

 

A = np.array([[1,2],[3,3],[2,4]])
np.delete(A, 1,  axis=0) ## 두 번째 행 삭제.
# array([[1, 2],
#        [2, 4]])

np.delete(A,[0,2],axis=0) ## 첫 번째 & 세 번째 행 삭제
# array([[3, 3]])

np.delete(A, 0, axis=1) ## 첫 번째 열 삭제
# array([[2],
#        [3],
#        [4]])

 

만약 특정 위치의 행과 열을 동시에 지우고 싶다면 아래와 같이 delete를 두 번 써주면 됩니다.

 

B = np.array([[1,2,3,4],[3,3,1,1],[2,4,4,1]])
del_col_index = 1
del_row_index = 2

## 세 번째 행과 두 번째 열 삭제
np.delete(np.delete(B, del_row_index, axis=0), del_col_index, axis=1)
# array([[1, 3, 4],
#        [3, 1, 1]])

2.3 수정

이번엔 배열을 수정하는 방법에 대해서 알아볼 텐데요. 여기서 수정한다는 것은 원래 있던 원소를 다른 값으로 대체한다는 뜻이에요.

- 1차원 배열 수정 -

1차원 배열은 인덱스를 통하여 수정할 수 있습니다.

 

a = np.array([1,3,6,11,21])
a[1] = 4 ## 두 번째 원소를 4로 수정
# array([ 1,  4,  6, 11, 21])
a[1:4] = 2 ## 두 번째부터 세 번째까지 모두 2로 수정
# array([ 1,  2,  2,  2, 21])
a[1:4] = [12,13,14] ## 두 번째부터 세 번째까지 12,13,14 로 수정
# array([ 1, 12, 13, 14, 21])
a[[1,4]] = [22,44] ## 두 번째와 다섯 번째 원소 22, 44로 수정
# array([ 1, 22,  6, 11, 44])

- 2차원 배열 수정 -

2차원 배열은 개별 원소 수정과 행 또는 열을 수정하는 방법에 대해서 알아보겠습니다. 먼저 개별 원소는 인덱스를 통해서 수정할 수 있습니다.

 

A = np.array([[1,2,3,4],[3,3,1,1],[2,4,4,1]])
A[1,3] = 88 ## 두 번째행, 네 번째 원소를 88로 수정
# array([[ 1,  2,  3,  4],
#        [ 3,  3,  1, 88],
#        [ 2,  4,  4,  1]])

A[1,[0,3]] = [100, 200] ## 2행 1열 원소는 100, 2행 4열 원소는 200으로 수정
# array([[  1,   2,   3,   4],
#        [100,   3,   1, 200],
#        [  2,   4,   4,   1]])

 

아래 코드는 행과 열을 수정한 것입니다.

 

A = np.array([[1,2,3,4],[3,3,1,1],[2,4,4,1]])
A[1,:] = [11,22,33,44] ## 두 번째 행 수정
# array([[ 1,  2,  3,  4],
#        [11, 22, 33, 44],
#        [ 2,  4,  4,  1]])
A[:,2] = [100, 200, 300] ## 세 번째 칼럼 수정
# array([[  1,   2, 100,   4],
#        [  3,   3, 200,   1],
#        [  2,   4, 300,   1]])

2.4 결합

- 1차원 배열 결합 -

1차원 배열 결합은 옆으로 길게 합치는 것을 말합니다. 합치는 것은 concatenate를 사용하면 되는데요. 합치고자 하는 배열을 리스트나 튜플로 묶으면 됩니다. 그 외에도 2가지 방법을 추가로 소개합니다.

 

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

np.concatenate((a,b)) ## concatenate 이용하기
# array([1, 2, 3, 4, 5, 6])
np.insert(a,len(a), b) ## insert 이용하기
# array([1, 2, 3, 4, 5, 6])
np.array(list(a)+list(b)) ## 리스트 합 이용하기
# array([1, 2, 3, 4, 5, 6])

 

성능을 확인해보니 속도 차이는 없었습니다. 하지만 concatenate를 사용하는 것이 제일 간단하므로 결합을 할 때에는 concatenate를 사용하는 것을 추천합니다~

 

concatenate는 원하는 만큼 배열을 리스트로 추가하여 결합할 수 있습니다.

 

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

np.concatenate([a,b,c])
# array([1, 2, 3, 4, 5, 6, 7, 8, 9])

- 2차원 배열 결합 - 

concatenate를 사용하면 2차원 배열 즉 행렬 간 결합도 할 수 있습니다. 여기에서도 axis를 이용하여 행 방향으로 합칠지(axis=0) 열 방향으로 합칠지(axis=1) 결정해줘야 합니다. 특히 행렬과 1차원 배열을 결합하는 경우 1차원 배열을 2차원으로 바꿔줘야 하며 행 방향으로 결합할 때에는 행벡터(1Xn), 열 방향으로 결합할 때에는 열 벡터(nX1)로 만들어줘야 합니다.

 

A = np.array([[1,3], [-1,-1]])
B = np.array([[10,20],[30,40]])
c = np.array([[100,300]]) ## np.array([100, 300])은 안된다. 반드시 2차원 형태로 만들어야함.

np.concatenate((A,c),axis=0)
# array([[  1,   3],
#        [ -1,  -1],
#        [100, 300]])
np.concatenate((A,c.T),axis=1) ## Transpose를 이용하여 c를 열벡터로 바꿔야한다.
# array([[  1,   3, 100],
#        [ -1,  -1, 300]])
np.concatenate((A,B), axis=0)
# array([[ 1,  3],
#        [-1, -1],
#        [10, 20],
#        [30, 40]])
np.concatenate((A,B), axis=1)
# array([[ 1,  3, 10, 20],
#        [-1, -1, 30, 40]])

 


이번 포스팅에서는 배열 연산에 대해서 알아보았습니다. 오늘 알려드린 내용은 데이터 분석 시 많이 사용되므로 도움이 되실 거라 생각해요. 다음 포스팅에서도 좋은 내용으로 찾아뵙겠습니다. 지금까지 꽁냥이의 글 읽어주셔서 감사합니다.

 

참고자료

Numpy Tutorial - https://numpy.org/doc/stable/user/absolute_beginners.html#


댓글


맨 위로