[Python으로 데이터 다루기 I - numpy]2강:Numpy와 선형대수
영벡터(영행렬)
원소가 모두 0인 벡터(행렬)
np.zeros(dim)
을 통해 생성. dim은 값, 혹은 튜플( , )이어야 한다.
np.zeros(2)
# array([0., 0.])
np.zeros((3, 3))
# array([[0., 0., 0.],
# [0., 0., 0.],
# [0., 0., 0.]])
일벡터(일행렬)
원소가 모두 1인 벡터(행렬)
np.ones(dim)
을 통해 생성. dim은 값 혹은 튜플( , )
np.ones(2)
# array([1., 1.])
np.ones((3, 3))
# array([[1., 1., 1.],
# [1., 1., 1.],
# [1., 1., 1.]])
대각행렬(diagonal matrix)
Main diagonal을 제외한 성분이 0인 행렬
np.diag(main_diagonal)
을 통해 생성
np.diag((2, 4))
# array([[2, 0],
# [0, 4]])
np.diag((1, 3, 5))
# array([[1, 0, 0],
# [0, 3, 0],
# [0, 0, 5]])
항등행렬(identity matrix)
Main diagonal이 1인 대각행렬
np.eye(n, dtype=int, uint, float, complex, ...)
를 사용. dtype으로 데이터타입을 같이 지정해줄 수도 있다.지정하지 않아도 상관없다.
np.eye(2, dtype=int)
# array([[1, 0],
# [0, 1]])
np.eye(3, dtype=float)
# array([[1., 0., 0.],
# [0., 1., 0.],
# [0., 0., 1.]])
행렬곱(dot product)
행렬간의 곱연산
np.dot()
or @
사용
.dot()은 메서드처럼 사용하고, @는 연산자처럼 사용한다.
mat_1 = np.array([[1, 4], [2, 3]])
mat_2 = np.array([[7, 9], [0, 6]])
mat_1.dot(mat_2)
# array([[ 7, 33],
# [14, 36]])
mat_1 @ mat_2
# array([[ 7, 33],
# [14, 36]])
트레이스(trace)
Main diagonal의 합 -> $\sum a_{ii}$
np.trace()
를 사용
arr = np.array([[1,2,3], [4,5,6], [7,8,9]])
arr
# array([[1, 2, 3],
# [4, 5, 6],
# [7, 8, 9]])
arr.trace()
# 15
np.eye(2, dtype=int)
# array([[1, 0],
# [0, 1]])
np.eye(2, dtype=int).trace()
# 2
행렬식(determinant)
행렬을 대표하는 값들 중 하나 -> 선형변환
선형변환 과정에서 벡터의 scaling 척도
np.linalg.det()
으로 계산
arr_2 = np.array([[2,3], [1,6]])
arr_2
# array([[2, 3],
# [1, 6]])
np.linalg.det(arr_2)
# 9.000000000000002
arr_2 = np.array([[1,4,7], [2,5,8], [3,6,9]])
arr_3
# array([[1, 4, 7],
# [2, 5, 8],
# [3, 6, 9]])
np.linalg.det(arr_3)
# 0.0 -> det값이 0이면 full rank가 아님, 즉 선형변환 과정에서 차원의 손실이 일어남을 알 수 있음. 즉 column(열)들간에 선형 종속관계가 존재한다.
선형대수에서 계산하는 것도 중요하지만, 계산의 결과와 각각의 값들이 어떤 의미를 가지는지 아는 것이 중요하다.
역행렬(Inverse Matrix)
행렬 A에 대해 AB = BA = I를 만족하는 행렬 B = A^-1
np.linalg.inv()
으로 계산
mat = np.array([[1,4], [2,3]])
mat
# array([[1, 4],
# [2, 3]])
mat_inv = np.linalg.inv(mat)
mat_inv
# array([[-0.6, 0.8],
# [ 0.4, -0.2]])
mat@mat_inv
# array([[ 1.00000000e+00, 0.00000000e+00],
# [-1.11022302e-16, 1.00000000e+00]])
# 행렬 * 역행렬 = I(항등행렬)임을 알 수 있다.
고유값과 고유벡터(eigenvalue and eigenvector)
정방행렬 A에 대해 Ax = (\lambda)x를 만족하는 상수 (\lambda)와 이에 대응하는 벡터
$Ax = \lambda x$가 있을 때, 이를 만족하는 람다가 고유값, x가 고유벡터이다.
선형변환 관점에서 보면, 선형 변환을 진행하는 과정에서 span되는 비율이 고유값, span되는 영역이 고유벡터이다.
np.linalg.eig()
으로 계산
mat = np.array([[2,0,-2], [1, 1, -2], [0,0,1]])
mat
# array([[ 2, 0, -2],
# [ 1, 1, -2],
# [ 0, 0, 1]])
np.linalg.eig(mat)
# 고유값과 고유벡터가 차례대로 출력된다.
# (array([1., 2., 1.]),
# array([[0. , 0.70710678, 0.89442719],
# [1. , 0.70710678, 0. ],
# [0. , 0. , 0.4472136 ]]))
# column을 기준으로 봐야 하기 때문에, 고유벡터는 각각
# 1에 대응되는 고유벡터 [0., 1., 0]
# 2에 대응되는 고유벡터 [0.70710678, 0.70710678, 0.]
# 1에 대응되는 고유벡터 [0.89442719, 0., 0.4472136]
Validation
eig_val, eig_vec = np.linalg.eig(mat)
eig_val
# array([1., 2., 1.])
eig_vec
# array([[0. , 0.70710678, 0.89442719],
# [1. , 0.70710678, 0. ],
# [0. , 0. , 0.4472136 ]])
mat @ eig_vec[:, 0] # Ax를 계산
# column을 기준으로 가져와야 한다. eig_vec을 0번째 열 것만 가져와서 한다.
# 행으로는 전부, 열로는 0번째.
# array([0., 1., 0.])
eig_val[0] * eig_vec[:, 0] # (lambda)x를 계산
# eig_val은 1by1 matrix, 벡터가 아닌 값이다. 따라서 @연산을 적용하면 충분한 dimension이 없다는 오류가 난다. 행렬곱이 아닌 그냥 곱하기를 써줘야 한다.
# 스칼라 * 벡터 의 형태임.
# array([0., 1., 0.])
검증을 통해 $Ax = \lambda x$가 올바름을 알 수 있다.
1. 어떤 벡터가 주어졌을 때 L2 norm을 구하는 함수 get_L2_norm()
을 작성하세요
- 매개변수 : 1차원 벡터 (
np.array
) - 반환값 : 인자로 주어진 벡터의 L2 Norm값 (
number
)import numpy as np mat = ([1,2,3,4]) def get_L2_norm(mat): return np.linalg.norm(mat, 2) get_L2_norm(mat)
2. 어떤 행렬이 singular matrix인지 확인하는 함수 is_singular()
를 작성하세요
- 매개변수 : 2차원 벡터(
np.array
) - 반환값 : 인자로 주어진 벡터가 singular하면 True, non-singular하면 False를 반환
# det값이 0이면 singular matrix(특이 행렬)이다! mat = ([1,2],[3,4]) def is_singular(mat): answer = np.linalg.det(mat) if answer == 0: return True else: return False is_singular(mat)