Upstage AI Lab 3기
Python EDA - Numpy
developzest
2024. 4. 30. 16:05
Numpy¶
- https://www.numpy.org
- Numerical computing with Python. 수치연산 및 벡터 연산에 최적화된 라이브러리
- 최적화된 C code로 구현되어 있어 좋은 성능을 보이며 파이썬과 다르게 수치 연산의 안정성이 보장됨(numerical stable)
1. Numpy Array and Operation¶
- numpy의 기본적인 사용법에 대해 학습
- numpy에서 numpy.array를 만드는 여러가지 방법과 지원하는 연산자에 대해 학습
1.1. Numpy Array creation¶
In [ ]:
# numpy 라이브러리 import
import numpy as np
1.1.1 파이썬 리스트/튜플을 np.array로 변환¶
In [ ]:
# 파이썬 리스트 선언
data = [1, 2, 3, 4]
# numpy array를 만드는 방식의 대부분은 파이썬 리스트를 np.array로 변환하는 방식
np.array(data)
Out[ ]:
array([1, 2, 3, 4])
In [ ]:
# 파이썬 2차원 리스트(행렬) 선언
data2 = [[1, 2],
[3, 4]]
# 2차원 리스트를 np.array로 변환.
arr2 = np.array(data2)
print(arr2)
print(arr2.shape)
[[1 2]
[3 4]]
(2, 2)
1.1.2 numpy의 함수를 통한 numpy array 생성¶
In [ ]:
# [start, stop), step
# 0부터 9까지 숫자를 자동으로 생성한 array
np.arange(0, 10)
Out[ ]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
shape¶
- 정의
- 가장 바깥 괄호부터 원소의 개수를 순차적으로 기록한 것
- 종류
- 1차원 : vector
- 2차원 : matrix
- 3차원 이상 : tensor
- 의미
- 안쪽부터 해석 하면 이해하기 쉬움
- ex)
np.zeros(shape=(32, 3, 224, 224))
- 224 x 224 크기를 갖는 3개의 channel을 갖는 이미지가 32개 있음
In [ ]:
# 원소가 '0'인 numpy array 생성
np.zeros(shape=(10,))
Out[ ]:
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
In [ ]:
# (2,3,4) 크기의 배열을 생성하여 1로 채움
np.ones(shape=(2,3,4))
Out[ ]:
array([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
In [ ]:
# [start, stop]에서 num개의 숫자를 균등한 구간으로 자른 원소로 생성
np.linspace(0, 1, 100)
Out[ ]:
array([0. , 0.01010101, 0.02020202, 0.03030303, 0.04040404,
0.05050505, 0.06060606, 0.07070707, 0.08080808, 0.09090909,
0.1010101 , 0.11111111, 0.12121212, 0.13131313, 0.14141414,
0.15151515, 0.16161616, 0.17171717, 0.18181818, 0.19191919,
0.2020202 , 0.21212121, 0.22222222, 0.23232323, 0.24242424,
0.25252525, 0.26262626, 0.27272727, 0.28282828, 0.29292929,
0.3030303 , 0.31313131, 0.32323232, 0.33333333, 0.34343434,
0.35353535, 0.36363636, 0.37373737, 0.38383838, 0.39393939,
0.4040404 , 0.41414141, 0.42424242, 0.43434343, 0.44444444,
0.45454545, 0.46464646, 0.47474747, 0.48484848, 0.49494949,
0.50505051, 0.51515152, 0.52525253, 0.53535354, 0.54545455,
0.55555556, 0.56565657, 0.57575758, 0.58585859, 0.5959596 ,
0.60606061, 0.61616162, 0.62626263, 0.63636364, 0.64646465,
0.65656566, 0.66666667, 0.67676768, 0.68686869, 0.6969697 ,
0.70707071, 0.71717172, 0.72727273, 0.73737374, 0.74747475,
0.75757576, 0.76767677, 0.77777778, 0.78787879, 0.7979798 ,
0.80808081, 0.81818182, 0.82828283, 0.83838384, 0.84848485,
0.85858586, 0.86868687, 0.87878788, 0.88888889, 0.8989899 ,
0.90909091, 0.91919192, 0.92929293, 0.93939394, 0.94949495,
0.95959596, 0.96969697, 0.97979798, 0.98989899, 1. ])
In [ ]:
# 주어진 shape을 가지는 numpy array를 만들어 주는데 원소는 표준정규분포에서 sampling.
np.random.randn(5, 3)
Out[ ]:
array([[ 1.76405235, 0.40015721, 0.97873798],
[ 2.2408932 , 1.86755799, -0.97727788],
[ 0.95008842, -0.15135721, -0.10321885],
[ 0.4105985 , 0.14404357, 1.45427351],
[ 0.76103773, 0.12167502, 0.44386323]])
In [ ]:
# 10부터 99까지 숫자를 자동으로 생성한 array
np.arange(10, 100)
Out[ ]:
array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
95, 96, 97, 98, 99])
1.2. Reshaping array¶
In [ ]:
# reshape을 이용하여 row vector를 column vector로 차원 변환
x = np.arange(1, 10).reshape(9, 1) # (9, ) != (9, 1)
x
Out[ ]:
array([[1],
[2],
[3],
[4],
[5],
[6],
[7],
[8],
[9]])
In [ ]:
# -1 : np가 알아서 약수를 계산해주기 때문에 일일히 값을 넣지 않아도 됨
x = np.arange(1, 121).reshape(24, -1)
x
Out[ ]:
array([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10],
[ 11, 12, 13, 14, 15],
[ 16, 17, 18, 19, 20],
[ 21, 22, 23, 24, 25],
[ 26, 27, 28, 29, 30],
[ 31, 32, 33, 34, 35],
[ 36, 37, 38, 39, 40],
[ 41, 42, 43, 44, 45],
[ 46, 47, 48, 49, 50],
[ 51, 52, 53, 54, 55],
[ 56, 57, 58, 59, 60],
[ 61, 62, 63, 64, 65],
[ 66, 67, 68, 69, 70],
[ 71, 72, 73, 74, 75],
[ 76, 77, 78, 79, 80],
[ 81, 82, 83, 84, 85],
[ 86, 87, 88, 89, 90],
[ 91, 92, 93, 94, 95],
[ 96, 97, 98, 99, 100],
[101, 102, 103, 104, 105],
[106, 107, 108, 109, 110],
[111, 112, 113, 114, 115],
[116, 117, 118, 119, 120]])
1.3. Concatenation of arrays¶
In [ ]:
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
In [ ]:
# stacking vertically
np.vstack([arr1, arr2])
Out[ ]:
array([[1, 2, 3],
[4, 5, 6]])
In [ ]:
# stacking horizontally
np.hstack([arr1, arr2])
Out[ ]:
array([1, 2, 3, 4, 5, 6])
1.4. Array Arithmetic (like vector) --> Universal Function¶
In [ ]:
# v1 = (1, 2, 3), v2 = (4, 5, 6) 벡터 2개 생성하기.
v1 = np.array((1, 2, 3))
v2 = np.array((4, 5, 6))
In [ ]:
# vector addition
v1 + v2
Out[ ]:
array([5, 7, 9])
In [ ]:
# vector subtraction
v1 - v2
Out[ ]:
array([-3, -3, -3])
element wise 연산¶
- 차원(축)을 기준으로 행렬 내에서 같은 위치에 있는 원소끼리 연산을 하는 방식
In [ ]:
# (not vector operation) elementwise multiplication
v1 * v2
Out[ ]:
array([ 4, 10, 18])
In [ ]:
# (not vector operation) elementwise division
v1 / v2
Out[ ]:
array([0.25, 0.4 , 0.5 ])
In [ ]:
# dot product (내적, 같은 위치의 원소를 곱해서 더한 값)
v1 @ v2
Out[ ]:
32
1.5. Broadcast and Universal Function¶
- 서로 크기가 다른 numpy array를 연산할 때, 자동으로 연산을 전파(broadcast)해주는 기능. 행렬곱 연산을 할 때 편리하다.
In [ ]:
arr1 = np.array([1, 2, 3])
In [ ]:
arr2 = np.array([-1, -1, -1, -1])
In [ ]:
# 2개의 array를 더해보면?
arr1 + arr2 # 2->2->3(v)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[7], line 2
1 # 2개의 array를 더해보면?
----> 2 arr1 + arr2 # 2->2->3(v)
ValueError: operands could not be broadcast together with shapes (3,) (4,)
In [ ]:
# 2개의 array를 곱해보면? (**)
arr1 * arr2 # (3, ) x (4, )
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[19], line 2
1 # 2개의 array를 곱해보면? (**)
----> 2 arr1 * arr2 # (3, ) x (4, )
ValueError: operands could not be broadcast together with shapes (3,) (4,)
In [ ]:
arr1 = np.array([1, 2, 3])
arr2 = np.array([[-1, -1, -1], [1, 1, 1]])
arr1 + arr2
Out[ ]:
array([[0, 1, 2],
[2, 3, 4]])
In [ ]:
# 2개의 array를 곱해보면? (**)
arr1 * arr2 # (3, ) x (2, 3)
Out[ ]:
array([[-1, -2, -3],
[ 1, 2, 3]])
In [ ]:
arr1 = np.array([1, 2, 3])
arr2 = np.array([[-1, -1, -1], [1, 1, 1]]).reshape(3, 2)
arr1 * arr2 # (3, ) x (3, 2)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[5], line 4
1 arr1 = np.array([1, 2, 3])
2 arr2 = np.array([[-1, -1, -1], [1, 1, 1]]).reshape(3, 2)
----> 4 arr1 * arr2 # (3, ) x (3, 2)
ValueError: operands could not be broadcast together with shapes (3,) (3,2)
- Universal Function : broadcast 기능을 확장해서, numpy array의 모든 원소에 동일한 함수를 반복문으로 적용한 것과 같은 효과를 내는 기능.
In [ ]:
arr1
Out[ ]:
array([1, 2, 3])
In [ ]:
# f = lambda x : 1/x
arr1 / 1
Out[ ]:
array([1., 2., 3.])
In [ ]:
1 / arr1
Out[ ]:
array([1. , 0.5 , 0.33333333])
In [ ]:
# f = lambda x : x + 2
arr1 + 2
Out[ ]:
array([3, 4, 5])
1.6. Indexing¶
In [ ]:
arr1 = np.arange(10)
arr1
Out[ ]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [ ]:
# 첫번째 원소
arr1[0]
Out[ ]:
0
In [ ]:
# 마지막 원소
arr1[-1]
Out[ ]:
9
In [ ]:
# 앞에서부터 원소 3개 slicing
arr1[0:3]
Out[ ]:
array([0, 1, 2])
In [ ]:
arr2 = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
arr2
Out[ ]:
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
In [ ]:
# arr2의 2row, 3column 원소 = 7
arr2[1][2]
Out[ ]:
7
In [ ]:
# arr2의 세번째 column [3, 7, 11]
Out[ ]:
array([ 9, 10, 11, 12])
In [ ]:
# arr2의 두번째 row
arr2.T[1]
Out[ ]:
array([ 2, 6, 10])
2. Numpy Methods¶
- numpy에서 사용되는 여러가지 함수들 사용
2.1. Math Functions¶
In [ ]:
# 표준정규분포에서 random sampling을 한 원소를 가지는 5x3 행렬 생성
mat1 = np.random.randn(5, 3)
mat1
Out[ ]:
array([[ 0.16557634, -1.19296651, -0.42420625],
[-0.04829529, -0.60565132, 1.30167102],
[ 0.98026611, 0.58149964, -2.17187435],
[ 0.18273081, 0.28105786, 1.57507791],
[-0.95497727, -0.22811994, 1.38508371]])
In [ ]:
# mat1에 절대값 씌우기
np.abs(mat1)
Out[ ]:
array([[5.68149639e-01, 4.78650568e-03, 3.00244482e-04],
[2.92818656e-01, 1.81252625e+00, 9.60072417e-01],
[1.49947476e+00, 4.03184370e-01, 6.38549168e-01],
[3.14421171e-01, 9.84043347e-02, 1.01295944e+00],
[4.42100278e-01, 5.75171266e-01, 4.89625229e-01]])
In [ ]:
# mat1 제곱하기
np.square(mat1)
Out[ ]:
array([[3.22794012e-01, 2.29106367e-05, 9.01467488e-08],
[8.57427655e-02, 3.28525141e+00, 9.21739047e-01],
[2.24842456e+00, 1.62557636e-01, 4.07745040e-01],
[9.88606726e-02, 9.68341308e-03, 1.02608682e+00],
[1.95452655e-01, 3.30821986e-01, 2.39732864e-01]])
In [ ]:
# mat1의 제곱근 구하기
np.sqrt(mat1)
/var/folders/88/5nvn4tln5150l93ty6c7l6bm0000gn/T/ipykernel_36109/605113763.py:2: RuntimeWarning: invalid value encountered in sqrt
np.sqrt(mat1)
Out[ ]:
array([[ nan, 0.06918458, nan],
[0.54112721, 1.34630095, nan],
[1.22453042, nan, 0.79909272],
[0.56073271, 0.31369465, nan],
[ nan, nan, nan]])
In [ ]:
np.sqrt(mat1.astype('complex'))
Out[ ]:
array([[0. +0.75375702j, 0.06918458+0.j ,
0. +0.01732756j],
[0.54112721+0.j , 1.34630095+0.j ,
0. +0.97983285j],
[1.22453042+0.j , 0. +0.63496801j,
0.79909272+0.j ],
[0.56073271+0.j , 0.31369465+0.j ,
0. +1.00645886j],
[0. +0.66490622j, 0. +0.75840047j,
0. +0.69973225j]])
In [ ]:
# linear algebra functions
vec = np.array([1, 2, 3])
# 1. norm
np.linalg.norm(vec)
# 2. eigenvalue
mat = np.array([[1, 0],
[0, 1]])
np.linalg.eig(mat)
Out[ ]:
EigResult(eigenvalues=array([1., 1.]), eigenvectors=array([[1., 0.],
[0., 1.]]))
2.2. Aggregation functions¶
In [ ]:
np.random.seed(0xC0FFEE)
mat2 = np.random.rand(3, 2)
mat2
Out[ ]:
array([[0.57290783, 0.81519505],
[0.92585076, 0.09358959],
[0.26716135, 0.96059676]])
In [ ]:
# Summation
np.sum(mat2)
Out[ ]:
3.635301352873192
In [ ]:
# axis= 0: 세로, 1: 가로 축을 기준으로 연산 가능
print(np.sum(mat2, axis=0))
print(np.sum(mat2, axis=1))
[1.76591995 1.86938141]
[1.38810288 1.01944035 1.22775812]
In [ ]:
# mean
np.mean(mat2, axis=0)
Out[ ]:
array([0.58863998, 0.62312714])
In [ ]:
# std
np.std(mat2, axis=0)
Out[ ]:
array([0.26913882, 0.37911557])
In [ ]:
# min, max
print(np.min(mat2, axis=0))
print(np.max(mat2, axis=0))
[0.26716135 0.09358959]
[0.92585076 0.96059676]
In [ ]:
# 최소값이 있는 Index
np.argmin(mat2, axis=0)
Out[ ]:
array([2, 1])
In [ ]:
# 최대값이 있는 Index
np.argmax(mat2, axis=0)
Out[ ]:
array([1, 2])
3. Performance Check
- Universal Function 기능을 통해 반복문을 사용한 것보다 훨씬 빠른 성능을 냅니다.
- 직접 실험을 통해 그 차이를 확인해보겠습니다.
In [ ]:
np.random.seed(0)
def reverse_num(values):
output = np.empty(len(values))
for i in range(len(values)):
output[i] = 1.0 / values[i]
return output
In [ ]:
# 1부터 100까지 범위에서 1000000개를 랜덤으로 뽑아서 array를 만듭니다.
big_array = np.random.randint(1, 100, 1000000)
big_array
Out[ ]:
array([23, 16, 94, ..., 3, 65, 77])
In [ ]:
%timeit reverse_num(big_array)
718 ms ± 12.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [ ]:
%timeit 1.0 / big_array
1.09 ms ± 41.8 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
In [ ]: