Python Numpy의 일반적이지 않은 Array, 변경, 차원, 복제, nan과 inf 타입에 대해 알아보겠습니다.
아래는 목차입니다.
특수한 NumPy Array - zeros, eye, arange, linspace, logspace
NumPy Array의 변경 - reshape, flatten, swapaxes, insert, delete
특수한 NumPy Array - zeros, eye, arange, linspace, logspace
모든 요소들이 모두 다 0 또는 1인 zeros/ones array를 생성하는 방법입니다.
dtype을 명시하지 않으면 기본적으로 실수형으로 생성되는 것을 확인할 수 있습니다.
>> a = np.zeros(3); a # zero array of length 3
array([0., 0., 0.])
>> a2 = np.zeros((2,3)); a2
array([[0., 0., 0.],
[0., 0., 0.]])
>> b = np.ones((2,3)); b
array([[1., 1., 1.],
[1., 1., 1.]])
>> b = np.ones((2,3), dtype=int); b
array([[1, 1, 1],
[1, 1, 1]])
대각 요소만 1이고 다른 요소들은 0인 단위 array를 만들기 위해 eye() 함수를 사용합니다.
(이렇게 대각원소가 1이고, 나머지는 모두 0인 n차 정방행렬을 단위행렬이라고 합니다.)
>> I = np.eye(3); I # 3x3 identity array
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
eye() 함수의 k 입력인자로 부 대각(subdiagonal) 요소들만 1인 array도 생성 가능합니다.
>> I1 = np.eye(3, k=-1); I1
array([[0., 0., 0.],
[1., 0., 0.],
[0., 1., 0.]])
기존 array의 shape(size)를 알지 못해도 같은 shape를 가지는 zero/ones array를 만들 수도 있습니다.
→ zeros_like(array), ones_like(array)
arange() : 등차수열을 만드는데 사용합니다.
>> x = np.arange(0, 5, 0.5); x # 0부터 5미만까지 공차가 0.5인 등차수열
array([0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5])
linspace(), logspace() : 일반 그래프나 log 그래프를 그리기 위한 수평축(x축) 변수값을 만드는데 사용합니다.
linspace(초항, 말항, 항의 개수)
>> x1 = np.linspace(0, 5, 5); x1 #0~5까지의 구간에서 5개 값으로 구성된 등차수열
array([0. , 1.25, 2.5 , 3.75, 5. ])
logspace에 대해 살펴보겠습니다.
logspace의 결과는 아래 그래프에서 y=-1 ~ 2 사이의 값을 5등분 했을 때 [-1, -0.25, 0.5, 1.25, 2]의 x 값입니다.
따라서 log10() 함수에 x2 값을 넣으면 y의 간격이 동일한 것을 확인할 수 있습니다.
>> x2 = np.logspace(-1, 2, 5); x2 # log10(x) 10^-1~10^2 까지의 구간에서 5개 값으로 구성된 수열
array([ 0.1 , 0.56234133, 3.16227766, 17.7827941 , 100. ])
>> np.log10(x2)
array([-1. , -0.25, 0.5 , 1.25, 2. ])
또한, logspace() 함수의 base 입력 인자를 따로 넣어주면 default 10이 아닌 다른 값으로 설정할 수 있습니다.
→ np.logspace(-1, 2, 5, base=2)
이 결과는 log2(x)의 2^-1 ~ 2^2 구간에서 등간격인 5개의 값으로 구성된 수열을 반환합니다.
참고로, linspace() 함수를 사용해서 logspace()를 구현할 수 있습니다.
NumPy Array의 변경 - reshape, flatten, swapaxes, insert, delete
reshape() : array를 다른 모양의 array로 바꾸기 위해 사용합니다.
>> a = np.arange(6); a
array([0, 1, 2, 3, 4, 5])
>> A1 = a.reshape(2,3); A1
array([[0, 1, 2],
[3, 4, 5]])
flatten() : 2D array를 1D array로 바꾸기 위해 사용합니다.
default는 행 우선으로 하며, order='F' 를 추가하면 열 우선으로 진행합니다.
>> a1 = A1.flatten(); a1
array([0, 1, 2, 3, 4, 5])
>> a2 = A1.flatten(order='F'); a2
array([0, 3, 1, 4, 2, 5])
>> at = np.transpose(A1); at
array([[0, 3],
[1, 4],
[2, 5]])
swapaxes() : 축을 바꿔주는 함수로 0(x), 1(y), 2(z) 축을 나타냅니다.
위 그림을 코드로 나타내면 아래와 같습니다. 3D array 참고
>> A = np.array([[[8,2],[3,7]],[[6,5],[1,4]]]); A
array([[[8, 2],
[3, 7]],
[[6, 5],
[1, 4]]])
>> A0 = A.swapaxes(0,1); A0
array([[[8, 2],
[6, 5]],
[[3, 7],
[1, 4]]])
insert() : array에 새로운 요소를 삽입할때 사용합니다.
insert(대상 array, 삽입 idx, 삽입 value)
axis=0 으로 하면 하나의 행을 삽입할 수도 있고, axis=1으로 하나의 열을 삽입할 수도 있습니다.
delete() : array에서 한 개 이상의 행이나 열을 제거할 때 사용합니다.
delete(대상 array, 삭제 idx)
axis를 사용해서 삭제하고자 하는 행, 열을 선택할 수 있습니다.
NumPy Array의 차원(Dimension) 변경
>> A = np.array([1,2]); A
array([1, 2])
>> A.shape, A.ndim
((2,), 1)
위 array는 행벡터일 것으로 생각되지만 shape가 (1,2)이 아니기 때문에 행 벡터인지 열 벡터인지 분명하지 않습니다. 이렇게 되면 행렬-벡터 연산이 수행될 때 dimension compatibility 조건에서 유연하게 행/열 벡터로 바뀐다는 장점이 있습니다만 상황에 따라서는 행/열 벡터를 분명하게 해야 할 때도 있습니다. 이럴 때 사용되는 NumPy의 상수가 바로 np.newaxis 입니다.
>> A_r = A[np.newaxis, :]; A_r # 행 벡터
array([[1, 2]])
>> A_r, A_r.shape, A_r.ndim
(array([[1, 2]]), (1, 2), 2)
>> A_c = A[:,np.newaxis]; A_c # 열 벡터
array([[1],
[2]])
>> A_c, A_c.shape, A_c.ndim
(array([[1],
[2]]),
(2, 1),2)
참고) np.newaxis 값을 print해보면 Nonde이 나옵니다. 따라서 np.newaxis 대신 None을 사용해도 결과는 같습니다.
np.repeat(반복 대상 array, 반복 횟수, 반복 기준 축) : 벡터를 복사 증식해서 행렬로 만들 때 사용합니다.
repeat 함수도 axis를 사용해서 행, 열로 복제가 가능합니다.
A_r : 1x2 , A_c : 2x1 인 두 array를 사용한 샘플코드입니다.
NumPy Array의 복제(Copy)
A array가 있을 때, 동일한 데이터와 형태를 가진 A1이라는 array를 생성하려고 합니다.
>> A1 = A
위와 같이 생성했을 때, A1의 값이 바뀌면 A의 값도 같이 바뀌는 것을 확인할 수 있습니다.
A와 A1의 id 값을 찍어보면 이름은 다르지만 동일한 객체이기 때문입니다.
>> A = np.arange(6);
>> A1 = A
>> id(A1), id(A)
(140622852480720, 140622852480720)
원래의 array를 보존하면서 값을 copy 하기 위해서는 ndarray.view() method를 사용합니다.
아래의 코드를 보면 A2가 변경되었지만 A의 값은 그대로 보존되었으며, id 값도 다른 것을 확인할 수 있습니다.
>> A
array([0, 1, 2, 3, 4, 5])
>> A2 = A.view() # array copy
>> print(A); print(A2)
[0 1 2 3 4 5]
[0 1 2 3 4 5]
>> A2.shape = 2,3; A2
array([[0, 1, 2],
[3, 4, 5]])
>> A
array([0, 1, 2, 3, 4, 5])
>> id(A), id(A2)
(140622852480720, 140622852408304)
하지만 여기에는 함정이 있습니다. 복제된 A2의 일부 값을 변경하면 A의 요소도 함께 바뀌게 됩니다.
이유는 A와 A2의 id는 다르지만, 각 요소들끼리의 id는 달라지지 않았기 때문입니다.
→ 이러한 복제를 얕은 복제(shallow copy) 라고 합니다.
그렇다면 어떤 경우에도 원본 array는 건들지 않으면서 copy하는 방법은 무엇일까요?
ndarray.copy() method를 이용하는 것입니다. 아래 결과를 보면 A3이 변경되어도 A의 값은 유지되는 것을 알 수 있습니다.
→ 이러한 복제를 깊은 복제(deep copy) 라고 합니다.
NumPy.nan과 NumPy.inf
np.nan : 없거나(missing) 유효하지 않은(invalid) 값을 나타냅니다.
np.inf : 무한대 숫자를 나타냅니다.
np.nan은 None과 비슷하면서도 다른 점이 있습니다. 차이점 중 하나는 None인지 검사를 할 때는 간단히 == 연산자를 사용하는 데 비해서 np.nan 인지 검사하기 위해서는 np.isnan() 이나 math.isnan()을 사용해야 합니다.
(주의) np.nan을 검사할 때 == 를 사용하면 아무런 경고 메시지도 없이 False를 반환합니다.
한편, np.inf는 무한대 숫자를 나타내는 float('inf')와 동일한 것으로 == 연산자를 사용해도 되고, np.isinf()나 math.isinf()를 사용할 수도 있습니다.
Q. 어떤 상황에서 np.nan이나 np.inf를 만나게 될까요?
A. Numpy를 사용하지 않고 아래와 같은 계산을 하려고 하면 Error가 발생합니다.
>> np.array([1])/0, np.array([0])/0, np.sqrt(-1)
(array([inf]), array([nan]), nan)
[심화] NumPy.nan_to_num()
np.nan, np.inf를 각각 0 이나 매우 큰 숫자로 대체하는 np.nan_to_num() 함수입니다.
>> x1 = np.nan_to_num(x, nan=0.0, posinf=None, neginf=None, copy=True)
x를 copy하며 np.nan과 np.inf를 0과 매우 큰 숫자로 대체한 결과를 x1에 저장합니다.
만약 다른 값으로 대체하고자 한다면 nan, posinf, neginf 에 값을 넣어주면 됩니다. copy를 0 또는 False로 하면 대체한 결과가 x에도 반영이 됩니다.
>> x = np.array([[np.nan, -np.inf, 3],[2, np.inf, None]], dtype=np.float16); x
array([[ nan, -inf, 3.],
[ 2., inf, nan]], dtype=float16)
>> x1 = np.nan_to_num(x); x1
array([[ 0.00e+00, -6.55e+04, 3.00e+00],
[ 2.00e+00, 6.55e+04, 0.00e+00]], dtype=float16)
만약, np.nan의 위치가 궁금하다면 np.where() 또는 np.argwhere(), np.isnan() 함수를 활용해서 찾을 수 있습니다.
np.where()와 np.argwhere()에 대한 내용은 링크를 참고해주세요.
>> x
array([[ nan, -inf, 3.],
[ 2., inf, nan]], dtype=float16)
>> np.where(np.isnan(x))
(array([0, 1]), array([0, 2]))
>> np.argwhere(np.isnan(x))
array([[0, 0],
[1, 2]])
참고하면 좋은 게시글
References
Python(파이썬)과 Matplotlib, Numpy, Pandas - 양원영, 고병천 외 3명
'프로그래밍 언어' 카테고리의 다른 글
Python pandas - DataFrame PDS 함수 (0) | 2023.01.29 |
---|---|
Python pandas - Series/DataFrame PDS (0) | 2023.01.27 |
Python Numpy - Array 1D, 2D, 3D (0) | 2023.01.22 |
Python NumPy (1) Array, Indexing, Sorting (0) | 2023.01.22 |
Java 8 람다(Lambda) / 스트림(Stream) / double colon(::) (0) | 2022.08.15 |
댓글