모든 게시물은 macOS Monterey 12.0.1 버전을 기준으로 작성하였습니다.
부스트캠프 AI Tech 3기 예비 캠퍼를 위한 Pre-Course 강좌를 바탕으로 작성하였습니다.
Numerical Python (Numpy)
"어떻게 행렬과 매트릭스를 코드로 표현할 것인가" 에 대한 이야기다.
코드로 방정식을 표현하는 방법에 대한 고민으로 시작해보자.
다양한 Matrix 계산에 있어서 늘 이차원 리스트를 사용하는 것은 메모리적인
문제와 속도적인 측면의 문제가 있고, 이를 해결하기 위한 패키지가 Numpy다.
- 일반 리스트에 비해 빠르고, 메모리 효율적이다.
- 반복문 없이 데이터 배열에 대한 처리를 지원한다.
- 선형대수와 관련된 기능을 제공한다.
- alias는 대개 np를 활용한다. import numpy as np
- numpy는 하나의 데이터 타입만 넣을 수 있다.
- a = np.array([sequential data], dtype) 방식으로 생성한다.
리스트와 가장 큰 차이점으로는 dynamic typing을 지원하지 않는다는 것이다.
즉, [1, 0.5, 0.7] 처럼 int와 float가 같이 array에 들어갈 수 없다.
리스트에 비해 array가 메모리 측면의 효율성을 가짐을 확인하여 보자.
a의 첫 번째 값은 1이고,
b의 마지막 값도 1이므로
a[0] is b[-1] 은 TRUE다.
여기서 is는 메모리의 위치를 비교하는 구문이다.
파이썬 내장 모듈인
리스트는 그 속에 숫자값을
넣는 것이 아니라
숫자값(1)이 가지는
고유의 주소값을 넣는다.
얘들을 numpy로
만들어주면 마련된
메모리 공간에 숫자의
주소값이 아닌 숫자 자체를
차례대로 넣는 것이기에
a의 첫번째 1과
b의 마지막 1의
주소값은 다르다.
array에 6개의 원소가 존재한다. np.float32로 저장한다는 것은
문자 하나에 32비트 메모리 용량을 제공한다는 것이고, 이는 4바이트에 해당한다.
4바이트를 차지하는 문자가 6개 존재하므로 array는 24바이트를 차지한다.
np.int8로 저장한다는 것은 문자 하나에 8비트, 이는 1바이트에 해당한다.
문자가 6개 존재하므로 array는 6바이트를 차지한다.
np.float64로 저장한다는 것은 문자 하나에 64비트, 이는 8바이트에 해당한다.
문자가 6개 존재하므로 array는 48바이트를 차지한다.
Numpy Handling Shape
reshape를 자유자재로 사용할 수 있어야 하는데 다음 예를 생각해보자.
데이터 개수가 8개인 얘를 reshape 한 다음에 뒤를 ( ?, 2 )로 고정해두면
당연히 ?에는 4가 들어갈 것이다. 이런 식으로 -1을 사용할 수 있다.
오른쪽 예에서는 (2,2,2)이므로 데이터 개수가 8개다.
np.array(test_matrix).reshape(1,-1,2).shape의 결과는 (1,4,2)가 될 것이다.
다차원 array를 1차원으로 펴주는 flatten() 함수가 있다는 것을 알아두자.
Indexing & Slicing
리스트와 다르게 이차원 배열에서 [0,0] 표기법을 제공한다.
matrix로 받아들일 때, 앞이 row,뒤가 column이다.
비교적 심화적인 접근 두 가지다. 첫 번째는 모든 행의 마지막 열이니까
9부터 세로로 19, 29, 39, ... , 99가 나오는 것이고, 두 번째가 좀 색다르다.
arr[ :, ::2 ] 는 순서대로 시작점, 종점, step size다.
즉, '처음부터 두 칸씩 끝까지'이므로 0,2,4 열을 추출하는 것이다.
두 번째 사진의 아래 예에 대하여 생각해보자.
행이 3개니까 1행과 3행, 열이 5개니까 1열과 4열의 교집합을 추출하면 되겠다.
Creation Function
array를 생성하는 함수는 다양하다.
np.arange(30)은 0부터 29까지의 값을 가진 배열을 만든다.
다른 점은 리스트에서는 floating point 표시가 불가능하지만 어레이는 가능하다.
그 후에 reshape을 통해 만드는 것이 일반적인 방법이다.
zeros나 ones는 그렇다치고 empty는 사용한 경험이 없다.
memory가 주어지는데 비어있는 배열이 주어진다. 공간만 잡아주고 초기화를
시키지 않아서 이전에 썼던 메모리 공간을 가져오게 되면 기입하지도 않은 값이
이미 존재하는 것을 확인할 수도 있다.
eye 행렬은 identity 행렬과 다르게 시작점을 정할 수 있다.
Operation Functions
위와 같은 연산 메서드를 사용함에 있어 axis의 개념에 대한 이해를 하는 것이 좋다.
axis = 1로 sum을 하면 그 방향으로 합을 구하여 3개의 원소를 가질 것이고,
axis = 0로 sum을 하면 그 방향으로 합을 구하여 4개의 원소를 가질 것이다.
※ 매우 중요 ※
2차원과 3차원까지는 axis가 어느 방향으로 형성되는지 꼭 알아둬야 한다.
새롭게 생성된 축이 항상 axis 0 가 된다는 것을 알면 좋겠다.
또 기억해야 할 매우 중요한 개념은 Concatenate다.
중요하므로 사진 두 개를 붙이지 않고 나열한다.
왼쪽은 위 개념을 적용한 예시이고, 오른쪽이 중요하다.
a 행렬은 2x2 행렬인데 b 는 matrix 형태가 아닌 vector다. (2, )
둘을 concatenate 시키려면 (2,2)와 (2,1)을 만들기 위한 새로운 축을 만들어야 한다.
이를 위해 b.reshape(-1,2) 혹은 b = b[np.newaxis, :]를 이용한다.
Array Operations
위는 Element-wise Operation의 예시다.
아래는 Matrix Operation이다.
Broadcasting은 말 그대로 퍼져나가는 연산이다.
Comparison
np.where은 자주 사용되니 알아두자.
- a의 값중에서 0보다 크다라는 명제를 기준으로 참에는 3, 거짓에는 2를 넣는다.
- True인 애들의 인덱스를 반환하라
- 메모리 값이 존재하지 않는 NaN 찾기
- 발산하지 않는 애들이 True
※ 매우 중요 ※
설명이 없으면 나중에 이해되지 않을것 같아 적어두면
axis = 1 방향으로 최대값은 7, 88, 76이고, 그 index는 3, 1, 1이다.
axis = 0 방향으로 최소값은 1, 2, 3, 4이고, 그 index는 0, 0, 2, 2다.
그리고 값이 아닌 index가 나옴을 기억하자.
argsort()는 array 속 값을 작은 순서대로 그 index를 나열한다.
아래 사진에서는 역순으로 나온 것을 확인할 수 있다.
Boolean & Fancy Index
오른쪽 사진에 위치한 fancy index가 조금 새롭게 느껴진 내용인데
a라는 array가 있고 0,0,1,3,2,1은 index 역할을 하는 것이다.
단, index 역할을 할 b를 선언할 때 integer로 선언해야 함에 주의하자.
Numpy Data i/o (in & out)
넘파이 데이터도 파일화 시키거나 로드할 수 있다.