일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- string
- sorting
- Tree
- dynamic programming
- 자바
- 코딩테스트
- array
- Matrix
- two pointers
- geometry
- greedy
- database
- bit manipulation
- 코테
- Number Theory
- hash table
- SQL
- Method
- Stack
- Data Structure
- java
- simulation
- Binary Search
- Binary Tree
- implement
- 구현
- Counting
- 파이썬
- Class
- Math
- Today
- Total
코린이의 소소한 공부노트
Numpy 기초 다지기 본문
딥러닝에서 많이 쓰이는 패키지 중 하나인 numpy에 대해서 이전 글에서 잠깐 언급했었다. 이번 글에서는 넘파이의 기초를 다져보겠다. 기초가 아주 많으니 읽다가 졸지 않기! 시작하기 전에 꼭 준비해야 할 것이 있쥬?!
import numpy as np
zeros, ones
이름 그대로 0들, 1들이다. 도대체 무엇이? 배열의 원소들이!
일단 zeros부터 살펴보겠다. zeros는 0으로 가득 찬 배열을 만들어준다.
z1 = np.zeros([3,3]) # []나 ()를 이용해서 shape를 주면 알아서 만든다.
z1
>>
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
z2 = np.zeros(3) # 정수를 쓰면 행 우선 벡터(1차원)가 만들어진다.
z2 # 3이라서 길이가 3인 행 우선 벡터 생성.
>>
array([0., 0., 0.])
z3 = np.zeros(3, order='F') # 이것은 열 우선 벡터.
z3 # 우리가 보기에는 위의 것과 차이가 없지만
>> # 메모리에는 열 벡터로 저장됨.
array([0., 0., 0.])
- zeros의 파라미터 중 order라는 것이 있는데, 'C'는 C-style로 행 우선(row-major) 벡터, 'F'는 Fortran-style로 열 우선(column-major) 벡터를 나타낸다. 기본값은 'C'이다.
- 보면 알겠지만 0이 다 실수형으로 저장되어있다. 파라미터 중 dtype이 float64인 게 기본값이어서 그렇다. 바꾸고 싶으면 얼마든지 바꾸면 된다.
이번에 볼 것은 ones이다. ones는 1로 채워준다는 거 빼고 zeros와 거의 같기 때문에 설명할 게 없다.
o1 = np.ones((2,2)) # 생성 방법은 zeros와 똑같다.
o1
>>
array([[1., 1.],
[1., 1.]])
o2 = np.ones(5, dtype=int) # 정수 1로 채운 배열을 만들어봤다.
o2
>>
array([1, 1, 1, 1, 1])
arange, reshape
arange는 이전에 봤던 range와 같은 기능을 한다. 숫자가 순서대로 들어간 넘파이 배열(1차원)을 만들어준다.
arr1 = np.arange(5) # 숫자 1개만 쓰면 0부터 n개가 나옴
arr1
>>
array([0, 1, 2, 3, 4])
arr2 = np.arange(4,9) # 시작(a)과 끝(b)을 알려주면 [a,b)가 나옴
arr2
>>
array([4, 5, 6, 7, 8])
arrange 오타가 아닐까 생각할 수 있다. 필자는 헷갈리지 않기 위해서 배열 Array의 범위 RANGE라서 arange구나 하면서 외웠다.
reshape를 이용해서 차원을 바꿀 수 있다.
arr3 = np.arange(9).reshape(3,3) # 0부터 8까지 9개의 숫자가 들어간 1차원 배열을
arr3 # 3행 3열로 바꿔줘!
>>
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
arr4 = np.arange(9).reshape(2,3) # 개수가 맞지 않으면
arr4 # 에러 발생!
>>
ValueError: cannot reshape array of size 9 into shape (2,3)
위의 예시는 선언 당시 reshape을 했지만, 배열을 먼저 선언한 후 나중에 reshape을 하면 원본에 영향을 미치지 않는다.
ar = np.array([[1,2,3],[1,2,3]])
ar.shape
>>
(2, 3)
# 1행 6열로 reshape
ar.reshape([1,6]) # [ ] 없어도 무관
>>
array([[1, 2, 3, 1, 2, 3]])
# 다시 shape 확인
ar.shape
>>
(2, 3)
reshape에서도 -1을 활용할 수 있다.
ar.shape
>>
(2, 3)
# 1차원 배열로 바꿔버리기
ar = ar.reshape(-1) # reshape(-1) == ravel()
ar.shape
>>
(6,)
# 다시 2행 3열로 바꾸기
ar = ar.reshape(-1, 3) # 6 / 3 = 2 이므로 -1에 2가 들어가게 된다.
ar.shape
>>
(2, 3)
# 3차원으로 늘려보기
ar = ar.reshape(-1, 2, 3) # 6 / 2 / 3 = 1이므로 -1에 1이 들어가게 된다.
ar.shape
>> (1, 2, 3)
ar
>>
array([[[1, 2, 3],
[1, 2, 3]]]) # 2행 3열 매트릭스에 [ ]가 한 겹 더 씌워졌다.
index, slicing
리스트에서 인덱스, 슬라이싱 많이 봤었다.
nums = [1,2,3,4,5]
nums[2] # 인덱스는 0부터!
>>
3
nums[2:] # 2번째부터 끝까지 슬라이싱
>>
[3, 4, 5]
이 인덱스와 슬라이싱을 array에서 똑같이 쓸 수 있다.
a = np.arange(9).reshape(3,3)
a
>>
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
# 2차원이기 때문에 인덱스는 2개까지 사용 가능
# 1. 인덱스를 1개만 쓸 경우 해당 행 전체를 가져옴
a[1]
>>
array([3, 4, 5])
# 2. 인덱스를 2개 쓸 경우 해당 행/열에 위치한 원소를 가져옴
a[1][-1] # 1행의 마지막열
>>
5
# 3. 인덱스 2개를 쓸 경우 한 대괄호 안에 사용 가능
a[1,-1] # 위의 예시와 같은 경우
>>
5
# 4. 슬라이싱도 리스트에서 했던 것처럼 쓰면 된다.
a[1:] # 1번째 행부터 끝 행까지
>>
array([[3, 4, 5],
[6, 7, 8]])
a[1:,1:] # (1행~끝행)에서 (1열~끝열)만 가져옴
>>
array([[4, 5],
[7, 8]])
예시를 보면 이해하는 것이 크게 어렵지 않을 거라 생각한다.
broadcast
브로드캐스트는 서로 다른 두 개의 배열의 shape이 다를 때 산술 연산하는 방법이다. 물론 다 되는 것은 아니고, 특정 조건을 만족할 때 가능하다.
원래 배열의 크기가 같으면 계산은 당연히 된다.
a = np.array([1, 2, 3]) # a.shape >> (3,)
b = np.array([0.1, 0.2, 0.3]) # b.shape >> (3,)
a+b
>>
array([1.1, 2.2, 3.3]) # 결과도 (3,) = 크기가 3인 벡터(1차원)
두 배열의 차원이 다르다면 차원이 낮은 배열이 알아서 복사가 되어 높은 배열에 맞춰지게 된다. 이때 타입이 다르다면 알아서 타입 캐스팅(type casting, 포함관계에서 더 상위에 있는 것으로 바꿔줌)을 해준다. 결과로 반환된 배열은 연산을 수행한 배열 중 차원의 수(ndim)가 가장 큰 배열이 된다.
a = np.array([[1, 2, 3],[4, 5, 6]]) # a.shape >> (2, 3), 정수
b = np.array([0.1, 0.2, 0.3]) # b.shape >> (3,), 실수
a+b
>>
array([[1.1, 2.2, 3.3], # a의 원소들이 정수 -> 실수로 타입 캐스팅되어 계산
[4.1, 5.2, 6.3]]) # 결과는 (2, 3) = 2행3열 매트릭스(2차원)
# 즉 이 덧셈 연산은
# [ [1, 2, 3] + [ [0.1, 0.2, 0.3]
# [4, 5, 6] ] [0.1, 0.2, 0.3] ] 이 된 것이다.
a는 2차원, 2행 3열 배열이고 b는 1차원이고 크기가 3인 배열이다.
a | 2차원 | 2 X 3 | 결과: 2 X 3 |
b | 1차원 | 3 |
가장 낮은 차원인 1차원의 크기가 둘 다 3이므로 브로드캐스팅이 가능하다. 이때 1차원을 2차원으로 늘려서 1 X 3으로 만들어 준 다음, 2차원의 shape이 2, 1이므로 2와 1의 최소공배수인 2로 맞춰서 결과 배열이 2 X 3이 되는 것이다.
정리해서 얘기해보자면, 브로드캐스팅이 가능한지 확인할 때, 가장 낮은 차원부터 shape을 확인한다. 즉, shape을 튜플로 나타냈을 때 가장 오른쪽부터 확인한다. 그랬을 때 차원의 크기가 같은 숫자 거나, 한쪽이 1일 때 브로드캐스팅이 적용 가능하다. 브로드캐스팅이 적용된 배열(결과)의 shape은 연산에 사용된 배열(위 예시에서는 a, b)들의 차원의 크기의 최소공배수 값이 된다.
넘파이 문서에 나와있는 여러 예시를 가지고 추가 설명을 해보겠다.
넘파이 문서에는 브로드캐스팅이 안 되는 예시도 함께 있다.
브로드캐스팅은 배열과 배열뿐만 아니라, 배열과 숫자 사이에서도 가능하다.
c = np.arange(9).reshape(3,3)
c
>>
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
# 사칙연산 모두 가능하다.
c+1
>>
array([[1, 2, 3], # 배열의 모든 원소에 +1
[4, 5, 6],
[7, 8, 9]])
c*2
>>
array([[ 0, 2, 4], # 배열의 모든 원소에 *2
[ 6, 8, 10],
[12, 14, 16]])
boolean indexing
논리형 연산자를 인덱스에 이용해서 배열을 쉽게 슬라이싱 할 수 있다. 불리언 인덱싱을 해두면 True인 값들만 골라낼 수 있다. 예시로 해볼 것은 0부터 8까지의 숫자가 담긴 2차원 배열에서 짝수를 찾아 -1로 바꾸는 것이다.
# 브로드캐스팅 예제에서 썼던 c를 데리고 왔다.
c
>>
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
# c에서 짝수가 어디 숨어있나 확인해보자.
c%2 == 0
>>
array([[ True, False, True],
[False, True, False],
[ True, False, True]]) # 이것이 바로 불리언 인덱싱
# 불리언 인덱싱으로 c에서 짝수만 골라내보자.
c[c%2 == 0] # True인 값만 찾아준다.
>>
array([0, 2, 4, 6, 8])
# 짝수를 다 확인했으니, 불리언 인덱싱으로 모두 -1로 바꿔보자.
c[c%2 == 0] = -1
c
>>
array([[-1, 1, -1],
[ 3, -1, 5],
[-1, 7, -1]])
math function
numpy는 수치해석 모듈(패키지)이기 때문에 각종 연산들이 다 들어있다.
1. 기본 연산자
aa = np.arange(6).reshape(2,3)
aa
>>
array([[0, 1, 2],
[3, 4, 5]])
# np.add(aa, 10): aa의 각 원소에 전부 10씩 더하는 것
aa + 10 # 위의 식을 더 간단히 표현한 것
>>
array([[10, 11, 12],
[13, 14, 15]])
# 덧셈 외에도 얼마든지 사용 가능
# 뺄셈 subtract -
# 곱셈 multiply *
# 나눗셈 divide /
# 나머지 mod %
# 당연하겠지만, 배열끼리의 연산도 물론 가능
aa + aa
>>
array([[ 0, 2, 4],
[ 6, 8, 10]])
2. random 모듈
랜덤 모듈은 무작위로 숫자를 만들어주는 모듈이다. 이 모듈 안의 메소드 몇가지만 살펴보겠다.
1) random 메소드
# random은 0 이상 1 미만의 실수 중 하나를 할당해준다.
a = np.random.random()
a
>>
0.1214054517246419
# 사이즈를 정해주면 해당 개수만큼 만들어 배열에 담아준다.
b = np.random.random(3)
b
>>
array([0.29878834 0.3038621 0.48833014])
type(b)
>>
numpy.ndarray
# ndarray는 모든 원소의 데이터 타입이 같은 배열이다.
# 파이썬 리스트에는 [1, 'a']처럼 다른 데이터 타입도 넣을 수 있다.
2) randint 메소드
# 인자에 x를 입력하면 0부터 x 미만까지의 수를 무작위로 반환한다.
a = np.random.randint(3)
a
>>
1
# 인자에 x,y를 입력하면 x 이상 y 미만의 수를 반환한다.
b = np.random.randint(4, 11)
>>
8
# 범위를 정할 때 직접 입력도 가능하다.
# size라는 인자를 활용해 원하는 크기로 만들 수 있다.
c = np.random.randint(low=3, high=10, size=(2,3))
c
>>
array([[3 8 6]
[6 3 4]])
# upper / lower bound를 배열로 설정해 각각의 범위를 따로 설정해 줄 수 있다.
# 이것을 쓰면 size를 입력하지 않아도 내가 원하는 크기로 만들 수 있다.
d = np.random.randint([1, 6, 11], [5, 10, 15])
d
>>
array([1, 9, 12])
# 첫 번째 원소 - 1 이상 5 미만
# 두 번째 원소 - 6 이상 10 미만
# 세 번째 원소 - 11 이상 15 미만
3) randn 메소드
# randn은 표준정규분포에서 샘플링한 수 중에서 무작위로 하나를 반환한다.
a = np.random.randn()
a
>>
-1.3808088579995796
# shape을 입력하면 해당 배열을 숫자로 채워준다.
# 반환된 b의 타입은 ndarray이다.
b = np.random.randn(3,3)
b
>>
array([[ 0.55735153 -1.95601948 -0.71974869]
[ 0.45122196 2.32436353 -0.32857802]
[-0.84054562 0.68703358 0.93896746]])
표준 정규분포(standard normal distribution)에서 샘플링한다는 것은, 배열에 있는 모든 숫자를 히스토그램이나 그래프로 그려보면 표준 정규분포 곡선을 그린다는 뜻이다.
4) random_sample 메소드
# random_sample은 0 이상 1 미만의 실수 중 하나를 균등분포에서 샘플링해 할당해준다.
a = np.random.random_sample()
a
>>
0.28998748563777266
# 사이즈를 정해주면 해당 개수만큼 만들어 배열에 담아준다.
b = np.random.random_sample(2)
b
>>
array([0.25077589, 0.96456571])
균등분포(uniform distribution)에서 샘플링한다는 것은, 배열에 있는 모든 숫자를 히스토그램이나 그래프로 그려보면 각 숫자가 나온 개수가 거의 같다는 뜻이다.
3), 4)번의 내용이 잘 이해가 가지 않는 사람나을 위해 그래프로 표시해보겠다.
import matplotlib.pyplot as plt # 이미지를 그릴 수 있게 해줌
%matplotlib inline # 주피터 내부에 그림을 띄움
# 만개씩 샘플링해서 저장해두기
a = np.random.random_sample(10000) # 균등분포
b = np.random.randn(10000) # 표준정규분포
# 히스토그램 그리기
plt.subplot(1,2,1) # 1행 2열 1번째
plt.hist(a) # a에 대한 히스토그램
plt.subplot(1,2,2) # 1행 2열 2번째
plt.hist(b) # b에 대한 히스토그램
plt.show() # 이미지 보기
3. 배열에 대한 연산
연산을 위해 배열 2개를 준비해보겠다.
a1 = np.random.randint(2, size=27).reshape(3,3,3)
a2 = np.random.randint(2, size=9).reshape(3,3)
a1
>>
array([[[1, 1, 0],
[0, 0, 1],
[0, 1, 1]],
[[0, 1, 0],
[0, 0, 0],
[0, 1, 1]],
[[1, 0, 1],
[0, 0, 0],
[1, 1, 0]]])
a2
>>
array([[0, 0, 1],
[0, 0, 0],
[1, 0, 1]])
연산은 여러 가지가 있지만, 기본적인 것 몇 가지만 살펴보겠다.
1) sum, mean
np.sum(a1) # a1 배열의 모든 원소의 합
>>
12
np.sum(a2) # a2 배열의 모든 원소의 합
>>
3
a1 + a2
>>
array([[[1, 1, 1],
[0, 0, 1],
[1, 1, 2]],
[[0, 1, 1],
[0, 0, 0],
[1, 1, 2]],
[[1, 0, 2],
[0, 0, 0],
[2, 1, 1]]])
np.sum(a1+a2) # a1 + a2 배열의 모든 원소의 합
>>
21
# 브로드캐스팅이 가능한 배열이므로 덧셈 연산이 가능함
# 12(a1의 sum) + 3(a2의 sum) * 3 = 21(a1+a2의 sum)
# 위와 같은 계산이 가능한 건
# a2의 차원을 늘리면서 a2의 shape이 (3,3,3)이 되므로
# a2를 a1에 3번 더한것과 같아지기 때문이다.
np.mean(a1+a2) # a1 + a2 배열의 원소의 평균
>>
0.7777777777777778
2) max, min
np.max(a1+a2) # a1 + a2 배열의 원소 중 가장 큰 값
>>
2
np.min(a1+a2) # a1 + a2 배열의 원소 중 가장 작은 값
>>
0
3) argmax, argmin
arr = np.array([1,6,3,7,2,9])
np.argmax(arr) # 가장 큰 값인 9의 인덱스 반환
>>
5
np.argmin(arr) # 가장 작은 값인 1의 인덱스 반환
>>
0
4) unique
arr = np.array([3,5,6,6,3,3,1])
np.unique(arr) # 중복값 빼고 반환
>>
array([1, 3, 5, 6])
data type
배열 안의 원소의 타입을 확인하고 싶을 때 dtype을 보면 된다.
a = np.array([1,2,3]) # 정수
b = np.array([1, 2, 3.]) # 정수 + 실수(3. == 3.0)
# a와 b 확인해보기
a
>>
array([1, 2, 3]) # 정수
b
>>
array([1., 2., 3.]) # 실수
# 데이터 타입 확인해보기
a.dtype # a는 모두 정수이기 때문에
>>
dtype('int32') # 정수타입이라고 나오고
b.dtype # b는 실수가 포함되어있기 때문에
>>
dtype('float64') # 실수타입으로 나온다.
# 선언할 때 dtype을 설정해줄 수 있다.
c = np.array([1, 2, 3.], dtype='int32') # b 선언에 dtype만 추가했을 뿐인데
c
>>
array([1, 2, 3]) # 결과가 완전 다름을 알 수 있다.
astype()으로 데이터의 타입을 바꿀 수 있다.
# a를 정수 >> 실수로 바꿔보기
a.astype('float32')
>>
array([1., 2., 3.], dtype=float32)
# 다시 a 확인해보기
a
>>
array([1, 2, 3])
# a의 데이터타입 확인해보기
a.dtype
>>
dtype('int32')
이것을 통해 astype을 쓸 때 원본의 dtype이 바뀌는 것이 아니라 복사본의 dtype이 바뀌는 것을 알 수 있다.
차원 바꾸기
reshape 부분에서 차원을 바꾸는 것이 잠깐 나오긴했지만, 차원을 바꿔주는 함수가 자체적으로 있다.
일단 차원을 바꿔볼 배열을 먼저 선언하겠다.
ar0 = np.arange(9).reshape(3,3)
ar0.shape
>>
(3, 3)
ar0
>>
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
차원을 바꿀 때 expand_dims라는 함수를 이용한다. 차원 바꾸기 대상과 0을 인자로 쓰면 높은 차원이 추가된다.
ar1 = np.expand_dims(ar0, 0)
ar1.shape
>>
(1, 3, 3)
ar1
>>
array([[[0, 1, 2],
[3, 4, 5],
[6, 7, 8]]])
# 3행 3열 매트릭스에 [ ] 하나가 추가되었다.
차원 바꾸기 대상과 -1을 인자로 쓰면 낮은 차원이 추가된다.
ar2 = np.expand_dims(ar0, -10)
ar2.shape
>>
(3, 3, 1)
ar2
>>
array([[[0],
[1],
[2]],
[[3],
[4],
[5]],
[[6],
[7],
[8]]])
# 3행 1열 매트릭스 3개가 [ ]로 둘러싸여있다.
너무 많은 내용들이 한꺼번에 머리에 들어가면 과부하가 걸리니, 차근차근 읽어보면서 이해해보고 직접 연습해보는 것이 가장 좋다!
'Deep Learning' 카테고리의 다른 글
matplotlib을 이용한 시각화 기초 (2) 이미지 (0) | 2021.10.26 |
---|---|
matplotlib을 이용한 시각화 기초 (1) 그래프 (0) | 2021.08.21 |
Tensor 이해하기 (0) | 2021.08.08 |