NumPy
numpy
모듈의 실용성과 효율성은 입에 침이 마르도록 칭찬해도 부족함이 없다고 생각한다.
벡터(vector)와 행렬(matrix)를 활용한 연산의 속도와 메모리 활용 효율성이 list()
와는 비교가 되지 않을뿐더러 머신러닝처럼 수학적 연산이 중요한 기술력에 근본적인 거름이 되어주는 모듈이기도 하다. (벡터: 1차원, 매트릭스: n차원)
numpy
는 파이썬 내장 기능이 아니기때문에 별도의 모듈 설치가 필요하다. 터미널을 열고 아래의 명령어를 입력하여 모듈을 설치해주자.
1
$ pip install numpy
설치가 완료되었다면, numpy
를 불러와주자.
1
import numpy as np
numpy
는 기본적으로 np
라는 가명 (alias)를 사용하는데, 만국공통 국룰이라고 생각하고 np
로 설정해주길 바란다.
NumPy 기초
NumPy의 data type
기초에서는 기본적인 python과 다른 numpy만의 새로운 data type에 대해 알아보자. 추후 준비가 되었을 때, 다음 포스팅에서 NumPy를 응용해보는 연습 및 코드 기록을 해보겠다.
numpy
느 기본적으로 python의 기본 자료구조 및 데이터 타입인 list, tuple, range, dict, set
과는 다르다.
ndarray
라는 데이터 타입을 기본으로 하는데 n-dimensional array, 즉 n차원 배열이라고 생각하면 된다.
- 1차원은 직선
- 2차원은 평면
- 3차원은 공간
- 4차원 ….. 인간의 두뇌로는 3차원적 사고까지가 한계임 :)
ndarray의 특징을 python의 list와 비교해보자
python’s list | NumPy ndarray |
---|---|
1. [1, 2, 3, 4, 5] ( 요소를 comma ‘,’ 로 구분) | 1. [1 2 3 4 5] (요소를 space로 구분) |
2. [1, True, 3.14, 'Hello'] 요소에 각기 다른 데이터 타입을 넣을 수 있음. | 2. [2. 3. 10. 0. 1.] 요소에 하나의 데이터 타입만 올 수 있음. (정수면 정수, 실수면 실수, 문자열이면 문자열…) (실수가 ndarray 요소라면 숫자 뒤에 ‘.’ 이 붙으니 참고하도록 하자.) |
3. list는 수학적 연산 속도가 느림 | 3. 반대로 넘파이는 빠름. 메모리 효율이 좋음 이렇기 때문이라도 numpy 가 머신러닝에서 필수가 되는 모듈임. |
4. 중첩 리스트를 사용한다면 [[1,2,3], [4, 5, 6]] 이렇게 표기가 됨 | 4. ndarray에서 중첩은 차원을 나타냄. 그래서 [[1 2 3] [4 5 6]] 이렇게 표기가 됨. |
그래서 어쩌라고? 싶을 수도 있다. 하지만 데이터 타입을 알아야 나중에 머신러닝 및 다른 NumPy
기반 모델링을 통한 계산을 할 때, 올바른 결과를 도출할 수 있다. 일례로 numpy
의 메서드중에서는 오류를 출력하지 않고 임의로 숫자를 부여해서 ndarray를 만드는 함수가 몇 개 있는데, 이때 모델링이 아무리 좋아도 데이터 핸들링이 안되기때문에 왜곡된 결괏값을 받더라도 알 길이 없을 확률이 높기 때문이다.
그렇기에 이 fundamental 을 꼭 필히 익히길 권장한다. (물론 필자에게도 해당되는 사항이다.)
이번엔 코드와 함께 살펴보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
a = [1, 2, 3, 4, 5] # python's list
print(type(a)) # <class 'list'> 라는 놈이 출력된다.
# 그렇다면 개별 요소는 어떤 type 을 갖고 있을까?
print(type(a[0])) # <class 'int'> 당연히 정수가 나온다.
### 이번엔 ndarray를 살펴보자.
b = np.ndarray(a) # 필자는 "np야 너 안에 있는 ndarray라는 행위를 좀 쓸게" 라고 생각하면서 코딩을 하는데, 좀 괜찮게 느껴진다.
# 파이썬 list인 a 를 갖고 ndarray를 만들었다.
# **기억해야할 점은 ndarray를 만드는 수많은 방법중 한가지일뿐이라는 것이다.
print(b) # [1 2 3 4]
print(type(b)) # <class 'numpy.ndarray'> numpy.ndarray 라는 class 라는 것을 볼 수 있다.
# 아마 무슨 소리야? 싶은 사람들도 있을것이다. class 의 개념은 심오하니깐.
# 하지만 일단은 단순하게 파이썬의 list와는 좀 다르구나 정도로 이해하고 넘어가자.
print(type(b[0])) # 당연하게도 numpy ndarray도 인덱싱과 슬라이싱이 가능하다.
# 결괏값은 <class 'numpy.int64'> 이거 정수 맞아? ㅇㅇ정수 맞다! 그러니 정수라고 생각하고 넘어가도 된다.
아까 테이블에서 나열했던 numpy 의 특징중 하나를 기억해보자.
“ndarray는 요소들의 data type이 다르면 안된다! 즉 하나의 통일된 data type을 가진 요소만 받을 수 있다.”
따라서 요소가 다르다면 데이터 타입을 변경해주어야한다. 그중 쉬운 방법은
1
d = np.ndarray([1, 2, 3.14, 5, 10], dtype=int64) # 이렇게 정수사이에 실수가 있다면, dtype을 변경하면서 에러를 피할수 있다.
그렇다면 과연 ndarray중에 정수, 실수, 문자열, 불리안 등과 같이 각기 다른 data type이 있다면 어떻게 될까?
모두 문자열(str)화 된다. 이는 값을 잃어버리지 않으면서 모두 포용할 수 있기 때문이기도 하다.
몇 가지 기본적인 특징을 추가적으로 언급하자면,
list
나tuple
처럼 indexing 및 slicing이 가능함. (e.g.)d[0]
( 매트릭스라면 0번째 행, 벡터라면 0번째 요소 )class
가ndarray
인 만큼 속성 (instance)와 행동 (methods) 를 갖고 있음.1 2 3 4 5 6 7 8 9
print(d.dtype) # int64 print(d.ndim) # 차원의 수 (현실에선 거의 쓰지 않음) print(d.shape) # 이건 굉장히 많이 쓰이지. 튜플로 값을 토해내는데 tuple의 요소 수(n)가 n-dimension (차원) 을 나타냄 # 또한 예를 들자면 (2, 3) 2행 3열을 뜻하고 (2x3 matrix를 뜻함) # (2, 2, 5) 2면 2행 5열을 뜻하는 3차원 배열을 뜻함. # 위에 있는 d 같은 경우에는 # d.shape => (5, ) 벡터를 알려주는 tuple 로 토해냄. print(d.size) # 총 요소 개수를 알려줌 d 같은 경우는 5이다.
ndarray
를 만드는 다양한 방법
뭐 얼마나 거창하길래? 별거 없다. 익숙해지기만 하면 된다! 다만 아래에 쓰일 numpy
고유의 메서드는 굉장히 많이 쓰이니 꼭 기억하길 바란다.
코드를 통해 알아보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1.
arr = np.zeros((3, 4)) # 3x4 매트릭스를 만들고 요소들의 값은 0으로 줘라.
arr = np.zeros((3, )) # 1x3 벡터를 만들고 요소의 값은 0으로 줘라. ==> [ 0 0 0 ]
arr = np.zeros((2, 3, 3)) # 2x3x3 매트릭스를 만들고 요소들의 값은 0으로 줘라. (3차원)
# 이 메서드의 인자는 tuple로 들어와야함.
# 2.
arr = np.ones((3, 2)) # 3x2 매트릭스를 만들고 요소들의 값은 1로 줘라.
# 3.
arr = np.empty((3, 2) # 3x2 매트릭스를 만들고 내용물을 비워둬라? 이렇게 해석하면 되나?
# 절반은 맞다. 하지만 내용물은 쓰레기 값 (임의의 값)을 갖는다고 이해하자 (100% 맞는 말은 아님.)
# 4.
arr = np.full((3, 2), 9) # 3x2 매트릭스를 만들고 요소들의 값은 9로 채워라!
# 5.
arr = np.arange(0, 10, 2) # [0 2 4 6 8] 파이썬의 range와 다르게 실제 값을 갖고 있음.
# 6.
arr = np.linspace(0, 10, 10) # 인자는 (시작, 끝, 요소 개수) 이렇게 받음.
# 10개의 요소를 균일한 간격으로 시작과 끝 range에 맞춰서 ndarray를 만들자 라는 소리임.
이 외에도 여러가지 방법이 있으리라 생각이 된다. 당장은 최소 필자가 알고 있는 이 정도까지는 익숙해지자. 정말 많이 쓰인다.
np.random
독자들은 확률 분포에 대해서 얼마나 아는가?
감히 말하건데 numpy
를 제대로 다루려면 정규분포, 균등분포와 같이 대표적인 분포도에 익숙해져 있어야 한다.
머신러닝이나 numpy
를 활용하게 된다면 난수 (random number)를 통한 방법에 대한 테스트를 진행하게 되는데 “어떤 분포도를 따랐느냐(?)”는 편향되지 않은, 나아가 신뢰할 수 있는 결괏값 도출에 대한 초석이 된다.
잔소리는 이쯤하고 np.random
을 활용해보자.
1
2
3
4
# 1. np.random.normal(mean, std, (<shape>)) 정규 분포 그래프를 이용한 난수 추출 -- 평균, 표준편차가 필요하다
mean = 50
std = 2
arr = np.random.normal(mean, std, (100000, )) # 10만개의 난수를 정규분포 내에서 추출을 한다.
음 근데 질문이 있어요. 진짜 난수인지 어떻게 알죠?
histogram 을 그려보면 안다. (독자들이 정규 분포를 안다고 가정하겠다.)
1
2
3
4
import matplotlib.pyplot as plt
plt.hist(arr, bins=100)
plt.show()
이렇게 matplotlib
을 이용해서 히스토그램을 만들어주면, 꽤나 흡사한 bell shape 커브를 볼 수 있을것이다. 이는 randomization이 잘되었다는 지표기도 하다.
(이미지 사이즈는 나중에 수정하겠습니다…)
여하튼 다시 돌아와서 np.random.normal()
을 살펴보도록 하자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 아마 예상하신 분도 있겠지만, 마지막 인자는 <shape> 를 나타내기에 튜플의 값을 통해 난수를 벡터로 받을 수도 그리고 매트릭스로 받을 수도 있다.
np.random.normal(mean, std, (3, 2)) # 3 x 2 매트릭스형태로 난수를 받겠다는 소리임.
# 2.
np.random.rand(mean, std, (3, 2)) # rand는 [0, 1) 0이상 1미만의 균등분포를 따라서 ndarray를 생성한다.
# 이번에는 균등분포이기에 벨모양의 커브가 아닌것을 인지하자
# 3.
np.random.randn(100000) # 이 친구는 평균값 0, 표준편차 1을 가진 정규 분포이다. 당연히 양수와 음수 둘다 나올 수 있음.
# 4.
np.random.randint(-100, 100, (100000, )) # -100과 100사이의 난수를 100000개 추출하겠다는 소리임.
# 5.
np.random.random((100000, )) # 인자로 shape 만 나옴.
# 이친구는 0이상 1미만의 균등분포에서 실수형 난수를 추출하여 shape에 맞게 ndarray 생성
아마 위 코드를 보자면 “2번과 5번의 차이가 뭔데 그럼?” 이렇게 질문을 하는 독자도 있을것이다.
차이는 거의 없다. 딱 하나! shape을 나타내는 tuple
이 인자로 들어가냐 아니냐의 차이이다.
“지금은 그게 뭐 어쨌다는 거지…” 싶을수 있지만 나중에는 필요한 순간이 있다는 전문가 들의 견해가 있었다ㅎㅎ (저도 아직 못써봄..)
numpy 난수 관련 함수
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
# numpy 난수 관련 함수
# 난수의 재현성을 확인해보자.
# 랜덤값(난수) : 컴퓨터의 난수는 알고리즘에 근거하는데, 초기값 (seed)을 이용해서 특정 알고리즘을 수행시켜 난수값을 도출.
#
import numpy as np
# arr = np.random.randint(0, 100, (5,)) # 0이상 100미만 1차원으로 5개만 추출하자 그리고 numpy array 를 만들자
# print(arr)
# 보다시피 실행을 해보면 실행때마다 각기 다른 난수를 토해낸다.
# seed (초기값)을 주면 난수 출력시 처음 출력되었던 난수만 고정시켜서 출력시킬 수 있다.
np.random.seed(1000)
arr = np.random.randint(0, 100, (5,))
print(arr)
# 왜 초기값을 고정해두냐?
# 나중에 ML할 때 우리가 어떤 모델링을 난수를 통해 결괏값을 보고자 한다.
# 그때 계속해서 실행때마다 난수가 변형되면, 모델이 맞는지 틀린지 검사하기가 힘듦.
# 그때 임의로 난수를 고정시켜서 확인하는 작업을 함.
# 그렇기때문에 seed값을 주는게 매우 중요하다.
# 그렇다면 데이터의 순서는 어떻게 바꾸나요?? ==> shuffle
arr = np.arange(0, 10, 2) # 파이썬의 레인지와 같다. 대신에 실제 요소를 들고 있지
print(arr)
# result = np.random.shuffle(arr) # 이렇게 사용하는 것이 아님.
# print(result) # None
# # 섞이긴 섞이는데 결과가 바뀌는게 아니라 뭐가 바뀐단 소리야? 원본이 바뀐단 소리.
np.random.shuffle(arr) # 그냥 이렇게 사용하는것임
print(arr)
# 데이터 집합에서 일부를 무작위로 선택. Sampling을 수행하기 위해서 사용하는 choice()
# numpy.random.choice(a, size, replace, p)
# a : ndarray, 혹은 정수가 나오면 arange()를 수행한 결과임.
# size: sample 개수
# replace = True : 한번 뽑은걸 다시 뽑을 수 있다.
# p = [0.1, 0.3, 0, 0.1, 0.1, 0.35, 0.05]
arr = np.array([1, 2, 3, 4, 5, 6, 7])
arr_1 = np.arange(5)
np.random.choice(arr)
np.random.choice(arr_1)
# 만약에 np.random.choice(5) 를 쓴다면
# print(np.random.choice(5))
np.random.choice(arr, 3) # 뒤에 인자로 들어온 3의 의미는 3개 뽑겠다는 소리임.
result = np.random.choice(arr, 3, replace=False)
print(result)
result = np.random.choice(arr, 3, replace=False, p=[0.1, 0.3, 0, 0.1, 0.1, 0.35, 0.05]) # p는 원래라면 ndarray임
# 그렇지만 다행이도 리스트로 넣어도 자체적으로 변환시킴
print(result)
# 여기까지가 random에 관한 주요 기능들임.
작성하다보니 글이 너무 길어졌다.
오늘은 여기까지만.. 다음 포스팅은 numpy
shape
에 관하여 작성해보겠다.