# pip install numpy
import numpy as np
numpy는 numeric python의 약자로,
n차원 배열 (ndarray)을 이용한 행렬 연산(성분별 연산)을 통해 고속 연산을 제공하는 라이브러리이다.
ndarray 배열은 ndim, shape, dtype이라는 속성을 가진다.
다음은 ndarray를 생성하고, 속성을 확인하는 코드이다.
# array함수를 통해 ndarray 생성 (인자로 sequence 전달)
arr = np.array([1,2,3])
# ndarray의 속성값
print(arr.ndim,arr.dtype,arr.shape)
# astype함수를 통해 type을 변경한 ndarray를 생성할 수 있다. => 이를 통해 정수로 이루어진 문자열 ndarray또한 type을 바꿀 수 있다.
arr1 = arr.astype(np.float64)
출력
1 int32 (3,)
다음은 다차원 배열 출력 예시이다.
#2차원 ndarray 생성
arr= np.array([[1,2,3],[4,5,6]])
arr.shape
출력
(2, 3)
numpy는 다양한 형태의 배열을 생성할 수 있다. zeros, ones 같은 함수는 각각 0,1로 구성된 배열을 return하고 linspace, randn, normal함수는 각각 균등분포, 정규분포에서 원소를 추출하여 배열을 생성한다.
다음 예시코드에서 부터 함수 사용법과 인자 구분등을 주석으로 확인할 수 있다.
# zeros, ones, arange ( arange는 python의 range객체와 같이 사용)
a = np.zeros((2,3))
b = np.ones((1,5))
c = np.arange(1,6,2)
d = np.zeros_like(c) # 인자로 받은 array와 동일한 shape과 dtype의 zeros 배열 생성
# np.linspace(a,b,num) a<= 원소 <=b 인 원소 num개를 균등한 간격으로 가진 배열 생성
a = np.linspace(0,1,10)
# np.random.random((shape)) -> 0~1사이의 균등분포에서 원소를 추출하여 shape를 가진 배열 생성, 인자 shape은 tuple형태로 전달
a = np.random.random((2,4))
#np.random.randn(shape) mean = 0, std = 1인 정규분포에서 shape size의 난수를 원소로하는 배열을 생성, shape는 '()'없이 전달 --> (randn, reshape, transpose는 shape을 '()'기호 없이 인자로 전달한다.
a = np.random.randn(2,4)
# np.random.normal(loc,scale,(shape))의 경우 평균과 표준편차를 지정할 수 있음
- 배열과 배열 사이 또는 배열과 scalar 사이에 element wise한 연산이 가능하다.
na = np.array([1,2,3])
nb = np.array([4,5,6])
print(na + nb, na * nb, na *2, sep = ' ')
출력
[5 7 9] [ 4 10 18] [2 4 6]
6 2.0 3 1
- numpy indexing & slicing
numpy의 1차원배열의 경우 파이썬 내장 리스트의 indexing과 slicing 기법을 동일하게 적용할 수 있다.
다만 slicing을 할 때, shallow copy이므로 deep copy 객체를 생성하려면.copy()함수를 사용한다.
다차원 배열의 경우에는 python 내장 리스트와 같이 indexing과 slicing을 할 수 있으나 comma를 통해서도 indexing과 slicing을 할 수 있다.
예시를 통해 확인해 보자.
aa = np.array([1,2,3,4,5])
print(aa[0],aa[:3],end='\n\n')
bb = np.array([[1,2,3,4,5], [6,7,8,9,10]])
print(bb[1,0],bb[:2,:2],bb[1,:],bb[:,0],bb[:,:2],sep ='\n')
출력
1 [1 2 3]
6
[[1 2]
[6 7]]
[ 6 7 8 9 10]
[1 6]
[[1 2]
[6 7]]
앞서 알아본 방식으로도 배열을 인덱싱할 수 있지만, numpy ndarray는 논리 인덱싱도 가능하다. 예시를 통해 자세히 알아보자.
# 논리 인덱싱
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
np.random.seed(0)
data = np.random.randn(7, 4)
data
출력
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, 0.33367433],
[ 1.49407907, -0.20515826, 0.3130677 , -0.85409574],
[-2.55298982, 0.6536186 , 0.8644362 , -0.74216502],
[ 2.26975462, -1.45436567, 0.04575852, -0.18718385]])
arr = data[names == "Bob",:2]
# names에서 0, 3을 논리 연산자로 가져와 data에서 indexing한다.
print(arr) #이 때 arr는 deep copy 객체
mask = (names == 'Bob') | (names == 'Will')
# 여러 행 정보를 masking하여 indexing에 활용한다.
# numpy 논리 연산자로 and or등을 사용할 수 없고 반드시 &,|를 사용해야된다.
arr_m = data[mask]
# 논리 인덱싱으로 원본 데이터 수정
# 다른 방식의 논리 인덱싱
data[data < 0] = 0
print(data)
출력
[[1.76405235 0.40015721]
[0.76103773 0.12167502]]
[[1.76405235 0.40015721 0.97873798 2.2408932 ]
[1.86755799 0. 0.95008842 0. ]
[0. 0.4105985 0.14404357 1.45427351]
[0.76103773 0.12167502 0.44386323 0.33367433]
[1.49407907 0. 0.3130677 0. ]
[0. 0.6536186 0.8644362 0. ]
[2.26975462 0. 0.04575852 0. ]]
다음은 추가적인 정수 예시이다. 정수 인덱싱을 통해 다양한 방식으로 array를 indexing할 수 있다.
# 정수 인덱싱
arr = np.empty((8, 4))
for i in range(8):
arr[i] = i
print(arr, end ='\n\n')
# 음수를 포함한 행 인덱스 배열을 ndarray의 index로 주어 특정 행만 뽑을 수 있다.
print(arr[[-4, -5, 0, 6]], '\n\n')
# 음수 인덱스 -1은 마지막행인 7행을 의미하고 -2행은 그 전 행인 6행을 의미한다.
# 2개의 인덱스 배열을 인자로 주면 인덱스 배열들의 대응하는 원소별 튜플에 해당하는 원소들로 구성된 1차원 ndarray가 반환된다.
print(arr[[-4, -5, 0, 6],[0,2,3,1]])
# 예를들어 (-4,0), (-5,2), ...의 원소들로 구성된 ndarray가 반환된다.
출력
[[0. 0. 0. 0.]
[1. 1. 1. 1.]
[2. 2. 2. 2.]
[3. 3. 3. 3.]
[4. 4. 4. 4.]
[5. 5. 5. 5.]
[6. 6. 6. 6.]
[7. 7. 7. 7.]]
[[4. 4. 4. 4.]
[3. 3. 3. 3.]
[0. 0. 0. 0.]
[6. 6. 6. 6.]]
[4. 3. 0. 6.]
행렬의 전치 연산을 할 수 있다.
역시 예시를 통해 알아보자.
# 행렬의 전치
arr = np.arange(0,20).reshape(4,5)
# reshape함수는 함수의 shape을 인자와 같이 바꿔준다. ex) 4*5 size행렬
arr = arr.dot(arr.T) # arr.T -> 전치 행렬
print(arr)
arr_3 = np.arange(2*3*4).reshape(2,3,4) # reshape에 인자로 -1을 넣으면 예시의 2*3*4의 값을 맞추는 인자가 -1대신 채워진다. ex) 2,3,-1 -> -1대신 4가 채워짐
print(arr_3)
arr_3 = arr_3.transpose(2,0,1) # axis 0 -> 면, 1 -> 행, 2-> 열을 의미하는데, transpose함수를 통해 기존의 2 * 3 * 4 의 고차원 행렬도 4*2*3 행렬로 전치할 수 있다.
arr3
# 같은 열 기준으로 새로운 4개의 면이 구성, 각 면별로 새로운 2행(기존에 면으로 구분되던 원소들이 행으로 구분), 3열 (기존에 행으로 구분되던 원소들이 열로 구분) 로 전환된다.
# 비슷한 함수 arr.swapaxes(0,2) -> 면과 열의 축 전치==>arr.transpose(2,1,0)와 같은 효과
출력
[[ 30 80 130 180]
[ 80 255 430 605]
[ 130 430 730 1030]
[ 180 605 1030 1455]]
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]]
array([[[ 0, 4, 8],
[12, 16, 20]],
[[ 1, 5, 9],
[13, 17, 21]],
[[ 2, 6, 10],
[14, 18, 22]],
[[ 3, 7, 11],
[15, 19, 23]]])
여기까지 numpy library의 기본적인 성질과 함수들을 알아보았다.
다음 포스트에서 수학함수, 선형대수함수, random module 등 다양한 활용법에대해 알아보자.
numpy(2) 링크-> Numpy(2)
References
https://compmath.korea.ac.kr/appmath/NumpyBasics.html
https://velog.io/@euisuk-chung/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8B%9C%EA%B0%81%ED%99%94-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0-Numpy