NumPy
NumPy(Numerical Python)는 고성능 수치 계산을 위한 핵심 라이브러리다. Python 리스트와 달리 동일한 타입의 원소를 연속된 메모리에 저장해 연산 속도가 훨씬 빠르다. Pandas, scikit-learn 등 대부분의 데이터 분석 라이브러리가 NumPy 배열을 기반으로 동작한다.
import numpy as np
1. ndarray — NumPy 배열
배열 생성
# 리스트 → ndarray 변환
arr = np.array([1, 2, 3, 4, 5])
arr2d = np.array([[1, 2, 3], [4, 5, 6]]) # 2차원
# 특수 배열 생성
np.zeros((3, 4)) # 0으로 채운 3×4 배열
np.ones((2, 3)) # 1으로 채운 2×3 배열
np.full((2, 2), 7) # 7으로 채운 2×2 배열
np.eye(3) # 3×3 단위행렬 (대각선=1, 나머지=0)
# 범위 배열
np.arange(0, 10, 2) # [0, 2, 4, 6, 8] — 파이썬 range와 유사
np.linspace(0, 1, 5) # [0.0, 0.25, 0.5, 0.75, 1.0] — 균등 간격 5개
np.logspace(0, 2, 3) # [1, 10, 100] — 로그 스케일 균등 간격
배열 속성
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr.shape # (2, 3) — (행, 열)
arr.ndim # 2 — 차원 수
arr.size # 6 — 전체 원소 수
arr.dtype # dtype('int64') — 원소 데이터 타입
⚠️ 단일 타입 규칙
NumPy 배열은 모든 원소가 같은 타입이어야 한다. 정수와 실수를 섞으면 자동으로 실수로 변환(업캐스팅)된다.
np.array([1, 2.5, 3]) # dtype float64 — 자동 변환
np.array([1, 2, 3], dtype=np.float32) # 명시적 타입 지정
2. 인덱싱 & 슬라이싱
기본 인덱싱
arr1d = np.array([10, 20, 30, 40, 50])
arr1d[0] # 10 (첫 번째)
arr1d[-1] # 50 (마지막)
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d[1, 2] # 6 — 1행 2열 (0-based)
arr2d[0] # [1, 2, 3] — 0번 행 전체
arr2d[:, 1] # [2, 5, 8] — 1번 열 전체
슬라이싱 — [start:stop:step]
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
arr[2:6] # [2, 3, 4, 5] — 인덱스 2~5
arr[::2] # [0, 2, 4, 6, 8] — 짝수 인덱스
arr[::-1] # 역순
# 2차원 슬라이싱
arr2d = np.arange(9).reshape(3, 3)
arr2d[0:2, 1:3] # 0~1행, 1~2열
Python 리스트와 다른 점: NumPy 슬라이싱은 원본의 뷰(view)를 반환한다. 슬라이스를 수정하면 원본도 바뀐다. 독립 복사본이 필요하면 .copy() 사용.
팬시 인덱싱 (Fancy Indexing)
arr = np.array([10, 20, 30, 40, 50])
# 정수 배열로 여러 원소 한 번에 선택
arr[[0, 2, 4]] # [10, 30, 50]
# 2차원
arr2d = np.arange(1, 10).reshape(3, 3)
arr2d[[0, 2], [0, 2]] # [1, 9] — (0,0), (2,2) 위치
불리언 인덱싱 (Boolean Indexing)
arr = np.array([1, 2, 3, 4, 5, 6])
# 조건에 맞는 원소만 선택
arr[arr > 3] # [4, 5, 6]
arr[arr % 2 == 0] # [2, 4, 6]
# 여러 조건 조합 — &(and), |(or), ~(not) 사용
arr[(arr > 2) & (arr < 5)] # [3, 4]
3. 연산
원소별 연산 (Element-wise)
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
a + b # [5, 7, 9]
a * b # [4, 10, 18]
a ** 2 # [1, 4, 9]
a + 10 # [11, 12, 13] — 스칼라 연산도 원소별 적용
브로드캐스팅 (Broadcasting)
크기가 다른 배열끼리 연산할 때 NumPy가 자동으로 크기를 맞춰주는 기능.
규칙: 뒤쪽 차원부터 비교해서 같거나 둘 중 하나가 1이면 브로드캐스팅 가능.
# (3,) + (3,1) → (3,3)
a = np.array([1, 2, 3]) # shape: (3,)
b = np.array([[10], [20], [30]]) # shape: (3,1)
a + b
# [[11, 12, 13],
# [21, 22, 23],
# [31, 32, 33]]
(3,) → (1,3) → (3,3)
(3,1) → (3,1) → (3,3)
행렬 곱셈 (Matrix Multiplication)
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
A @ B # 행렬 곱셈 (권장)
np.dot(A, B) # 동일
np.matmul(A, B) # 동일
# ⚠️ A * B 는 원소별 곱셈 (행렬 곱셈 아님!)
axis 개념
arr = np.array([[1, 2, 3],
[4, 5, 6]])
np.sum(arr) # 21 — 전체 합
np.sum(arr, axis=0) # [5, 7, 9] — 열 방향 합 (행을 기준으로 내려가며)
np.sum(arr, axis=1) # [6, 15] — 행 방향 합 (열을 기준으로 옆으로 가며)
axis 직관적 이해: axis=0은 행(rows)이 사라지는 방향, axis=1은 열(columns)이 사라지는 방향.
4. 통계
기본 통계 함수
arr = np.array([2, 4, 4, 4, 5, 5, 7, 9])
np.mean(arr) # 5.0 — 평균
np.median(arr) # 4.5 — 중앙값
np.var(arr) # 분산 (ddof=0, 모분산)
np.std(arr) # 표준편차 (ddof=0, 모표준편차)
# 표본 분산/표준편차 (n-1로 나눔)
np.var(arr, ddof=1)
np.std(arr, ddof=1)
ddof(자유도 보정): ddof=0 → 모집단 전체를 가진 경우(모분산). ddof=1 → 표본에서 추정하는 경우(표본분산). 수업에서 특별히 언급 없으면 보통 ddof=1이 통계적으로 올바른 선택.
최빈값 (Mode)
NumPy에 mode() 함수는 없어서 np.unique()로 구현한다.
arr = np.array([1, 2, 2, 3, 3, 3, 4])
values, counts = np.unique(arr, return_counts=True)
mode = values[np.argmax(counts)] # 3 — 가장 많이 나온 값
난수 생성 (np.random)
np.random.seed(42) # 재현 가능한 결과를 위한 시드 고정
np.random.rand(3, 4) # [0, 1) 균등분포 — shape (3,4)
np.random.randn(3, 4) # 표준정규분포 (평균0, 표준편차1)
np.random.normal(5, 2, (3, 4)) # 정규분포 (평균5, 표준편차2)
np.random.randint(1, 7, 10) # 1~6 정수 랜덤 10개 (주사위)
np.random.choice([1,2,3], 5) # 배열에서 5개 무작위 선택 (복원추출)
np.random.choice([1,2,3], 5, replace=False) # 비복원추출
# 권장 최신 방식 (재현성 더 좋음)
rng = np.random.default_rng(seed=42)
rng.integers(1, 7, size=10)
rng.choice(arr, size=5, replace=True)
5. 정렬
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6])
# np.sort(): 정렬된 새 배열 반환 (원본 유지!)
sorted_arr = np.sort(arr) # 오름차순
sorted_desc = np.sort(arr)[::-1] # 내림차순 (역순 슬라이싱)
# arr.sort(): 원본을 직접 정렬 (in-place)
arr.sort() # 원본이 바뀜!
# argsort(): 정렬된 순서의 인덱스 반환
indices = np.argsort(arr) # [1, 3, 6, 0, 2, 4, 7, 5] 같은 인덱스 배열
# 활용: 다른 배열을 같은 순서로 정렬할 때 유용
names = np.array(['c', 'a', 'b'])
scores = np.array([85, 92, 78])
names[np.argsort(scores)] # ['b', 'c', 'a'] — 점수 순으로 이름 정렬
np.sort() vs arr.sort() 차이: np.sort()는 새 배열을 반환하고 원본을 건드리지 않는다. arr.sort()는 원본을 수정한다. 원본 보존이 필요하면 np.sort() 사용.
6. 배열 병합 & 분할
원소 추가/삭제
arr = np.array([1, 2, 3, 4, 5])
# 삽입: np.insert(배열, 인덱스, 값)
np.insert(arr, 2, 99) # [1, 2, 99, 3, 4, 5] — 2번 위치에 99 삽입
# 삭제: np.delete(배열, 인덱스)
np.delete(arr, 2) # [1, 2, 4, 5] — 2번 인덱스 삭제
# 끝에 추가: np.append (새 배열 반환)
np.append(arr, [6, 7]) # [1, 2, 3, 4, 5, 6, 7]
배열 병합
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
# 세로 방향 쌓기 (행 추가)
np.vstack([a, b]) # shape: (4,2)
np.concatenate([a, b], axis=0) # 동일
# 가로 방향 쌓기 (열 추가)
np.hstack([a, b]) # shape: (2,4)
np.concatenate([a, b], axis=1) # 동일
# 1D 배열을 열로 쌓기
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
np.column_stack([x, y]) # [[1,4],[2,5],[3,6]] — shape (3,2)
배열 분할
arr = np.arange(12).reshape(4, 3)
# 세로 방향 분할 (행 기준)
np.vsplit(arr, 2) # 2개로 균등 분할 → [shape(2,3), shape(2,3)]
# 가로 방향 분할 (열 기준)
np.hsplit(arr, 3) # 3개로 균등 분할 → [shape(4,1), shape(4,1), shape(4,1)]
7. 형태 변경 (Reshape)
reshape
arr = np.arange(12) # [0,1,2,...,11] — shape (12,)
arr.reshape(3, 4) # shape (3,4)
arr.reshape(2, 2, 3) # shape (2,2,3)
arr.reshape(3, -1) # shape (3,4) — -1은 "자동 계산"
arr.reshape(-1) # 1차원으로 펼치기
-1 사용법: reshape에서 -1은 "나머지 크기를 자동으로 계산해줘"라는 의미. 전체 원소 수가 12일 때 reshape(3, -1)이면 열은 자동으로 4가 된다.
ravel vs flatten
arr2d = np.array([[1, 2, 3], [4, 5, 6]])
arr2d.ravel() # 1차원 변환 — 원본의 뷰(view), 수정 시 원본도 바뀜
arr2d.flatten() # 1차원 변환 — 새 배열(deep copy), 원본 유지
차원 추가/제거
arr = np.array([1, 2, 3]) # shape (3,)
# 차원 추가
np.expand_dims(arr, axis=0) # shape (1,3) — 행 방향 차원 추가
np.expand_dims(arr, axis=1) # shape (3,1) — 열 방향 차원 추가
arr[np.newaxis, :] # shape (1,3) — 동일
# 크기가 1인 차원 제거
arr2 = np.array([[[1, 2, 3]]]) # shape (1,1,3)
np.squeeze(arr2) # shape (3,) — 크기 1인 차원 모두 제거
전치 (Transpose)
arr = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2,3)
arr.T # shape (3,2) — 행↔열 교환
arr.transpose() # 동일
'SKN_AI_33 > Data_Analysis' 카테고리의 다른 글
| 05_EDA_Titanic실습 (0) | 2026.07.06 |
|---|---|
| 04_EDA (0) | 2026.07.06 |
| 03_Seaborn (0) | 2026.07.06 |
| 02_Pandas (0) | 2026.07.06 |
