[Python] 넘파이

7 minute read

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번의 참조가 필요

    image-20220119210842379

# 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)

image-20220119212046981

  • 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]]
'''

image-20220119214020429


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]]
'''

image-20220119215349932

  • 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]]
'''

image-20220119220423961

  • 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이 다른 배열 간 연산을 지원

image-20220119221237272

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 간 연산도 지원

image-20220119221509193

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