Numpy
1. Numerical Computing
- 소수의 전문가 (과학자, 엔지니어)를 위한 학문분야였음
- 효율성 / 편리성이 중요했음
- 넘파이도 초보자를 위한 파트는 아님
2. Numpy
- ndarray
- 속도가 빠르다
- stride 기법 도입하여 인덱싱, 슬라이싱 빠르게 만듦
- C로 만들어짐
- CPU 지원
- 전문가가 사용할 수 있게 미묘한 차이를 가진 메서드를 다양하게 지원
- BUT 오래전에 만들어서 GPU 지원을 안함
import numpy as np
3. Array
- 3차원 Array부터 Tensor라고 함
- cube도 3차원
4. ndarray
(1) np.array
1차원
a = np.array([1, '1']) # 파사드 방식(팩토리 메서드)
a # array(['1', '1'], dtype='<U11')
- 다른 데이터타입(숫자, 문자)을 입력하더라도 homogeneous이기 때문에 자동으로 문자 타입으로 변환
2차원
aa = np.array([[1,2], [3, 4]])
aa
# array([[1, 2],
# [3, 4]])
3차원
aa = np.array([[[1,2], [3, 4]], [[1,2], [3, 4]]])
aa
# array([[[1, 2],
# [3, 4]],
# [[1, 2],
# [3, 4]]])
- 앞에 있는 대괄호 개수로 차원을 판단할 수 있음
- 차원은 공백으로 구분하며, 데이터가 많을 때는 …으로 표현
(2) shape, dtype
aa.shape # (2, 2, 2)
aa.dtype # dtype('int32')
- aa.ndim : aa.shape의 개수로도 확인할 수 있음
- aa.size : aa.shape의 값을 다 곱해서도 확인할 수 있음
(3) 데이터 초기화
다음과 같이 다양한 방법으로 데이터를 생성해볼 수 있다.
b = np.ones((3,4)) # 1로 채우기
b = np.zeros((3,4)) # 0으로 채우기
b = np.full((3,4), 5) # 5로 채우기
c = np.ones_like(b)
# b의 shape처럼 만들어 1로 채움
np.eye(4, k=1)
np.identity(4)
np.tri(4)
np.arange(10) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
5. 인덱싱, 슬라이싱
큰 것부터 뽑는다고 생각하면 쉽다.
2차원
a = np.arange(25).reshape(5,5)
a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
a[0][1] # 1
a[0,1] # 1
a[0,:] # array([0, 1, 2, 3, 4])
3차원
b = np.arange(24).reshape(2,3,4)
b
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
b[1,1,2] # 18
- 한 개 이상의 :은 …으로 바꿀 수 있으며, 맨 끝에 있는 …은 생략이 가능하다.
b[1, :, :]
b[1, ...]
b[1]
# array([[12, 13, 14, 15],
# [16, 17, 18, 19],
# [20, 21, 22, 23]])
6. 차원 추가
None
ornp.newaxis
: 차원을 추가하는 용도로 만들어짐- 마지막
:
은 생략 가능
c = np.arange(25).reshape(5,5)
c[None, :].shape # (1, 5, 5)
c[:, None].shape # (5, 1, 5)
c[:,:, None].shape # (5, 5, 1)
7. elementwise
ndarray로는 같은 위치의 요소끼리 계산하는 것이 가능하다.
a = [1,2,3]
b = [4,5,6]
a+b # [1, 2, 3, 4, 5, 6]
x = np.array(a)
y = np.array(b)
x+y # array([5, 7, 9])
e = np.array([[1,2],[3,4]])
e*e
# array([[ 1, 4],
# [ 9, 16]])
행렬곱을 하려면 np.array 말고 np.mat로 생성하거나 @를 사용할 수 있다.
e@e
# array([[ 7, 10],
# [15, 22]])
8. Copy / View
- copy를 해놓으면 원본으로 돌아갈 수 있으므로 원본 백업 용도로 사용할 수 있다.
- 파이썬과 numpy에서의 copy 활용 방법이 약간 다르다.
shallow / deep | 파이썬 | 넘파이 |
---|---|---|
shallow copy | .copy() | .view() |
deep copy | copy.deepcopy() | .copy() |
copy가 필요한 상황 (파이썬)
a = [1,2,3]
b = a
a[0] = 100
b
# [100, 2, 3]
같은 것을 가르키게 되므로 mutable인 a를 변경할 시 b도 함께 변경된다.
copy가 필요한 상황 (넘파이)
x = np.array([1,2,3])
y = x
y[0] = 100
x
# array([100, 2, 3])
같은 것을 가르키게 되므로 mutable인 x를 변경할 시 y도 함께 변경된다.
copy 적용 (파이썬)
a = [1,2,3]
b = a.copy() # a[:]도 같은 결과를 도출함
a[0] = 100
b
# [1, 2, 3]
만약 a = [[1,2], [3,4]]
처럼 겹으로 쌓여있으면 위와 같이 했을 때 b는 변경되므로 deep copy가 필요할 것이다.
import copy
a = [[1,2], [3,4]]
b = copy.deepcopy(a)
a[0][0] = 100
b
# [[1, 2], [3, 4]]
deep copy를 적용하니 변경되지 않았다.
copy 적용 (numpy)
x = np.array([[1,2],[3,4]])
y = x.copy()
y[0,0] = 100
x
# array([[1, 2],
# [3, 4]])
numpy는 기본 copy가 deep copy이다. 파이썬의 copy처럼 사용하고 싶다면 view를 사용한다.
x = np.array([[1,2],[3,4]])
y = x.view()
y[0,0] = 100
x
# array([[100, 2],
# [ 3, 4]])
shallow copy가 되었기 때문에 y를 변경했을 때 x도 변경되었다.
9. Stride
- 데이터의 다음 데이터로 갈 때 필요한 메모리 크기
- stride를 이용하면 속도가 빨라진다.
- numpy는 기본적으로 일렬로 데이터를 표현하는데 stride를 이용해 N차원 표현이 가능하다.
- stride 개념에서 axis 개념이 도출된다.
a = np.arange(25)
a.strides # (4,)
b = a.reshape(5,5)
b.strides # (20, 4)
# 행은 20만큼, 열은 4만큼씩 +
# 스트라이드를 할당하는 것은 안됨
a.strides = (20,4)
10. Broadcasting
a = np.array([1,2,3])
b = np.array([1,2,3]).reshape(3,1)
a + b
# array([[2, 3, 4],
# [3, 4, 5],
# [4, 5, 6]])
11. Data Type
(1) Typecasting
array를 생성하면서 다음과 같이 데이터타입을 지정할 수 있다.
a = np.arange(1000, dtype=np.float64)
- 데이터타입 약자 확인 방법
- np.sctypeDict
각 데이터타입의 값 범위를 확인할 수 있는 여러 메서드 또한 존재한다.
# int8의 값 범위 확인
np.iinfo(np.int8)
# float64의 값 범위 확인
np.finfo(np.float64)
만약 기존에 이미 생성된 데이터의 데이터타입을 바꾸고자 한다면 as~메서드를 사용할 것이다. 예를 들어, float로 바꾸고자 한다면 np.asfarray()
를 사용할 수 있으며 가장 범용적으로 사용하는 것은 astype()
이다.
a = np.array([1,2,3])
b = np.asfarray(a)
c = a.astype('i8')
d = a.astype(np.int64)
(2) 사용자 정의 데이터타입
Structured Arrays
x = np.array([('Rex', 9, 81.0), ('Fido', 3, 27.0)],
dtype=[('name', 'U10'), ('age', 'i4'), ('weight', 'f4')])
위와 같은 예시에서, dtype 리스트의 각 튜플 안에는 첫번째에 이름, 두번째에 데이터타입이 들어가게 된다.
이렇게 생성한 데이터에 대해서는 Pandas와 유사한 방법으로 데이터 추출이 가능하다. (Pandas는 Numpy 기반으로 만들어졌다.)
x['age'] # array([9, 3])
단, attribute 방식은 불가능하다.
x.age # AttributeError
Record Arrays
recordarr = np.rec.array([(1, 2., 'Hello'), (2, 3., "World")],
dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')])
Record Arrays를 사용하게 될 경우, 두 가지 방식의 데이터 추출 모두 가능하다. 즉, Pandas에서도 Record Arrays를 사용했음을 알 수 있다.
recordarr['foo'] # array([1, 2])
recordarr.foo # array([1, 2])
12. Shape
(1) reshape
a = np.arange(25)
a.reshape(5,5) # 5 X 5
# 리턴이 있으므로 mutable 아님. a는 바뀌지 않았음
a.reshape(5,-1) # 5 X 5
전체 사이즈에 맞게 reshape해야 한다.
reshape에 하나의 값이 전달되었을 때 나머지 값은 -값으로 어떤 것을 넣더라도 자동으로 전체 사이즈에 맞게 reshape된다. scikit에서는 -1만 허용하므로 -1을 사용하는 것이 좋다.
(2) resize
a.resize(5,6, refcheck=False)
a.resize(5,4, refcheck=False)
# 여기서는 리턴 없음. a가 바로 변경됨
resize에 의해 새로 추가되는 값은 0으로 표현되며, 원래보다 사이즈를 줄일 경우에는 앞부분 값으로 잘리게 된다.
13. 쪼개기 / 붙이기
(1) split
x = np.arange(25).reshape(5,5)
y = np.arange(24).reshape(4,6)
np.vsplit(y, 2) # vertical split
np.split(y, 2, axis=0) # vertical split
np.hsplit(y, 2) # horizontal split
np.split(y, 2, axis=1) # horizontal split
np.vsplit(x, 2) # TypeError
np.hsplit(x, 2) # TypeError
# 홀수이기 때문에 등분이 안되었음
np.vsplit(x, (2,3))
# 2보다 작은 것, 2와 3사이, 3보다 큰 것으로 구간을 쪼개기
split 하고 나면 unpacking도 가능해진다.
a, b = np.vsplit(y, 2)
# a
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]])
# b
array([[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
(2) stack, concatenate, r_, c_
위의 unpacking한 결과를 활용할 것이다.
np.stack([a, b])
2차원 2개를 붙였는데 3차원 array가 된 것을 확인할 수 있다. 만약 vstack
, hstack
을 사용한다면 2차원 결과를 반환받을 수 있다. indexer 기반(판다스의 iloc, loc처럼)의 r_
, c_
를 사용할 수도 있다.
np.vstack([a, b])
np.r_[a,b] # vstack과 같은 결과를 반환
np.hstack([a, b])
np.c_[a,b] # hstack과 같은 결과를 반환
stack
대신에 concatenate를
사용할 수도 있다. concatenate는
stack과
다르게 바로 2차원 결과를 반환한다.
np.concatenate([a,b])
+ 기타 유용한 명령어
lookfor
# sum이 들어간 모든 것을 찾아줌
np.lookfor('sum')
scipy.lookfor('sum')
# numpy에서 제공하는 모든 것을 찾아줌
np.lookfor('*')
# sum에 대한 설명을 더 자세히 보고 싶을 때
np.info(np.sum)
Leave a comment