NumPy
Numerical Python
Python의 수치 연산 라이브러리. 다차원 배열(ndarray)과 벡터 연산 제공. 데이터 과학의 기초.
Numerical Python
Python의 수치 연산 라이브러리. 다차원 배열(ndarray)과 벡터 연산 제공. 데이터 과학의 기초.
NumPy(Numerical Python)는 2005년 Travis Oliphant가 Numeric과 Numarray를 통합해 만든 Python 수치 컴퓨팅 라이브러리입니다. C로 구현된 ndarray(N-dimensional array)는 Python 리스트보다 50-100배 빠른 벡터 연산을 제공하며, 데이터 과학 생태계 전체의 기반입니다.
브로드캐스팅(Broadcasting)은 NumPy의 핵심 기능으로, 서로 다른 형상의 배열 간 연산을 자동으로 처리합니다. (3, 4) 배열과 (4,) 배열을 더하면 자동으로 행 방향으로 확장해 연산합니다. 명시적 반복문 없이 벡터화된 코드를 작성할 수 있어 성능과 가독성 모두 향상됩니다.
주요 기능으로 선형대수(np.linalg), 푸리에 변환(np.fft), 난수 생성(np.random), 통계 함수(mean, std, percentile)를 제공합니다. dtype으로 float32, float64, int64 등 메모리 효율적인 데이터 타입을 지정하고, reshape, transpose, concatenate로 배열 형상을 조작합니다.
Pandas DataFrame, TensorFlow/PyTorch 텐서, scikit-learn 입력은 모두 NumPy 배열과 호환됩니다. GPU 가속이 필요하면 CuPy(NVIDIA CUDA), JAX(Google TPU)가 NumPy API를 그대로 사용합니다.
import numpy as np
# 배열 생성
arr = np.array([1, 2, 3, 4, 5])
matrix = np.arange(12).reshape(3, 4) # 0~11을 3x4 행렬로
zeros = np.zeros((2, 3), dtype=np.float32)
ones = np.ones((3, 3))
identity = np.eye(4) # 4x4 단위행렬
print(f"Shape: {matrix.shape}, Dtype: {matrix.dtype}")
# Shape: (3, 4), Dtype: int64
# 브로드캐스팅 예제
a = np.array([[1], [2], [3]]) # (3, 1)
b = np.array([10, 20, 30, 40]) # (4,)
result = a + b # (3, 4) - 자동 확장
print(result)
# [[11 21 31 41]
# [12 22 32 42]
# [13 23 33 43]]
# 벡터화 연산 (반복문 대신)
data = np.random.randn(1000000)
# 느림: [x**2 for x in data]
squared = data ** 2 # 빠름: 벡터화 연산
# 인덱싱과 슬라이싱
arr = np.arange(10)
print(arr[2:7:2]) # [2 4 6] - start:stop:step
print(arr[arr > 5]) # [6 7 8 9] - 불리언 인덱싱
# 고급 인덱싱 (Fancy Indexing)
indices = np.array([1, 3, 5])
print(arr[indices]) # [1 3 5]
# 선형대수
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A @ B) # 행렬 곱셈
print(np.linalg.det(A)) # 행렬식: -2.0
print(np.linalg.inv(A)) # 역행렬
eigenvalues, eigenvectors = np.linalg.eig(A)
# 통계 함수
data = np.random.normal(100, 15, size=1000)
print(f"평균: {np.mean(data):.2f}")
print(f"표준편차: {np.std(data):.2f}")
print(f"중앙값: {np.median(data):.2f}")
print(f"95 퍼센타일: {np.percentile(data, 95):.2f}")
# 축(axis) 기반 연산
matrix = np.random.randint(0, 10, size=(3, 4))
print(f"열별 합계: {matrix.sum(axis=0)}") # (4,)
print(f"행별 합계: {matrix.sum(axis=1)}") # (3,)
# 배열 결합
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6]])
vertical = np.vstack([arr1, arr2]) # (3, 2)
horizontal = np.hstack([arr1, arr1]) # (2, 4)
시니어: "피처 전처리 로직인데, for문으로 100만 행 돌리니까 5분 걸려요. NumPy 벡터화하면 1초 이내로 줄일 수 있어요."
주니어: "브로드캐스팅이 뭔가요?"
시니어: "shape이 다른 배열 연산 시 자동으로 차원을 맞춰주는 거예요. (1000, 10) 배열에서 각 열의 평균을 빼려면 그냥 data - data.mean(axis=0) 하면 돼요."
면접관: "NumPy 성능 최적화 경험이 있나요?"
지원자: "반복문을 벡터화 연산으로 대체하고, dtype을 float64에서 float32로 줄여 메모리를 절반으로 줄였습니다. 큰 배열은 np.memmap으로 디스크 매핑해서 메모리 부족 문제를 해결했고, 연산 집약적인 부분은 Numba의 @jit 데코레이터로 JIT 컴파일했습니다."
리뷰어: "arr.copy() 빼먹으면 view 반환되어 원본 수정될 수 있어요. 슬라이싱 결과를 수정할 거면 명시적으로 copy() 호출하세요."
개발자: "아, 그래서 원본 데이터가 바뀌었군요. 바로 수정하겠습니다."
원인: 메모리 최적화 위해 무분별하게 dtype을 float32로 변경, 금융 모델에서 정밀도 손실 발생
영향: 거래 예측 모델 정확도 15% 하락, 실제 손실 발생
해결: 중요 컬럼은 float64 유지, 덜 중요한 피처만 float32 사용
교훈: dtype 변경 전 도메인별 정밀도 요구사항 검토, 검증 데이터셋으로 영향 테스트
원인: arr[::2] 슬라이싱이 view를 반환하는 것을 몰라 수정 시 원본도 변경됨
영향: 훈련 데이터가 변형되어 모델 재학습 필요, 디버깅에 이틀 소요
해결: .copy()로 명시적 복사 후 수정
교훈: NumPy 슬라이싱은 view 반환, 수정 전 항상 .copy() 고려
Q1. NumPy의 브로드캐스팅(Broadcasting)이란?
Q2. arr = np.arange(10); b = arr[2:5] 후 b[0] = 99를 하면?
Q3. NumPy 성능 최적화 방법으로 올바른 것은?
이 페이지에 오류가 있거나 추가하고 싶은 내용이 있다면 알려주세요!