NumPy는 고성능 다차원 배열 객체와 이를 활용한 수치 계산을 위한 핵심 라이브러리로, 데이터 과학 및 분석 작업의 기반이 됩니다. 주로 2차원 형태의 업무 데이터를 처리하는 데 최적화되어 있으며, 다음과 같이 임포트하여 사용합니다.
import numpy as np
print(np.__version__)
배열 생성과 데이터 형식
NumPy 배열(ndarray)은 동일한 데이터 타입을 가지는 연속적인 메모리 공간을 기반으로 하며, 이는 파이썬 내장 리스트와 구분되는 핵심 특징입니다. 리스트와 달리 모든 요소가 같은 형식이어야 하며, 혼합형 입력 시 형 변환 우선순위는 문자열 > 실수 > 정수 순입니다.
x = np.array([1, 2, 3])
배열 생성 시 다양한 옵션을 지정할 수 있습니다:
- dtype: 원소의 데이터 타입 (예:
np.float32) - copy: 원본 객체 복사 여부
- order: 메모리 저장 방향 ('C': 행 우선, 'F': 열 우선)
- ndmin: 최소 차원 수 지정
특수 배열 생성 함수
다양한 패턴의 배열을 효율적으로 생성할 수 있는 도구들이 제공됩니다.
1. 1로 채워진 배열: np.ones()
# 4행 3열의 정수 배열 생성
np.ones((4, 3), dtype=np.int8)
# 1차원 배열
np.ones(5)
2. 0으로 채워진 배열: np.zeros()
np.zeros((3, 2))
3. 특정 값으로 채우기: np.full()
np.full((2, 4), fill_value=7)
4. 단위 행렬 생성: np.eye()
# 3x3 단위 행렬
np.eye(3)
# 대각선 오프셋 적용
np.eye(4, k=1) # 위로 한 칸 이동
5. 등간격 수열 생성
np.linspace()은 시작값, 끝값, 개수를 기준으로 균등하게 분포된 값을 생성합니다.
np.linspace(0, 10, num=5) # [0, 2.5, 5, 7.5, 10]
np.linspace(0, 10, num=5, endpoint=False) # [0, 2, 4, 6, 8]
np.arange()은 시작, 종료, 간격(step)을 기반으로 배열을 만듭니다.
np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
6. 난수 기반 배열
확률 분포를 활용한 배열 생성도 가능합니다.
- 정수 난수:
np.random.randint(0, 100, size=(3,3)) - 표준 정규분포:
np.random.randn(2, 4) - 일반 정규분포:
np.random.normal(loc=175, scale=10, size=1000) - 0~1 사이 실수:
np.random.random((2, 3)) - 셔플 인덱스:
np.random.permutation(6)→ [3, 1, 5, 0, 4, 2]
배열 속성 확인
생성된 배열의 구조를 분석하기 위한 주요 속성들:
- ndim: 차원 수 (예: 2차원 배열 → 2)
- shape: 각 차원의 크기 튜플 (예: (3, 4))
- size: 전체 원소 수
- dtype: 데이터 타입
arr = np.array([[1, 2], [3, 4], [5, 6]])
print(arr.ndim) # 2
print(arr.shape) # (3, 2)
print(arr.size) # 6
인덱싱과 슬라이싱
다양한 방식으로 배열의 특정 요소에 접근할 수 있습니다.
# 기본 인덱싱
data = np.array([[10, 20, 30], [40, 50, 60]])
# 전통적 접근
print(data[1][2]) # 60
# NumPy 스타일
print(data[1, 2]) # 60 (권장)
# 불리언 마스킹
mask = data > 30
print(mask)
# [[False False False]
# [ True True True]]
print(data[mask]) # [40 50 60]
슬라이싱 (왼쪽 닫힘, 오른쪽 열림)
vec = np.arange(8)
print(vec[2:6]) # [2, 3, 4, 5]
mat = np.random.randint(1, 10, (4, 5))
# 행 슬라이싱
print(mat[1:3])
# 열 슬라이싱
print(mat[:, 1:3]) # 모든 행, 2~3번째 열
형태 변환: reshape()
총 원소 수가 유지되는 조건에서 배열의 형태를 재구성합니다.
a = np.arange(12)
b = a.reshape(3, 4)
print(b.shape) # (3, 4)
배열 결합 (Concatenation)
여러 배열을 하나로 연결할 수 있습니다.
A = np.ones((2, 3))
B = np.zeros((2, 3))
# 수직 결합 (axis=0)
np.concatenate([A, B], axis=0) # 결과: (4, 3)
# 수평 결합 (axis=1)
np.concatenate([A, B], axis=1) # 결과: (2, 6)
단축 함수도 제공됩니다:
np.vstack([A, B])→ 수직 결합np.hstack([A, B])→ 수평 결합
배열 분할 (Splitting)
배열을 여러 조각으로 나눌 수 있습니다.
X = np.arange(12).reshape(3, 4)
# 균등 분할
np.split(X, 2, axis=1) # 두 개의 (3,2) 배열 반환
# 지정 위치에서 분할
np.split(X, [1, 3], axis=1) # 세 부분으로 분할
편의 함수:
np.hsplit(X, 2)→ 수평 분할np.vsplit(X, 3)→ 수직 분할
복사와 참조
기본 할당은 참조만 생성하므로, 원본 배열이 변경됩니다. 독립적인 사본을 만들려면 copy()를 사용해야 합니다.
original = np.array([1, 2, 3])
view = original # 참조
clone = original.copy() # 독립 복사
view[0] = 99
print(original[0]) # 99 (변경됨)
print(clone[0]) # 1 (유지됨)