[Home] Python으로 돌아가기

NumPy-2: 연산, 브로드캐스트, 복사

목차

참조 자료

(3) 연산

(4) 브로드캐스트

(5) 복사

참조 자료

(3) 연산

1) 요소별 연산

import numpy as np
import numpy as np
A = np.array([1,2])
B = np.array([4,5])

C = A + B # array([5,7]), 요소별 덧셈
D = A * B # array([4,10]), 요소별 곱셈

E = A**2 # array([1,4]), 모든 요소에 자승
F = A + 2  # array([3,4]), 모든 요소에 덧셈
G = np.sin(A) # array([-0.7568,-0.5440]), 모든 요소에 sin 

H = A < 3   # array([True,True]), 모든 요소에 Boolean
I = A[0:2,] # array([1,2])

A *= B  # array([4,10]), A = A*B와 동일 

2) Universal 함수

NumPy는 수학 함수 np.sin(), np.cos() 등을 제공한다.

스칼라값을 입력으로 하는 math 패키지와 달리 배열에 대해 elementwise로(즉, 각 요소에 대해) 적용된다.


import numpy as np
x = np.arange(0.,np.pi,1.) # array([0.,1.,2.,3.])
y = np.sin(x) # array([0.,0.8414,0.9092,0.1411])
    

[Note]
▪ "Universal Function (ufunc)"은
   배열의 모든(Universal) 요소에 대해 개별적으로 적용 가능한 함수
   예: np.sin(), np.exp(), np.sqrt(), np.add(), np.maximum() 등

3) 행렬 연산


import numpy as np

A = np.arange(4).reshape(2,2)      
# array([[0,1],
#        [2,3]])
B = np.arange(5,5+4).reshape(2,2)  
# array([[5,6],
#        [7,8]])
# 2차원 배열
# 2행 2열 행렬
# 일반적인 행렬


X = np.arange(2)                   
# array([0,1])
# 1차원 배열(= 리스트), (2,)
# 길이가 2인 벡터 
# 1차원이므로 행/열 개념 없음

Y = np.arange(2).reshape(2,1)      
# array([[0],
#        [1]])
# 2차원 배열, (2,1)
# 2행 1열 열 벡터
# 세로로 쌓인 값들

Z = np.arange(2).reshape(1,2) 
# array([[0,1]])
# 2차원 배열, (1,2)
# 1행 2열 열 벡터
# 가로로 나열된 값들
    

[Note]
리스트(list)와 배열(array)의 차이


# A*B 
C1 = np.dot(A,B)     
C2 = np.matmul(A,B)  
C3 = A@B             
# 배열 * 배열 = 배열
# C1, C2, C3는 동일한 결과
# 차원: (2,2)*(2,2) = (2,2)
# array([[ 7, 8],
#        [31,36]] )

# A*X
X1 = np.dot(A,X)    
X2 = np.matmul(A,X) 
X3 = A@X            
# 배열 * 리스트 = 리스트
# X1, X2, X3는 동일한 결과
# 차원: (2,2)*(2,2) = (2,2)
# array([1,3])

# A*Y
Y1 = np.dot(A,Y)    
Y2 = np.matmul(A,Y) 
Y3 = A@Y            
# 배열 * 배열 = 배열
# Y1, Y2, Y3는 동일한 결과
# 차원: (2,2)*(2*1) = (2,1)
# array([[1],
#        [3]])

# A*Z 
Z1 = np.dot(A,Z)     
Z2 = np.matmul(A,Z)  
Z3 = A@Z            
# 배열 * 배열 = 배열
# Z1, Z2, Z3는 동일한 결과
# 차원: (2,2)*(1*2) = 행렬 차수 에러

# X*Y 
XY = X@Y 
# 배열 * 리스트 = 리스트
# 차원: (2,)*(2*1) = (1,)
# array([1])
    

# 전치행렬(transpose matrix)
A             
# array([[0, 1],
#        [2, 3]] )

A.T             
np.transpose(A) 
# array([[0, 2],
#        [1, 3]] )

# 선형대수함수
A = np.array([[1,2],[3,7]])
[D,V] = np.linalg.eig(A)   
# D = eigenvalues(고유치), V = eigen vectors(고유벡터)
    

