NumPy 완전 정복
NumPy는 Python 과학 컴퓨팅의 핵심 라이브러리입니다. ndarray 기반 벡터 연산으로 순수 Python보다 수십~수백 배 빠릅니다.
설치와 임포트
pip install numpy
import numpy as np
ndarray 생성
# 리스트로 생성
a = np.array([1, 2, 3, 4, 5]) # 1D
b = np.array([[1, 2, 3], [4, 5, 6]]) # 2D
print(a.shape) # (5,)
print(b.shape) # (2, 3)
print(b.dtype) # int64
print(b.ndim) # 2
# 특수 배열
np.zeros((3, 4)) # 0으로 채운 3x4
np.ones((2, 3)) # 1로 채운 2x3
np.eye(4) # 4x4 단위행렬
np.full((3, 3), 7) # 7로 채운 3x3
# 범위 배열
np.arange(0, 10, 2) # [0 2 4 6 8]
np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1. ]
# 난수
np.random.seed(42)
np.random.rand(3, 3) # 균등분포 [0, 1)
np.random.randn(3, 3) # 표준정규분포
np.random.randint(1, 100, (3, 3)) # 정수 난수
인덱싱과 슬라이싱
a = np.array([10, 20, 30, 40, 50])
b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 기본 인덱싱
print(a[0]) # 10
print(a[-1]) # 50
print(b[1, 2]) # 6 (1행 2열)
# 슬라이싱
print(a[1:4]) # [20 30 40]
print(b[:, 1]) # [2 5 8] (전체 행의 1열)
print(b[0:2, 1:]) # [[2 3] [5 6]]
# 불리언 인덱싱 (조건 필터링)
a = np.array([10, 25, 3, 48, 12, 37])
mask = a > 20
print(a[mask]) # [25 48 37]
print(a[a > 20]) # 동일: [25 48 37]
# 팬시 인덱싱 (인덱스 배열)
idx = np.array([0, 2, 4])
print(a[idx]) # [10 3 12]
브로드캐스팅
# 스칼라 연산 (모든 원소에 적용)
a = np.array([1, 2, 3, 4, 5])
print(a * 2) # [2 4 6 8 10]
print(a ** 2) # [ 1 4 9 16 25]
print(a > 3) # [False False False True True]
# 배열 간 연산 (같은 shape → 원소별 연산)
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
print(x + y) # [5 7 9]
print(x * y) # [ 4 10 18]
# 브로드캐스팅: shape이 달라도 규칙에 따라 자동 확장
a = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3)
b = np.array([10, 20, 30]) # shape (3,) → (1, 3) → (2, 3)
print(a + b)
# [[11 22 33]
# [14 25 36]]
# 열 벡터 브로드캐스팅
col = np.array([[100], [200]]) # shape (2, 1) → (2, 3)
print(a + col)
# [[101 102 103]
# [204 205 206]]
벡터 연산
# 수학 함수
a = np.array([1, 4, 9, 16, 25], dtype=float)
print(np.sqrt(a)) # [1. 2. 3. 4. 5.]
print(np.log(a)) # 자연로그
print(np.exp(np.array([0, 1, 2]))) # [1. 2.718 7.389]
# 통계 함수
a = np.array([[1, 2, 3], [4, 5, 6]])
print(a.sum()) # 21 (전체)
print(a.sum(axis=0)) # [5 7 9] (열 방향 합)
print(a.sum(axis=1)) # [ 6 15] (행 방향 합)
print(a.mean()) # 3.5
print(a.std()) # 표준편차
print(a.min(), a.max()) # 1 6
print(a.argmin()) # 0 (최솟값 인덱스)
print(a.argmax()) # 5
# 정렬
a = np.array([3, 1, 4, 1, 5, 9, 2, 6])
print(np.sort(a)) # [1 1 2 3 4 5 6 9]
print(np.argsort(a)) # 정렬된 인덱스
선형대수
# 행렬 곱 (dot product)
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A @ B) # 행렬 곱 [[19 22] [43 50]]
print(np.dot(A, B)) # 동일
# 전치행렬
print(A.T) # [[1 3] [2 4]]
# 역행렬
print(np.linalg.inv(A))
# 행렬식 (determinant)
print(np.linalg.det(A)) # -2.0
# 고유값/고유벡터
eigenvalues, eigenvectors = np.linalg.eig(A)
# 연립방정식 풀기: Ax = b
b = np.array([5, 11])
x = np.linalg.solve(A, b) # x = [1. 2.]
# 내적 (벡터)
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])
print(np.dot(v1, v2)) # 32
print(np.linalg.norm(v1)) # 벡터 크기 (L2 norm)
배열 변환
a = np.arange(12)
# reshape — 총 원소 수 유지
b = a.reshape(3, 4) # (12,) → (3, 4)
c = a.reshape(2, 2, 3) # (12,) → (2, 2, 3)
d = a.reshape(-1, 3) # -1은 자동 계산 → (4, 3)
# flatten / ravel
print(b.flatten()) # 1D 복사본
print(b.ravel()) # 1D 뷰 (메모리 공유)
# 쌓기 (concatenate / stack)
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
np.concatenate([x, y]) # [1 2 3 4 5 6]
np.vstack([x, y]) # [[1 2 3] [4 5 6]]
np.hstack([x.reshape(-1,1), y.reshape(-1,1)]) # 열 방향 결합
# 분리
np.split(np.arange(9), 3) # [array([0,1,2]), array([3,4,5]), array([6,7,8])]
실전 예제 — 이미지 처리
# 이미지 = 픽셀값 배열
image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) # 100x100 RGB
# 그레이스케일 변환 (가중 평균)
gray = (0.299 * image[:,:,0] + 0.587 * image[:,:,1] + 0.114 * image[:,:,2]).astype(np.uint8)
# 밝기 조정 (클리핑)
bright = np.clip(image.astype(int) + 50, 0, 255).astype(np.uint8)
# 좌우 반전
flipped = image[:, ::-1, :]
정리
| 기능 | 방법 |
|---|---|
| 배열 생성 | np.array(), np.zeros(), np.arange() |
| 조건 필터링 | 불리언 인덱싱 a[a > 0] |
| 원소별 연산 | +, -, *, /, ** |
| 방향별 집계 | .sum(axis=0), .mean(axis=1) |
| 행렬 곱 | @ 연산자 또는 np.dot() |
| 형태 변환 | .reshape(), .flatten() |
NumPy의 핵심은 반복문 없이 배열 전체에 연산을 적용하는 벡터화(vectorization)입니다.