[Python] 넘파이
Numpy
어떻게 벡터와 행렬을 코드로 표현할 것인가? ➡ numpy
- Numerical Python
- 파이썬의 고성능 과학 계산용 패키지
- Matrix와 Vector 같은 Array 연산의 사실 상 표준
- 일반 List에 비해 빠르고, 메모리 효율적
- 반복문 없이 데이터 배열에 대한 처리를 지원
- 선형대수와 관련된 다양한 기능을 제공
- C, C++, 포트란 등의 언어와 통합 가능
ndarray
- 넘파이는 np.array 함수를 사용하여 배열을 생성(ndarray)
- 넘파이는 하나의 데이터 type만 배열에 넣을 수 있음(Dynamic typing not supported)
- C의 Array를 사용하여 배열을 생성
test_array = np.array([1,4,5,8],float) # 배열 생성 시 데이터 타입을 지정(int8, float16, float32 등)
print(test_array)
print(type(test_array[3]))
'''
[1. 4. 5. 8.]
<class 'numpy.float64'>
'''
-
넘파이는 배열 생성 시 새로운(독립적인) 메모리 공간에 따로 수들을 순서대로 저장
- 메모리 효율적
- 일반 List는 값이 저장되어 있는 메모리 주소가 static하게 할당되어 있고, 그 메모리 주소를 저장하는 방식
- 2번의 참조가 필요
# List
a = [1,2,3,4,5]
b = [5,4,3,2,1]
print(a[0] is b[-1])
# Numpy array
a = np.array(a)
b = np.array(b)
print(a[0] is b[-1])
'''
True
False
'''
ndarray.shape
: numpy array의 dimension 구성을 반환(형상)
tensor = [[[1,2,3,8],[1,2,3,8],[1,2,3,8]],
[[1,2,3,8],[1,2,3,8],[1,2,3,8]],
[[1,2,3,8],[1,2,3,8],[1,2,3,8]],
[[1,2,3,8],[1,2,3,8],[1,2,3,8]]]
np.array(tensor,int).shape
# (4, 3, 4)
ndarray.dtype
: numpy array의 데이터 type을 반환
test_array = np.array([1, 4, 5, "8"], float) # String Type의 데이터를 입력해도
print(test_array)
print(type(test_array[3])) # Float Type으로 자동 형변환을 실시
print(test_array.dtype) # Array(배열) 전체의 데이터 Type을 반환함
print(test_array.shape) # Array(배열) 의 shape을 반환함
'''
[1. 4. 5. 8.]
<class 'numpy.float64'>
float64
(4,)
'''
ndarray.ndim
: 차원의 개수ndarray.size
: data의 총 개수
tensor = [[[1,2,3,8],[1,2,3,8],[1,2,3,8]],
[[1,2,3,8],[1,2,3,8],[1,2,3,8]],
[[1,2,3,8],[1,2,3,8],[1,2,3,8]],
[[1,2,3,8],[1,2,3,8],[1,2,3,8]]]
tensor = np.array(tensor,int)
print(tensor.ndim, tensor.size)
# 3 48
ndarray.nbytes
: ndarray object의 메모리 크기를 반환
a = np.array([[1,2,3],[4.5,"5","6"]], dtype=np.float32) # 32bits = 4bytes -> 6*4 bytes
b = np.array([[1,2,3],[4.5,"5","6"]], dtype=np.int8) # 8bits = 1bytes -> 6*1bytes
c = np.array([[1,2,3],[4.5,"5","6"]], dtype=np.float64) # 64bits = 8bytes -> 6*8bytes
print(f"a: {a.nbytes} bytes")
print(f"b: {b.nbytes} bytes")
print(f"c: {c.nbytes} bytes")
'''
a: 24 bytes
b: 6 bytes
c: 48 bytes
'''
Handling shape
ndarray.reshape(*args)
: Array의 shape의 크기를 변경. element의 수는 동일.
test_matrix = np.array([[1,2,3,4],[1,2,5,8]],int)
print(test_matrix, test_matrix.shape)
test_matrix = test_matrix.reshape(8,)
print(test_matrix, test_matrix.shape)
'''
[[1 2 3 4]
[1 2 5 8]] (2, 4)
[1 2 3 4 1 2 5 8] (8,)
'''
print(test_matrix.reshape(-1,2),test_matrix.reshape(-1,2).shape)
# print(test_matrix.reshape(4,2),test_matrix.reshape(4,2).shape)
'''
[[1 2]
[3 4]
[1 2]
[5 8]] (4, 2)
'''
ndarray.flatten()
: 다차원 array를 1차원 array로 변환
test_matrix = np.array([[1,2,3,4],[1,2,5,8]],int)
print(test_matrix, test_matrix.shape)
print(test_matrix.flatten(),test_matrix.flatten().shape)
'''
[[1 2 3 4]
[1 2 5 8]] (2, 4)
[1 2 3 4 1 2 5 8] (8,)
'''
Indexing & Slicing
- List와 달리 이차원 배열에서 [0,0] 표기법을 지원
- 앞은 row, 뒤는 column
test_example = np.array([[1,2,3],[4.5,5,6]],int)
print(test_example[0][0],test_example[0,0])
test_example[0,0] = 12
print(test_example)
'''
1 1
[[12 2 3]
[ 4 5 6]]
'''
- List와 달리 행과 열 부분을 나눠서 slicing이 가능함
- matrix의 부분 집합을 추출할 때 유용
a = np.array([[1,2,3,4,5],[6,7,8,9,10]],int)
print(a)
print(a[:,2:])
print(a[1,1:3])
print(a[1:3])
'''
[[ 1 2 3 4 5]
[ 6 7 8 9 10]]
[[ 3 4 5]
[ 8 9 10]]
[7 8]
[[ 6 7 8 9 10]]
'''
# step을 지정할 수 있음
a = np.array([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]],int)
print(a)
print(a[:,::2])
print(a[::2,::3])
'''
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]]
[[ 1 3 5]
[ 6 8 10]
[11 13 15]]
[[ 1 4]
[11 14]]
'''
Creation function
np.arange(start, end, step)
: array의 범위를 지정하여, 값의 list를 생성하는 명령어
print(np.arange(10))
print(np.arange(0,5,0.5))
print(np.arange(10).reshape(2,5)) # reshape와 함께 자주 사용
'''
[0 1 2 3 4 5 6 7 8 9]
[0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5]
[[0 1 2 3 4]
[5 6 7 8 9]]
'''
np.zeros(shape,dtype), np.ones(shape,dtype), np.empty(shape,dtype)
- 각각 0으로, 1로, 햘당되지 않은 메모리로 채워진 행렬을 반환
np.zeros_like(ndarray,dtype), np.ones_like(ndarray,dtype), np.empty(ndarray,dtype)
- 각각 인자로 전달받은 ndarray와 같은 형상인 0, 1, 할당되지 않은 메모리로 채워진 행렬을 반환
np.identity(n, dtype)
: 단위 행렬 생성np.eye(N, M, k, dtype)
: 대각선이 1인 행렬, k값의 시작 index 변경 가능
print(np.eye(3))
print(np.eye(3,5,k=2))
'''
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
[[0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0.]
[0. 0. 0. 0. 1.]]
'''
np.diag(ndarray, k)
: 대각 행렬의 값을 추출, k값의 시작 인덱스 변경 가능
matrix = np.arange(9).reshape(3,3)
print(matrix)
print(np.diag(matrix))
print(np.diag(matrix, k=1))
'''
[[0 1 2]
[3 4 5]
[6 7 8]]
[0 4 8]
[1 5]
'''
np.random.uniform(from, to, n), np.random.normal(from, to, n)
: 데이터 분포에 따른 sampling으로 array를 생성
print(np.random.uniform(0,1,10).reshape(2,5))
print(np.random.normal(0,1,10).reshape(2,5))
'''
[[0.35006331 0.24951611 0.49920446 0.61727984 0.82651679]
[0.04549254 0.17509728 0.52278906 0.12011707 0.7854978 ]]
[[-0.07886817 1.05551477 -0.09244039 0.32329219 -1.3036152 ]
[-0.22816616 0.25514229 1.3512013 1.59589969 0.0897062 ]]
'''
Operation functions
ndarray.sum(axis)
: axis를 기준으로 ndarray의 element 간의 합을 구함
third_order_tensor = np.array([[[1,2,3,4],[5,6,7,8],[9,10,11,12]],
[[1,2,3,4],[5,6,7,8],[9,10,11,12]],
[[1,2,3,4],[5,6,7,8],[9,10,11,12]]])
print(third_order_tensor.sum(axis=2))
print(third_order_tensor.sum(axis=1))
print(third_order_tensor.sum(axis=0))
'''
[[10 26 42]
[10 26 42]
[10 26 42]]
[[15 18 21 24]
[15 18 21 24]
[15 18 21 24]]
[[ 3 6 9 12]
[15 18 21 24]
[27 30 33 36]]
'''
ndarray.mean(axis), ndarray.std(axis)
: ndarray의 element 간 평균 또는 표준 편차- 그 외에도 다양한 수학 연산자를 제공
np.exp(ndarray), np.sqrt(ndarray), np.sin(ndarray), np.sinh(ndarray), np.arcsin(ndarray)...
Concatenate
np.vstack((ndarray1,ndarray2)), hstack((ndarray1, ndarray2))
a = np.array([1,2,3])
b = np.array([2,3,4])
print(np.vstack((a,b)))
a = np.array([[1],[2],[3]])
b = np.array([[2],[3],[4]])
print(np.hstack((a,b)))
'''
[[1 2 3]
[2 3 4]]
[[1 2]
[2 3]
[3 4]]
'''
np.concatenate((ndarray1, ndarray2), axis)
a = np.array([1,2,3])
b = np.array([2,3,4])
print(np.concatenate((a,b), axis=0))
a = np.array([[1,2],[3,4]])
b = np.array([[5],[6]])
print(np.concatenate((a,b), axis=1))
'''
[1 2 3 2 3 4]
[[1 2 5]
[3 4 6]]
'''
Array operations
- numpy는 array 간의 기본적인 사칙 연산을 지원함
test_a = np.array([[1,2,3],[4,5,6]], float)
print(test_a+test_a)
print(test_a-test_a)
print(test_a*test_a) # element wise multiplication(Hadamard multiplication)
print(test_a.dot(test_a.T)) # matrix multiplication
print(test_a.transpose(), test_a.T) # transpose
'''
[[ 2. 4. 6.]
[ 8. 10. 12.]]
[[0. 0. 0.]
[0. 0. 0.]]
[[ 1. 4. 9.]
[16. 25. 36.]]
[[14. 32.]
[32. 77.]]
[[1. 4.]
[2. 5.]
[3. 6.]]
[[1. 4.]
[2. 5.]
[3. 6.]]
'''
Broadcasting
- Shape이 다른 배열 간 연산을 지원
test_matrix = np.array([[1,2,3],[4,5,6]],float)
scalar = 3
print(test_matrix + scalar)
print(test_matrix - scalar)
print(test_matrix * scalar)
print(test_matrix / scalar)
print(test_matrix // scalar)
print(test_matrix ** scalar)
'''
[[4. 5. 6.]
[7. 8. 9.]]
[[-2. -1. 0.]
[ 1. 2. 3.]]
[[ 3. 6. 9.]
[12. 15. 18.]]
[[0.33333333 0.66666667 1. ]
[1.33333333 1.66666667 2. ]]
[[0. 0. 1.]
[1. 1. 2.]]
[[ 1. 8. 27.]
[ 64. 125. 216.]]
'''
- Scalar 외에도 vector - matrix 간 연산도 지원
Numpy performance
- 일반적으로 속도는 for loop < list comprehension < numpy 순
- 100,000,000 번의 loop이 돌 때, 약 4배 이상의 성능 차이를 보임
- Numpy는 C로 구현되어 있어, 성능을 확보하는 대신 파이썬의 가장 큰 특징인 dynamic typing을 포기함
- 대용량 계산에서는 가장 흔히 사용됨
- Concatenate처럼 계산이 아닌, 할당에서는 연산 속도의 이점이 없음
Comparisons
np.any(condition), np.all(condition)
: Array의 데이터 전부 혹인 일부가 조건에 만족 여부 반환
a = np.arange(10)
print(a)
print(a>5)
print(np.any(a>5))
print(np.all(a>5))
'''
[0 1 2 3 4 5 6 7 8 9]
[False False False False False False True True True True]
True
False
'''
np.logical_and(bool_ndarray1, bool_ndarray2), np.logical_or(bool_ndarray1, bool_ndarray2), np.logical_not(bool_ndarray)
a = np.array([1,3,0], float)
print(a > 0, a < 3)
print(np.logical_and(a > 0, a < 3))
print(np.logical_or(a > 0, a < 3))
print(np.logical_not(a > 0))
'''
[ True True False] [ True False True]
[ True False False]
[ True True True]
[False False True]
'''
np.where(condition, TRUE, FALSE)
- 인자로 condition만 전달 시 True인 값의 인덱스 리스트를 반환
- 인자로 condition, TRUE, FALSE 모두 전달 시 True인 값은 TRUE로, False인 값은 FALSE로 치환된 리스트 반환
a = np.array([1,3,0], float)
print(a > 0)
print(np.where(a > 0))
print(np.where(a > 0,3,2))
'''
[ True True False]
(array([0, 1], dtype=int64),)
[3 3 2]
'''
np.isnan(ndarray), np.isfinite(ndarray)
a = np.array([1,np.NaN,np.Inf], float)
print(np.isnan(a))
print(np.isfinite(a))
'''
[False True False]
[ True False False]
'''
argmax, argmin, argsort
np.argmax(ndarray, axis), np.argmin(ndarray, axis)
: array 내 최댓값 또는 최솟값의 index를 반환np.argsort(ndarray, axis)
: 오름차순으로 정렬했을 때 기존 array 원소의 index를 반환
a = np.array([[1,2,4,7], [9,88,6,45], [9,76,3,4]])
print(a)
print(np.argmax(a,axis=1))
print(np.argmin(a, axis=0))
'''
[[ 1 2 4 7]
[ 9 88 6 45]
[ 9 76 3 4]]
[3 1 1]
[0 0 2 2]
[[0 0 2 2]
[1 2 0 0]
[2 1 1 1]]
'''
Boolean & Fancy indexing
Boolean index
- 특정 조건에 따른 값을 매열 형태로 추출
- Comparison operation 함수들도 모두 사용 가능
test_array = np.array([1,4,0,2,3,8,9,7], float)
condition = test_array > 3
print(condition)
print(test_array[condition]) # 조건이 True인 index의 element만 추출
'''
[False True False False False True True True]
[4. 8. 9. 7.]
'''
Fancy index
- numpy는 array를 index value로 사용해서 값 추출
ndarray.take(ndarray)
: fancy index(bracket index)과 같은 효과
a = np.array([2,4,6,8], float)
b = np.array([0,0,1,3,2,1], int)
print(a[b]) # bracket index, b 배열의 값을 index로 하여 a의 값들을 추출
print(a.take(b)) # take 함수: bracket index와 같은 효과
'''
[2. 2. 4. 8. 6. 4.]
[2. 2. 4. 8. 6. 4.]
'''
- matrix 형태의 데이터도 가능
a = np.array([[1,4],[9,16]], float)
b = np.array([0,0,1,1,0], int)
c = np.array([0,1,1,1,1], int)
print(a[b,c])
# [ 1. 4. 16. 16. 4.]
Numpy data i/o
- text type의 데이터를 읽고, 저장하는 기능
# 파일 저장
np.save("npy_test_object", arr=a_int_3)
# 파일 호출
a_test = np.load(file="npy_test_object.npy")
Leave a comment