#언팩킹(unpacking): *(asterisk)를 사용
import numpy as np
n = 8

# 잘못된 예: 배열 안에 배열이 들어감
a = np.array([0, 1, np.zeros(n)])  # dtype=object

# 올바른 예: 배열을 평평하게 결합
b = np.array([0, 1, *np.zeros(n)])  # dtype=float64
    

[Note]
NumPy의 dtype (데이터 타입)
파이썬의 언팩킹(Unpacking)

4) 정렬과 탐색


import numpy as np

X = np.array( [9.1,8.2,2.3] )

np.amin(X) # 최솟값, 2.3
np.amax(X) # 최댓값, 9.1

np.argmin(X) # 최솟값 위치, 2
np.argmax(X) # 최댓값 위치, 0

np.sort(X) # 오름차순 정렬, array( [2.3,8.2,9.1] )
np.argsort(X) # 오름차순 정렬 위치, array([2, 1, 0], dtype=int64)

np.argsort(X)[0]  # 가장 작은 값의 위치, 2
np.argsort(X)[-1] # 가장 큰 값의 위치, 0
np.argsort(X)[-2] # 두 번째로 큰 값의 위치, 1
    

(4) 브로드캐스트

형태가 다른 행렬의 연산


import numpy as np

A = np.arange(4.).reshape(2,2) 
# 2D array, (2,2) 
# array( [[0.,1.],
#         [2.,3.]] )

X = np.array( [1.,0.] )        
# 1D array, (2,) 
# array( [1.,0.] )

Y = X.reshape(1,2)             
# 2D array, (1,2)
# array( [[1.,0.]] )

Z = X.reshape(2,1)             
# 2D array, (2,1) 
# array( [[1.],
          [0.]] )

A + 1  
# (2,2) + scalar*I = (2,2) 
# array( [[1.,2.],
#         [3.,4.]] )

A + X  
# (2,2) + (2,) = (2,2) 
# array( [[1.,1.], 
#         [3.,3.]])

A + Y  
# (2,2) + (1,2) = (2,2) 
# array( [[1.,1.], 
#         [3.,3.]])

A + Z  
# (2,2) + (2,1) = (2,2)
# array( [[1.,2.], 
#         [2.,3.]])
https://blog.finxter.com/numpy-broadcasting-a-simple-tutorial/
https://gomguard.tistory.com/146

행렬 연산 함수 호출시 주의사항


import numpy as np

A = np.arange(4.).reshape(2,2) 
# 2D array, (2,2)
# array( [[0.,1.],
#         [2.,3.]] )

X = np.array( [1.,0.] )        
# 1D array, (2,) 

Y = X.reshape(1,2)             
# 2D array, (1,2)

Z = X.reshape(2,1)             
# 2D array, (2,1)


np.matmul(A,Y)   
# (2,2)*(1,2) = Dimension Error

np.matmul(A,Z)   
# (2,2)*(2,1) = (2,1)
# array( [[0.],
#         [2.]] )
np.matmul(A,X)   
# (2,2)*(2,) = (2,)
# array( [0.,2.] )
    

(5) 복사

1) 얕은 복사 vs 깊은 복사

▪ 깊은 복사(deep copy)
서로 값만 같을 뿐 본질적으로 서로 다르기 때문에 한 변수가 수정될 시 다른 변수가 수정되지 않는다.

▪ 얕은 복사(shallow copy)
서로 다른 변수명이지만 본질적으로 서로 같은 대상을 의미하므로 하나의 변수 역시 수정이 된다.


import numpy as np

A = [1,2,3]     # [1, 2, 3]
X = np.array(A) # array( [1, 2, 3] )

Z = X.copy()    # deep copy
Z is X          # False

Y = X           # shallow copy
Y[0] = 10       # 

X               # array( [10, 2, 3] )
Y               # array( [10, 2, 3] )

Y is X          # True
X is Y          # True
    

2) 부분행렬 복사

부분행렬은 얕은 복사이다.


import numpy as np
X = np.array( [1,2,3] )

Y = X[0:2]      # shallow copy
Y[0] = 10       #

Y               # array( [10,2] )
X               # array( [10,2,3] )