일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 백준
- androidStudio
- Coroutine
- Codeforces
- 코드포스
- Hilt
- Kotlin
- 암호학
- TEST
- Coroutines
- android
- pandas
- Compose
- ProGuard
- MiTweet
- AWS
- boj
- architecture
- MyVoca
- livedata
- textfield
- Rxjava
- 코루틴
- activity
- Python
- GitHub
- relay
- 프로그래머스
- Gradle
- 쿠링
- Today
- Total
이동식 저장소
Numpy - Numerical Python 본문
Numpy는 수학 연산을 더 쉽고 빠르게 지원해 주는 Python 라이브러리입니다. Numpy는 내부의 많은 부분이 C로 작성되어 있기 때문에, Python 리스트 연산보다 빠른 속도를 자랑합니다. Python으로 회귀분석 등의 통계 작업을 하고자 한다면 Numpy를 꼭 알고 가도록 합시다. 저도 요즘 배우는 중입니다.
Numpy는 외부 라이브러리입니다. 따라서 pip를 통해 먼저 설치해야 합니다.
다음의 커맨드를 입력하여 Numpy를 설치합시다.
pip install numpy
위에서 썼듯이 외부 라이브러리인 Numpy를 사용하려면 먼저 import를 해야 합니다. 관례적으로 Numpy는 np라는 이름으로 줄여 씁니다.
import numpy as np
저 역시 numpy 대신 np로 사용하도록 하겠습니다.
Array
Numpy의 기본 데이터 타입은 array입니다. Python의 리스트를 사용하여 array를 만들어 보도록 합시다.
test_array = np.array(["1", "4", 5, 8], float) # no dynamic typing
간단하죠? 그런데 주석이 한 줄 적혀있네요.
Python의 리스트에는 정수, 실수, 문자열, container 등의 객체가 함께 들어갈 수 있습니다. 그러나 numpy의 array는 단일 타입만을 넣을 수 있습니다. 위에서 numpy의 대부분이 C로 구현되어 있다고 한 점 기억하시나요? C 언어의 배열이 다중 타입을 지원하지 않기 때문에 numpy의 array 역시 하나의 데이터 타입만을 저장할 수 있습니다.
위에서 만든 test_array에는 float 타입 데이터가 들어갑니다.
array를 만들었으니 이것저것 출력해 봅시다.
print(test_array)
print(type(test_array))
print(type(test_array[0]))
print(test_array.dtype) # same as above
실수형 데이터가 들어갔기 때문에 1.0, 4.0 등이 출력됩니다.
3, 4번째 줄의 float64는 '64비트 부동 소수점 실수'를 의미합니다. 이것의 의미는 컴퓨터 구조 과목에서 배울 수 있으며, 여기서는 자세히 다루지 않겠습니다.
Shape
array의 shape란, 배열의 크기와 비슷한 개념입니다. 예를 들어 3*2 배열이 있다면, 이 배열의 shape는 [3, 2]가 됩니다. 실제로 그러한지 출력해 봅시다.
print(test_array.shape) # shape of array
print(np.array([[1, 2], [3, 4], [5, 5]], str).shape)
test_array는 4칸짜리 1차원 배열이기 때문에 shape가 (4)입니다. 반면 코드에서 만든 새로운 array는 3*2의 2차원 배열이기 때문에 (3, 2)가 출력되는 것입니다. shape가 무엇인지 잘 기억합시다.
그런데 shape를 바꿀 수도 있습니다.
뭐라구요?
print(test_array.reshape(2, 2))
print(test_array.reshape(-1, 2)) # -1: fit to size
너무 놀라지는 마시고..
reshape 메소드를 호출하여 shape을 바꾼 array를 얻을 수 있습니다. 기존 array의 shape은 바뀌지 않습니다.
매개변수로는 원하는 shape을 넣으면 됩니다. 단, 원소의 개수는 동일하게 해 줘야 합니다. 따라서 5*4 array를 2*10으로 바꿀 수는 있지만, 3*7로는 바꿀 수 없습니다.
매개변수에 -1이 들어갈 경우, 나머지 매개변수 값과 array의 크기에 맞춰서 자동으로 값이 결정됩니다. 위에서 (2, 2)로 결정된 모습을 볼 수 있습니다.
reshape의 특수한 형태로 flatten이 있습니다. 단어의 뜻에서 알 수 있듯이, 다차원 배열을 일차원으로 바꿉니다.
# multi-dimensional -> one dimensional
test_array2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], int)
print(test_array2.flatten())
Indexing
array의 각 element에 접근하려면 index를 사용하면 됩니다. 인덱스를 표기하는 방법은 두 가지가 있습니다.
# indexing
print(test_array2[0][0])
print(test_array2[0, 0])
C나 Python에서 쓰는 것과 같은 방법이 있고, 다른 하나는 대괄호 하나에 모든 인덱스를 넣는 방법이 있습니다. 각자 편리한 방법으로 사용하면 됩니다. 그나저나 두 번째 방법은 어느 언어에서 쓰는 것 같던데..
Slicing
numpy의 array도 slice 연산을 지원합니다. Python 리스트의 slicing을 생각하시면 됩니다.
다만 한 가지 독특한 점은, 행과 열을 모두 자를 수 있다는 점입니다. 그러니까 대충 이런 겁니다.
예시를 통해 이해해 봅시다.
# slicing: [row, column]
# pair of start:end:step
print(test_array2[:, 2:])
print(test_array2[1, 1:3])
print(test_array2[1:3])
행과 열의 범위를 지정할 수 있다는 뜻으로 이해하시면 좋습니다.
step도 지정할 수 있습니다. 예시만 보고 간단하게 넘어갑시다.
Array를 만드는 다양한 방법
가장 기본적인 방법으로, Python 리스트를 이용하는 방법입니다.
print(np.array([1, 2, 3], int))
두 번째로 range()를 사용하는 방법입니다. 사용법은 파이썬의 range()와 같으며, step을 실수로 지정할 수도 있다는 점을 주의합시다.
print(np.arange(10))
print(np.arange(0, 20, 2).reshape(-1, 5))
print(np.arange(0, 10, 0.5)) # step = 0.5
0 또는 1로 초기화된 array를 얻을 수도 있습니다.
print(np.zeros(shape=(2, 5,), dtype=np.int16)) # gets array, value all zero
print(np.ones(shape=(3, 4,), dtype=np.int16)) # gets array, value all one
비어있는 array를 얻을 수도 있습니다. 이 방법을 사용하면 shape와 데이터 타입은 지정되지만, 초기화는 되지 않습니다.
print(np.empty(shape=(3, 5,), dtype=float))
초기화를 반드시 해 줍시다.
마지막으로, 선형대수에서 중요하게 다뤄지는 '대각 행렬'을 얻을 수 있습니다. 반드시 정사각행렬일 필요는 없습니다.
print(np.eye(3, 5))
print(np.eye(3, 5, k=2)) # start of 1: 2nd column
k값을 지정하여 1로 이루어진 대각선의 시작 위치를 지정할 수 있습니다.
Axis
행렬에는 여러 축이 있습니다. 예를 들어 2차원 행렬에는 '행'과 '열'이 있죠. Numpy에서는 이것을 axis라는 이름으로 부르며, 각각에 정수 인덱스를 붙입니다. 그림으로 쉽게 이해해 봅시다.
shape에서 먼저 오는 축부터 번호가 매겨진다는 점을 기억하도록 합시다.
Sum
Array의 합을 구할 수 있습니다. 단순히 모든 element의 합을 구할 수도 있고, 축을 지정하여 합을 구할 수도 있습니다. 예를 들어, 4*3 array에서 axis=1로 지정하면 각 행의 합이 반환됩니다.
test_array2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], int)
# calculate sum
print(test_array2.sum(axis=0)) # column
print(test_array2.sum(axis=1)) # row
모든 원소의 합을 구하려면 axis를 None으로 지정하거나, 또는 지정하지 않으면 됩니다.
Concat
Array 여러 개를 이어붙이는 함수입니다. 이때 가로 또는 세로 방향으로 이어붙일 수 있습니다.
axis 방향을 따라서 이어붙인다고 생각하면 됩니다.
연산
Array끼리 다양한 연산을 적용할 수 있습니다. 대표적으로 덧셈, 곱셈, 행렬곱(dot production)이 있습니다. 곱셈과 행렬곱은 다릅니다!! 주의하세요. 곱셈은 같은 위치에 있는 값끼리 곱하며, 행렬의 곱은 여러분이 아시는 그것 맞습니다.
# operations: only when operation is available
print(test_array2 + test_array2) # values at same place!!
print(test_array2 * test_array2)
print(test_array2.dot(test_array2)) # dot production
+, * 연산자를 이용하여 연산을 적용할 수 있습니다.
Broadcasting
shape가 다른 두 array를 더하려고 하면 어떻게 될까요? Error? Undefined Behavior?
결과는 "정상 작동"입니다. broadcasting이라는 기법 덕분입니다.
Broadcasting은 강력하지만, 사용자(코딩하는 우리)에게 "잘못된 결과"라는 오해를 줄 수도 있습니다. 정신 똑바로 차립시다. ㅋㅋ
비교 연산
Array의 값이 조건을 만족하는지 알고 싶을 때가 있습니다.
각 원소가 5보다 큰가?
Numpy에서는 연산자 하나로 조건을 검사할 수 있습니다. 부등호 < 또는 >를 쓰면 각 원소가 조건을 만족하는지 알 수 있습니다.
test_array3 = np.arange(10)
print(test_array3 > 5) # each element..
0부터 9까지의 정수(test_array3)에 대해 조건을 비교한 결과가 출력되었습니다. 원소가 조건을 만족한다면 True, 아니라면 False입니다.
Array의 [모든/적어도 하나의] 원소가 조건을 만족하는지 알고 싶다면 아래의 메소드를 사용합시다.
print(np.all(test_array3 < 5))
print(np.any(test_array3 < 5))
원소 각각이 아니라 Array 전체에 조건을 적용하기 때문에 값이 하나만 반환되었습니다.
Where
Array에서 어떤 조건을 만족하는 값을 찾을 수 있습니다. 독특한 점은, 값이 아니라 index를 찾는다는 점입니다.
print(np.where(test_array3 < 5)) # gets index that fits the condition
test_array3에서 조건을 만족하는 값의 인덱스가 반환되었습니다. 인덱스를 통해 값에 접근할 수 있도록 한 것입니다.
조건이 참일 때와 거짓일 때 반환할 값을 지정할 수도 있습니다.
print(np.where(test_array3 < 5, 0, 1)) # condition, if true, if false
이러면 리스트가 반환되네요.
최댓값과 최솟값
Array에서의 최댓값과 최솟값 인덱스를 찾을 수 있습니다. Numpy는 값 대신 인덱스를 찾도록 디자인했나 봅니다.
# find index of min/max value in array
print(np.argmin(test_array3), np.argmax(test_array3))
Fancy Index
Array의 원소에 접근하려면 인덱스가 필요합니다. Fancy Index는 array의 값을 인덱스로 사용하여 원소에 접근하는 방법입니다. 예시를 보면서 이해합시다.
# fancy index: gets value using array values as index
test_array4 = np.array([2, 4, 6, 8, 10])
test_array5 = np.array([0, 1, 2, 4, 1])
print(test_array4[test_array5])
print(test_array4.take(test_array5)) # same
test_array5의 값을 인덱스로 활용하여 test_array4의 값을 얻었습니다. 개인적으로는 take를 사용하는 두 번째 방법이 가독성이 더 좋다고 생각합니다만, 자신의 선호에 따라 사용하면 되겠습니다. Numpy가 인덱스에 신경을 많이 썼네요.
Numpy는 언뜻 보면 간단해 보입니다. 실제로도 간단합니다. 이게 기계학습과 무슨 상관이 있나 싶지만, 선형대수는 그 자체로 데이터 처리에서 매우 중요한 기초가 되므로 완벽하게 익혀 놓도록 합시다.
사실 저도 타이핑하면서 복습했습니다. ㅎ
'Secondary > Python' 카테고리의 다른 글
static method, class method, abstract method (0) | 2023.03.01 |
---|---|
Data Cleansing (0) | 2020.01.15 |
matplotlib (0) | 2020.01.12 |
Pandas (2) (0) | 2020.01.10 |
Pandas (1) (0) | 2020.01.09 |