넘파이(Numpy)
넘파이(Numpy)는 파이썬에서 수학 및 과학 연산을 위한 라이브러리이다.
C언어로 구현되어 있어 행렬, 배열 처리를 빠르고 효율적으로 수행할 수 있다.
1. 설치하기
!pip install numpy
import numpy as np
2. Numpy 배열 특징
2-1. N차원 배열 (N-dimensional array)
np.array()
1️⃣ Python의 리스트, 튜플 등 배열과 유사한 시퀀스를 NumPy의 N차원 배열 객체로 변환하는 함수
2️⃣ Python의 리스트와는 달리 동일한 자료형을 가지는 원소들로 이루어져 있다.
3️⃣ 1차원, 2차원, 3차원 등 다양한 차원을 가질 수 있다.
4️⃣ 리스트와 달리 print 했을 때 행렬(벡터) 형태로 출력된다.
➡️ 이러한 배열을 ndarray라 함!
list1 = [1, 2, 3, 4]
print(list1)
print(type(list1)) # 타입: list
print(list1[0])
print(type(list1[0])) # 타입: int
list2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
print(list2)
print(type(list2)) # 타입: list
print(type(list2[0])) # 타입: list
# 1차원 배열
# [ 1 2 3 4 ]
ndarr1 = np.array([1, 2, 3, 4])
print(ndarr1)
print(type(ndarr1)) # 타입: ndarray
print(type(ndarr1[0])) # 1 # 타입: int
# 2차원 배열
# [ [1 2 3]
# [4 5 6] ]
ndarr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(ndarr2)
print(type(ndarr2)) # 타입: ndarray
print(type(ndarr2[0])) # [1, 2, 3] # 타입: ndarray
.tolist()
: ndarray를 리스트로 변환하는 메서드
list1 = [1, 2, 3, 4]
ndarr1 = np.array(list1) # 리스트 -> ndarray
print(list1) # [1 2 3 4]
print(type(ndarr1)) # ndarray
list2 = ndarr1.tolist() # ndarray -> 리스트
print(list2) # [1, 2, 3, 4]
print(type(list2)) # 리스트
2-2. ndarray 데이터 타입
# 리스트
list1 = [1, 3.14, 'Python', '😊', True]
print(list1)
print(type(list1)) # list
print(type(list1[0])) # int
print(type(list1[1])) # float
print(type(list1[2])) # str
print(type(list1[3])) # str
print(type(list1[4])) # bool
▶️ 파이썬의 리스트는 동일한 자료형 뿐만 아니라 서로 다른 자료형을 가지는 요소들로도 구성할 수 있다.
# ndarray (1)
ndarr1 = np.array([1, 2, 3, 4])
print(ndarr1)
print(type(ndarr1)) # ndarray
print(type(ndarr1[0])) # int64
print(type(ndarr1[1])) # int64
print(type(ndarr1[2])) # int64
print(type(ndarr1[3])) # int64
▶️ 하지만 ndarray는 파이썬의 리스트와 달리 서로 동일한 자료형의 요소들로만 구성이 가능하다.
만약 서로 다른 자료형의 요소들로 ndarray를 구성한다면 더 큰 범위의 타입으로 요소들이 변환된다.
데이터 타입 범위: Boolean < int < float < string
# ndarray (2)
ndarr2 = np.array([1, 2, 3.14, 4])
print(ndarr2) # [1. 2. 3.14 4.]
print(type(ndarr2)) # ndarray
print(type(ndarr2[0])) # float64
print(type(ndarr2[1])) # float64
print(type(ndarr2[2])) # float64
print(type(ndarr2[3])) # float64
▶️ 가장 큰 범위인 float 타입인 3.14로 인해 다른 int 타입 원소들이 float 타입으로 변환되는 걸 볼 수 있다.
# ndarray (3)
ndarr3 = np.array([1, 2, 3.14, True])
print(ndarr3) # [1. 2. 3.14 1.]
print(type(ndarr3)) # ndarray
print(type(ndarr3[0])) # float64
print(type(ndarr3[1])) # float64
print(type(ndarr3[2])) # float64
print(type(ndarr3[3])) # float64
▶️ 마찬가지로 3.14로 인해 다른 원소들이 float으로 변환되는 걸 볼 수 있다.
이때 T/F 값을 가지는 bool 타입의 원소는 True: 1, False: 0으로 변환된다.
# ndarray (4)
ndarr4 = np.array(['1', 2, 3.14, True])
print(ndarr4) # ['1' '2' '3.14' 'True']
print(type(ndarr4)) # ndarray
print(type(ndarr4[0])) # str_
print(type(ndarr4[1])) # str_
print(type(ndarr4[2])) # str_
print(type(ndarr4[3])) # str_
▶️ string 데이터 타입의 범위가 제일 크기 때문에 다른 원소들 전부 문자열로 변환된다.
dtype을 지정하여 변환될 데이터 타입을 정할 수 있다.
ndarr4 = np.array(['1', 2, 3.14, True], dtype=int)
print(ndarr4) # [1 2 3 1]
print(type(ndarr4)) # ndarray
print(type(ndarr4[0])) # int64, str -> int
print(type(ndarr4[1])) # int64
print(type(ndarr4[2])) # int64, float -> int
print(type(ndarr4[3])) # int64
✅ [참고] 이때 dtype의 경우 문자열도 dtype=int 지정을 통해 정수로 변환이 가능하다.
이는 자바스크립트의 이항 연산자 처리 '1' == 1 : True 판별하는 방식과 유사하다고 보면 된다.
따라서 문자열 타입인 ndarry[0] 값 ‘1’ → 정수형 1로 변환된 것이다.
즉, ndarray는 요소의 데이터 타입을 각각 다르게 입력해도 가장 큰 범위의 타입만 가질 수 있다.
2-3. ndarray 인덱싱과 슬라이싱
np.shape()
: ndarray의 행렬 형태가 몇 by 몇인지를 알려 준다.
ndarr1 = np.array(['🍓', '🍉', '🍌', '🍒', '🍑'])
print(ndarr1) # ['🍓' '🍉' '🍌' '🍒' '🍑']
print(ndarr1.shape) # (5,)
▶️ 1차원 행렬의 경우는 단순히 길이만 가지기 때문에 (5, )처럼 마지막 쉼표로 1차원임을 나타낸다.
따라서 ndarr1 은 ndarray 데이터인 ndarr1의 길이가 5인 1차원 배열이라고 해석할 수 있다.
📍 ndarray를 인덱싱 할 시 차원이 달라진다.
print(ndarr1[0]) # '🍓'
print(ndarr1[4]) # '🍑'
print(ndarr1[-1]) # '🍑'
print(ndarr1[-2]) # '🍒'
▶️ 인덱싱 결과는 1차원 배열(벡터)에서 대괄호가 하나 벗겨져서 출력되는 걸 볼 수 있다.
이를 스칼라 값이라 표현하고, 코랩 환경에서 프린트 시 따옴표가 사라지는 건 코랩 특징일 뿐 신경 쓰지 말 것!
type은 항상 type()을 찍어서 판단하자.
📍 ndarray 슬라이싱은 차원이 유지된다.
print(ndarr1[0:3])
print(ndarr1[2:])
print(ndarr1[:3])
📍 2차원 행렬
ndarr2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
print(ndarr2d)
print(ndarr2d.shape) # (3, 4) = (행, 열)
▶️ 2차원 행렬에서는 .shpae의 값은 생략되지 않고 (행 개수, 열 개수) 형태로 출력된다.
📍 [행 조건, 열 조건] 슬라이싱
# 0행, 모든 열 가져오기
print(ndarr2d[0, :])
print(ndarr2d[0,])
print(ndarr2d[0])
▶️ 모든 열을 가져올 때 열 조건을 생략해도 된다. 세 표현 전부 같은 출력 값을 가진다.
# 모든 행, 0열 가져오기
print(ndarr2d[:, 0])
# print(ndarr2d[,0]) # 오류, 불가능
▶️ 모든 행, 0열 가져오는 방법은 행 조건 생략할 수 없다.
📍 특정 인덱스 요소만 출력하는 방법
ndarr1 = np.array([10, 15, 2, 8, 20, 90, 85, 44, 23, 32])
idx = [2, 5, 9]
print(ndarr1[idx]) # [2 90 32]
📍 행 조건 또는 열 조건에 슬라이싱 주기
ndarr2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
print(ndarr2d[[0, 1], :])
▶️ 행 조건: 0행~1행, 열 조건: 모든 열
📍 boolean 인덱싱
ndarr1 = np.array(['🍓', '🍉', '🍌', '🍒', '🍑'])
sel = [True, False, True, True, False]
ndarr1[sel] # array(['🍓', '🍌', '🍒'], dtype='<U1')
▶️ sel 리스트의 각 값은 ndarr1 의 동일한 인덱스에 있는 요소를 선택할지 말지 결정한다.
True면 선택하고, False면 선택하지 않는다.
즉, True로 인덱싱 된 요소들만 추출한다.
📍 각 요소에 대해 조건을 평가하기
ndarr2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# 7보다 큰 원소는 True, 아닌 원소는 False로 변환되어서 출력됨
ndarr2d > 7
*️⃣ ndarr2d > 7
- 조건을 ndarr2d에 적용하면 boolean 배열 형태로 조건을 만족하는 요소는 True, 만족하지 않는 요소는 False 형태로 출력된다.
- 이 표현식은 Numpy 배열의 브로드캐스팅(broadcasting) 기능을 사용한다고 볼 수 있다.
- 불리언 인덱싱이나 조건 필터링에도 사용할 수 있다.
ndarr2d[ndarr2d > 7]
# array([ 8, 9, 10, 11, 12])
▶️ 필터링을 사용하여 True로 변환된 요소만 출력한 것을 볼 수 있다.
3. 행렬 연산
3-1. 기본 연산
a = np.array([[1, 2, 3],
[2, 3, 4]])
b = np.array([[3, 4, 5],
[1, 2, 3]])
a, b 행렬 모두 (2, 3) shape 행렬이다. 이때 이 두 행렬을 연산하면 다음과 같다.
# 행렬 덧셈
print(a + b)
# 행렬 뺄셈
print(a - b)
# 행렬 원소별 곱셈
print(a * b)
# 행렬 나눗셈
print(a / b)
▶️ 출력값
3-2. 행렬곱
- @ 연산자 또는 np.dot로 행렬곱을 수행할 수 있다.
- 첫 번째 행렬의 한 행과 두 번째 행렬의 한 열을 각각 곱한 뒤 더하는 과정으로 연산한다.
- 첫 번째 행렬의 열의 개수와 두 번째 행렬의 행의 개수가 같아야 한다.
- ex) A가 2x3 행렬, B가 3x2 행렬이면 수행 가능
- 수행 결과 행렬은 (첫 번째 배열의 행 개수 x 두 번째 행렬의 열 개수) 형태이다.
- ex) A(2x3) x B(3x2) → (2x2) 행렬
ndarr3 = np.array([[1, 2, 3],
[1, 2, 3],
[2, 3, 4]])
ndarr4 = np.array([[1, 2],
[3, 4],
[5, 6]])
print(ndarr3.shape) # (3, 3)
print(ndarr4.shape) # (3, 2)
▶️ ndarr3의 열 개수(3)와 ndarr4의 행 개수(3)가 같으므로 행렬 연산 가능
행렬 연산을 하게 되면 3 x 2 크기의 행렬 결과가 출력되는 것을 예상할 수 있음
print(ndarr3 @ ndarr4)
print(np.dot(ndarr3, ndarr4))
▶️ 둘 다 출력 값이 다음과 같다.
[[22 28]
[22 28]
[31 40]]
▶️ 연산 방법
4. 배열 생성
numpy.arange([start, ]stop, [step, ], dtype=None)
- 기본 문법
- start (선택, 기본값: 0): 배열 생성이 시작되는 값
- stop (필수): 배열 생성이 끝나는 값
- step (선택, 기본값: 1): 값 사이의 간격
- dtype (선택): 반환할 배열의 데이터 타입
1️⃣ 일정한 간격으로 숫자들을 생성한다.
2️⃣ 파이썬의 range() 함수와 유사하지만 일반 배열이 아닌 행렬(벡터) 형태의 ndarray를 반환한다.
3️⃣ arange도 마찬가지로 range처럼 stop의 마지막 값은 포함하지 않는다. ex) arange(1, 11) → 1 ~ 10
arr1 = range(1, 11)
print(arr1) # range(1, 11)
for i in arr1:
print(i, end=' ') # 1 2 3 4 5 6 7 8 9 10
▶️ range(1, 11)은 시퀀스 객체이므로 print 시 내용이 출력되지 않고 객체 정보가 출력된다.
내용을 출력하려면 for 반복문을 이용하여 각 요소를 출력하면 된다.
arr2 = np.arange(1, 11)
print(arr2) # [ 1 2 3 4 5 6 7 8 9 10]
print(type(arr2)) # <class 'numpy.ndarray'>
for i in arr2:
print(i, end=' ') # 1 2 3 4 5 6 7 8 9 10
▶️ arange(1, 11)은 print 시 배열의 내용을 출력한다. ndarray 형태이므로 행렬 형태로 출력된다.
arange 또한 for 반복문을 통해 각 요소에 접근할 수 있다.
5. 배열 정렬
numpy.sort(a, axis=-1, kind=None, order=None)
- 기본 문법
- a (필수): 정렬할 배열
- axis (선택, 기본값: 1):
- 정렬 기준 축을 지정
- 0: 열 방향
- 1: 행 방향
- None: 배열을 평탄화(flatten) 한 후 정렬합니다.
- kind (선택, 기본값: 'quicksort'):
- 정렬 알고리즘 선택
- 종류
- 'quicksort': 빠른 정렬(기본값)
- 'mergesort': 병합 정렬 (안정적)
- 'heapsort': 힙 정렬
- 'stable': 안정 정렬(병합 정렬 기반)
- order (선택, 기본값: None):
- 구조화 배열에서 정렬할 필드 지정
✅ 원본 배열을 변경하지 않고 정렬된 배열의 복사본을 반환한다.
5-1. 1차원 배열
ndarr1 = np.array([1, 10, 5, 7, 2, 4, 3, 6, 8, 9])
print(ndarr1) # [ 1 10 5 7 2 4 3 6 8 9]
print(np.sort(ndarr1)) # 오름차순 정렬 # [ 1 2 3 4 5 6 7 8 9 10]
print(ndarr1) # 변화 없음 # [ 1 10 5 7 2 4 3 6 8 9]
print(np.sort(ndarr1)[::-1]) # 내림차순 정렬 # [ 1 2 3 4 5 6 7 8 9 10]
▶️ sort()를 사용해도 ndarr1의 형태는 변함이 없다. 즉, 원본 배열의 형태를 변경하지 않는다는 것을 알 수 있다.
아무런 조건이 안 붙은 sort()는 오름차순을 뜻한다.
[::-1] 슬라이싱을 이용하여 역순(내림차순) 정렬, [start:stop:step] 형태에서 시작, 끝 값이 생략되었고 step이 -1로 설정되었기 때문에 내림차순 정렬이 가능하다.
5-2. 다차원 배열
ndarr2d = np.array([[11, 10, 12, 9],
[3, 1, 4, 2],
[5, 6, 7, 8]])
print(ndarr2d.shape) # (3, 4)
📍 axis=0 열 방향 오름차순
np.sort(ndarr2d, axis=0)
📍 axis=1 행 방향 오름차순
np.sort(ndarr2d, axis=1)
📍 슬라이싱을 이용한 정렬
- axis=1을 이용하여 행 방향 오름차순 정렬 후
- [:,::-1] 을 이용하여 각 행을 내림차순 정렬
- ::-1 (각 행의 열 조건): start, stop, step 값을 설정하여 역순으로 정렬
- : (행 조건): 모든 행 선택
np.sort(ndarr2d, axis=1)[:, ::-1]
📍 axis=-1
- 배열의 마지막 축(axis=-1)을 기준으로 정렬한다.
- ex) 예를 들어 2차원 배열에서는 axis=[0,1] 이기 때문에 배열의 마지막 축인 axis=-1은 axis[-1] 인덱스 값을 뜻한다. 즉, axis=1과 동일하다. 따라서 현재 예제에서는 행 방향 오름차순으로 값이 출력된다.
np.sort(ndarr2d, axis = -1)
'파이썬 > 파이썬 기초 문법' 카테고리의 다른 글
파이썬 파일 입출력 (0) | 2024.10.20 |
---|---|
파이썬 모듈 (2) | 2024.10.20 |
파이썬의 예외 처리 (0) | 2024.10.20 |
파이썬 스페셜 메서드 (4) | 2024.10.19 |
파이썬 상속 (2) | 2024.10.19 |