Futureskill - Numpy
FutureSkill(https://www.futureskill.io) 에서 공부한 내용.
Numpy 는 NUMerical PYthon 의 약자로서, 행렬 등을 다루기 위한 배열(Array)같은 형태의 자료형을 자유자재로 잘 다룰 수 있도록 도와주는 라이브러리 입니다.
데이터 분석을 하기 위해서는 데이터 연산을 해야 합니다. 이 때 연산을 효율적으로 하기 위해서 데이터는 모두 벡터로 표현됩니다. 이런 벡터 연산에 최적화된 Numpy를 통해서 데이터를 빠르게 처리할 수 있습니다.
Numpy Creation
import numpy as np
L = [1, 2, 3, 4, 5]
arr = np.array(L) # Python list L -> Numpy array
arr2 = np.zeros(10) # 원소가 모두 0인 array
L = [1.1, 2.2, 3.3, -4.4, 5.5]
np.array(L, dtype = np.float32) # 원소가 모두 32bytes 사이즈의 float(float32) 타입
arr = np.array(range(1,1001))
arr = np.arange(1,1001) # 1부터 1000까지의 정수 array
arr = np.arange(0, 1.1, 0.1) # 0부터 1까지 0.1의 간격으로 놓인 실수들을 가지는 array
numpy array가 가질 수 있는 자료형은 다음과 같습니다.
i - integer
b - boolean
u - unsigned integer
f - float
c - complex float
m - timedelta
M - datetime
O - object
S - string
U - unicode string
V - fixed chunk of memory for other type ( void )
Python list는 다른 종류의 데이터 타입을 저장할 수 있는 반면 numpy array 내에서는 모든 원소가 동일한 data type을 가져야 합니다. 이는 연산을 빠르게 처리하기 위한 Numpy의 특성 때문입니다.
Numpy가 빠른 연산을 할 수 있는 이유는 Numpy는 연산을 여러개의 부분적 연산(subtasks)들로 나눠서 병렬적으로 처리가 가능하고, C로 구현되어 있기 때문에 Python list보다 빠르게 연산을 처리할 수 있기 때문입니다.
Numpy indexing
arr[1] # arr의 2번째 원소.
arr2 = np.array([1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2[1][2] # arr의 2번째 행, 3번째 열의 원소
arr2[0] # 첫번째 행 fancy indexing
arr2[:,1] # 두번째 열 facny indexing
Numpy masking
arr = np.array([30,4,550,9,112,10000,15,21])
mask = (30 < arr[:])
#mask = 30 < arr
arr[mask] # 주어진 데이터에 30보다 큰 원소들만 뽑아내는 masking
# arr[arr>30] 과 같이 간단히 쓸 수 있다.
Numpy functions
arr = np.array([30,4,550,9,112,10000,15,21])
arr.min() # 최소값
arr.max() # 최대값
arr.argmin() # 최소값의 위치
arr.argmax() # 최대값의 위치
arr1 = np.array([1, 2, 3, 4, 5])
arr2 = np.array([6, 7, 8, 9])
np.concatenate((arr1,arr2), axis = 0) # [arr1, arr2] 꼴로 합치는 코드.
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr3 = np.array([7, 8, 9])
np.vstack((arr1, arr2, arr3)) # 세로로 쌓는 코드
np.concatenate([arr1, arr2, arr3]).reshape(3,3) # 세로로 쌓는 코드
# vstack 이나 concatenate 둘다
# (arr1, arr2, arr3) or [arr1, arr2, arr3] 모두 사용가능하다.
Numpy arithmetic operation
v1 = np.array([1, 2, 3])
v2 = np.array([4, -1, 2])
v1 + v2
np.add(v1,v2) # v1 + v2 벡터합
np.dot(v1, v2) # (v1, v2) 내적
innerPd = np.vdot(v1, v2)
innerPd2 = np.inner(v1, v2) # 1d에서만 가능함.
MatMul = np.matmul(v1, v2) # 1d에서는 내적처럼 바꿔준다.
MatMul2 = np.matmul(v1, v2.T) # 위의 Matmul과 같은 값
내적(inner product)와 관련된 코드는 다음과 같은 경우들로 조금 더 깊이 살펴보겠습니다.
사실 inner product 와 dot product는 차이가 있긴 합니다. 이 부분은 이곳에 설명이 아주 잘 나와있습니다.
https://math.stackexchange.com/questions/476738/difference-between-dot-product-and-inner-product
요약해서 dot product는 $\R^{n}$ 에서의 inner product 정도로만 알고 있어도 될 것 같습니다.
np.dot(): numpy.dot(vector_a, vector_b, out = None) → 벡터 a, b의 내적(dot product)를 을 계산합니다. 2D array를 다룰 수 있지만 행렬(matrix)로 생각하고 행렬곱(matrix multiplication)을 계산합니다.np.vdot(): numpy.vdot(vector_a, vector_b) → 벡터 a, b의 내적(dot product)를 계산합니다.
vector_a = np.array([2 + 3j])
vector_b = np.array([4 + 5j])
dot_product = np.dot(vector_a, vector_b)
vdot_product = np.vdot(vector_a, vector_b)
matmul = np.matmul(vector_a, vector_b)
matmul2 = np.matmul(vector_a, vector_b.T)
print(dot_product)
print(vdot_product)
print(matmul)
print(matmul2)
→ np.dot와 np.inner의 계산값은 -7+22j 가 나오게 됩니다. 이는 별도로 conjugate 처리를 하지 않고 계산을 했기 때문입니다.
$(2+3j)\cdot(4+5j) = -7+22j$
→ np.vdot의 경우에는 (2-3j) 에 대해 conjugate 처리를 해주기 때문에 23-2j 가 나오게 됩니다.
(1,2j) , (2,3j) 와 같은 2차원 꼴로 예를 들어서 보면 더 쉽게 차이를 볼 수 있습니다. 실제 dot product와 더 가까운 것은 np.vdot이라고 할 수 있습니다. 하지만 np.vdot과는 (a,b) 순서가 다르다고 보시면 됩니다.
실제 $\vec{a} \cdot \vec{b} =$ np.vdot(b,a) 라고 생각하시면 됩니다.
np.matmul
np.matmul의 경우 1차원 벡터를 받으면 알아서 뒤의 벡터를 전치해준 뒤 일반적인 내적을 계산해줍니다. 따라서 np.matmul(v1, v2) 와 np.matmul(v1, v2.T) 의 값이 같게 나옵니다. 복소수의 경우에서는 np.dot, np.inner 와 같은 값을 출력합니다.
np.matmul의 경우 vector_a = 2+3j로 선언 후 사용하려고 하면 사용이 되지 않습니다. 다른 함수들과 달리 scalar를 입력으로 받을 수 없기 때문입니다. 따라서 vector_a = np.array([2 + 3j])로 선언 후 사용해야 합니다.
1차원에서는 위와 같이 조금 복잡하게 계산이 이루어집니다. 2차원에서의 np.dot과 np.vdot의 차이는 다음과 같습니다.
vector_a = np.array([[1, 4], [5, 6]])
vector_b = np.array([[2, 4], [5, 2]])
dot_product = np.dot(vector_a, vector_b)
matmul = np.matmul(vector_a, vector_b)
vdot_product = np.vdot(vector_a, vector_b)
값을 확인해보면 처음 둘은 그냥 행렬곱(matrix multiplication) 을 계산해주고, vdot의 경우 두 벡터를 flatten 한 뒤에 내적을 구합니다 따라서 [1, 4, 5, 6] 과 [2, 4, 5, 2] 의 내적과 같은 계산이 됩니다.
(계속 numpy doc을 보며 공부하고 내용을 보완, 수정할 것!)
Numpy functions
arr1 = np.array([1, 2, 3])
arr1+=3 # broadcasting
img = np.random.random((5, 5))
ravel = img.ravel()
reshape = img.reshape(-1)
flatten = img.flatten()
# 행의 갯수를 모를 때 flatten
N = int(input("N? : "))
data = np.random.random((N, 6))
flatten_data = data.flatten()
ravel_data = data.ravel()
reshape_data = data.reshape(-1)
ravel, reshape는 복사가 아니므로 img의 원소를 바꾸면 같이 바뀝니다. 반면에 flatten은 복사이므로 바뀌지 않습니다.
# math functions
x = 10
np.sin(x)
np.cos(x)
np.tan(x)
np.abs(x)
np.sqrt(x)
np.square(x)
# python 출력 소수점 4자리까지
print("abs(10) : ", '%.4f'%np.abs(x))
print("sqrt(10) : ", '%.4f'%np.sqrt(x))
print("square(10) : ", '%.4f'%np.square(x))
x = 3.14
print("exp({}) : {}".format(x, np.exp(x)))
print("log({}) : {}".format(x, np.log10(x)))
# log(x) = ln(x) 이므로 헷갈리지 말자.
Sorting
arr = np.array([1, -5, 3.4, 55.41, -1009, 2245, -575, -1935.124])
arr2 = np.sort(arr)
arr3 = np.argsort(arr) # arr을 정렬하는 인덱스의 어레이 반환
arr4 = arr[arr3]
arr5 = np.lexsort((arr, arr3))
arr6 = np.argsort(arr3)
print('sorted arr', arr2)
print('sorted arr', arr4)
print('argsort', arr3)
print('answer', arr5)
print('answer', arr6)
####### np.lexsort example #######
a = np.array([5, 5, 4])
b = np.array([3, 1, 1])
print(np.lexsort(a,b))
###### Matrix sorting ######
mat = np.array([[1, -1, 3],
[4, 3, -6],
[0, 11, 9]])
mat2 = np.sort(mat) # 행별로 오름차순 정렬
mat3 = np.sort(mat, axis = 0)[::-1] # 열별로 내림차순 정렬
print(mat2)
print(mat3)
→ np.lexsort가 어떻게 구현되는 지 보기위해 아래 예제를 들어보겠습니다.
- 먼저
b를 기준으로 sort 합니다. → [1, 1, 3] → idx = [2, 1, 0] or [1, 2, 0] 1이 2개 이므로 b[1] = b[2] 가 같습니다. - 이제
a를 기준으로 sort 합니다. → 이 때 a[1] > a[2] 이므로 idx = [2, 1, 0] 으로 정렬합니다.
Leave a comment