배열 만들기
배열은 한 가지 타입의 값을 차원 수에 관계없이 격자 형태로 담아요.
| 작업 | 코드 |
|---|
| NumPy 임포트 | import numpy as np |
| 리스트로 만들기 | np.array([1, 2, 3]) |
| 중첩 리스트로 2D 배열 | np.array([[1, 2, 3], [4, 5, 6]]) |
| 0, 1, 지정한 값으로 채우기 | np.zeros((2, 3)), np.ones(4), np.full((2, 2), 7) |
| 간격 지정 범위 (끝 제외) | np.arange(0, 10, 2) # [0 2 4 6 8] |
| 균등 간격 n개 점 (끝 포함) | np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1.] |
| 단위 행렬 | np.eye(3) |
| 다른 배열과 같은 형태 | np.zeros_like(m), np.ones_like(m) |
| 원소 타입 지정 | np.array([1, 2, 3], dtype=np.float32) |
| 좌표 격자 | xx, yy = np.meshgrid(np.arange(3), np.arange(2)) |
배열 살펴보기
| 작업 | 코드 |
|---|
| shape: 각 차원의 크기 | m.shape # (3, 4) |
| 차원 수 | m.ndim # 2 |
| 원소 개수 | m.size # 12 |
| 원소 타입 | m.dtype # int64 |
| 타입 변환 | m.astype(np.float64) |
| 첫 번째 차원의 길이 | len(m) # 3 |
| Python 리스트로 되돌리기 | m.tolist() |
| 소수점 자릿수 줄여 출력 | np.set_printoptions(precision=3, suppress=True) |
인덱싱과 슬라이싱
슬라이스는 뷰라서 원본 배열과 메모리를 공유해요. 마스크와 인덱스 리스트는 복사본을 반환해요.
| 작업 | 코드 |
|---|
| 첫 번째와 마지막 원소 | a[0], a[-1] |
| start:stop 슬라이스 (stop 제외) | a[1:4] # [20 30 40] |
| 하나 걸러 원소, 역순 | a[::2], a[::-1] |
| 2D 배열의 원소 하나 | m[1, 2] # 6 |
| 행 하나, 열 하나 | m[0], m[:, 1] |
| 행과 열 블록 | m[:2, 1:3] |
| 조건에 맞는 원소 | a[a > 25] # [30 40 50] |
| 조건 조합 | a[(a > 15) & (a < 45)] |
| 위치 리스트로 선택 | a[[0, 2, 4]] |
| 조건에 맞는 위치 | np.flatnonzero(a > 25) # [2 3 4] |
| 원소별로 값 선택 | np.where(a > 25, a, 0) |
| 슬라이스 수정 전에 복사 | part = a[1:3].copy() |
| 마스크로 값 설정 | a[a > 40] = 0 |
연산과 브로드캐스팅
연산자는 원소별로 동작해요. 브로드캐스팅은 스칼라, 행, 열을 늘려 다른 배열의 형태에 맞춰요.
| 작업 | 코드 |
|---|
| 원소별 산술 연산 | x + y, x - y, x * y, x / y, x ** 2 |
| 스칼라와 연산 | x * 10 |
| 제곱근, 지수, 로그 | np.sqrt(x), np.exp(x), np.log(x) |
| 절댓값, 반올림 | np.abs(x - 5), np.round(x / 3, 2) |
| 값을 범위 안으로 제한 | np.clip(x, 2, 8) |
| 원소별 최댓값 | np.maximum(x, 3) |
| 내적 | x @ y # 37.0 |
| 열마다 중심화 (행 브로드캐스트) | m - m.mean(axis=0) |
| 행마다 스케일 조정 (열 브로드캐스트) | m / m.sum(axis=1, keepdims=True) |
| 실수를 안전하게 비교 | np.isclose(0.1 + 0.2, 0.3) # True, np.allclose(x, y) |
| 삼각함수와 pi | np.sin(np.pi / 2) # 1.0 |
축 방향 집계
axis=0은 행 방향(위에서 아래)으로 계산해서 열마다 결과 하나를 내요. axis=1은 열 방향(왼쪽에서 오른쪽)으로 계산해서 행마다 결과 하나를 내요.
| 작업 | 코드 |
|---|
| 모든 원소의 합계 | m.sum() # 21 |
| 열마다 결과 하나 | m.sum(axis=0) # [5 7 9] |
| 행마다 결과 하나 | m.sum(axis=1) # [ 6 15] |
| 평균과 표준편차 | m.mean(), m.std(), m.mean(axis=0) |
| 최솟값, 최댓값 | m.min(), m.max(axis=1) |
| 최댓값의 위치 | m.argmax(), m.argmax(axis=1) |
| 중앙값과 백분위수 | np.median(m), np.percentile(m, [25, 50, 75]) |
| 누적 합계 | np.cumsum(m, axis=1) |
| 모든 원소의 곱 | m.prod() # 720 |
| any, all, 개수 | (m > 4).any(), (m > 0).all(), np.count_nonzero(m > 2) |
형태 바꾸기와 합치기
reshape는 원소는 그대로 두고 형태만 바꾸므로 전체 크기가 같아야 해요.
| 작업 | 코드 |
|---|
| 새 형태 | a.reshape(2, 3) |
| 크기 하나는 NumPy가 계산 | a.reshape(3, -1), a.reshape(-1, 1) |
| 1D로 펼치기 | m.ravel(), m.flatten() |
| 전치 | m.T |
| 차원 추가 | a[:, np.newaxis], np.expand_dims(a, axis=0) |
| 크기가 1인 차원 제거 | np.squeeze(np.ones((1, 3, 1))) |
| 기존 축을 따라 연결 | np.concatenate([a, a]) |
| 행 또는 열로 쌓기 | np.vstack([m, m]), np.hstack([m, m]) |
| 1D 배열을 열로 | np.column_stack([a, a ** 2]) |
| 새 축을 따라 쌓기 | np.stack([a, a], axis=0) # shape (2, 6) |
| 여러 조각으로 나누기 | np.split(a, 3), np.array_split(a, 4) |
| 반복 또는 타일링 | np.repeat([1, 2], 2) # [1 1 2 2], np.tile([1, 2], 2) # [1 2 1 2] |
정렬, 검색, 고유값
| 작업 | 코드 |
|---|
| 정렬된 복사본 | np.sort(a) |
| 큰 값부터 | np.sort(a)[::-1] |
| 정렬 순서의 위치 | np.argsort(a) |
| 가장 큰 값 3개 | np.sort(a)[-3:] |
| 고유값과 개수 | values, counts = np.unique(a, return_counts=True) |
NumPy 2 이름 (unique_values는 정렬되지 않을 수 있음) | np.unique_values(a), np.unique_counts(a) |
| 각 원소가 리스트에 있는지 | np.isin(a, [1, 9]) |
| 정렬된 배열의 삽입 위치 | np.searchsorted([1, 3, 5, 7], 4) # 2 |
| 집합 연산 | np.intersect1d(a, [1, 2, 10]), np.union1d(a, [10]), np.setdiff1d(a, [1]) |
| 한 열 기준으로 행 정렬 | m[m[:, 1].argsort()] |
난수
시드를 지정한 Generator를 하나 만들고 모든 난수를 거기서 뽑으세요. 그러면 실행할 때마다 같은 결과가 나와요.
| 작업 | 코드 |
|---|
| 시드 지정 생성기 | rng = np.random.default_rng(42) |
| [0, 1) 범위의 실수 | rng.random(3) |
| 정수 (상한 제외) | rng.integers(1, 7, size=10) |
| 정규분포 | rng.normal(loc=0, scale=1, size=(2, 3)) |
| 범위 안의 균등분포 실수 | rng.uniform(5, 10, size=4) |
| 리스트에서 뽑기 | rng.choice(["a", "b", "c"], size=5) |
| 중복 없이 뽑기 | rng.choice(10, size=3, replace=False) |
| 가중치를 두고 뽑기 | rng.choice(["H", "T"], size=10, p=[0.7, 0.3]) |
| 섞은 복사본, 제자리 섞기 | rng.permutation(a), rng.shuffle(a) |
| 이항분포와 포아송분포 | rng.binomial(10, 0.5, size=5), rng.poisson(3, size=5) |
선형대수
| 작업 | 코드 |
|---|
| 행렬 곱 | A @ A |
| Ax = b 풀기 | np.linalg.solve(A, b) # [0.8 1.4] |
| 역행렬과 행렬식 | np.linalg.inv(A), np.linalg.det(A) |
| 벡터 길이 | np.linalg.norm(b) |
| 고윳값과 고유벡터 | values, vectors = np.linalg.eig(A) |
| 직선 피팅 | slope, intercept = np.polyfit(x, y, 1) |
| 외적 | np.outer([1, 2], [3, 4]) |
| 대각 성분과 대각합 | np.diag(A), np.trace(A) |
결측값과 특수값
np.nan은 결측 실수를 나타내요. 이 값과 연산하면 결과가 모두 nan이 되니 nan 함수나 마스크를 쓰세요.
| 작업 | 코드 |
|---|
| 결측값 | np.nan |
| 결측값 찾기 | np.isnan(v), np.isnan(v).sum() |
| 결측값 제거 | v[~np.isnan(v)] |
| 결측값과 무한대 바꾸기 | np.nan_to_num(v, nan=0.0, posinf=0.0) |
| 무한대와 유한값 확인 | np.inf, np.isfinite(v) |
| NaN을 건너뛰는 통계 | np.nanmean(v[:3]) # 2.0, np.nanmax(v) |
저장과 불러오기
| 작업 | 코드 |
|---|
| 배열 하나, 바이너리 파일 | np.save("data.npy", m), m = np.load("data.npy") |
| 여러 배열을 한 파일에 | np.savez("data.npz", x=x, y=y), np.load("data.npz")["x"] |
| CSV 텍스트 저장 | np.savetxt("data.csv", m, delimiter=",", fmt="%d") |
| CSV 텍스트 읽기 | np.loadtxt("data.csv", delimiter=",") |
NumPy 2: 예전 이름과 지금 쓰는 이름
NumPy 2에서 많은 별칭이 제거되어, 예전 튜토리얼에서 복사한 코드는 AttributeError로 실패할 수 있어요.
| 작업 | NumPy 2 | 예전 방식 |
|---|
| 결측값 | np.nan | np.NaN |
| 무한대 | np.inf | np.Inf |
| 64비트 실수 타입 | np.float64 | np.float_ |
| 곡선 아래 넓이 | np.trapezoid(y, x) | np.trapz(y, x) |
| 각 원소가 다른 배열에 있는지 | np.isin(a, b) | np.in1d(a, b) |
| 행 쌓기 | np.vstack([a, a]) | np.row_stack([a, a]) |
| 곱 | np.prod(a) | np.product(a) |
| 난수 | rng = np.random.default_rng(0)
rng.random(3) | np.random.seed(0)
np.random.rand(3) |
데이터 과학 코드의 기반이 되는 NumPy 호출을 작업별로 모았어요. 배열 만들기, 살펴보기, 인덱싱과 슬라이싱, 원소별 연산, 축 방향 집계, 형태 바꾸기와 합치기, 정렬과 검색, 난수 생성, 선형대수 풀이까지 담았어요. 각 행에는 바로 붙여 넣을 수 있는 짧은 코드와 무엇을 하는지 설명하는 한 줄이 있어요.
모든 내용은 Coddy에서 실행하는 버전인 NumPy 2 기준이고, 마지막 표에는 NumPy 2에서 제거된 이름과 대신 쓸 코드를 함께 정리했어요. 대부분의 예제는 a = np.array([3, 1, 4, 1, 5, 9, 2, 6])와 m = np.arange(12).reshape(3, 4)를 사용해요. Python 자체가 처음이라면 Python 치트시트와 Python 플레이그라운드부터 시작하고, pandas, Matplotlib, scikit-learn 치트시트도 함께 띄워 두세요.