🧩 NumPy란?
NumPy는 다차원 배열 객체, 다양한 파생 객체(마스크 배열 및 행렬 등), 그리고 배열에 대한 빠른 연산을 위한 다양한 루틴을 제공하는 파이썬 라이브러리다. (NumPy 공식 문서 발췌)
NumPy는 수치 계산을 빠르고 편하게 하기 위한 파이썬 라이브러리다. 백준이나 SWEA 같은 알고리즘 대회 환경에서는 NumPy 라이브러리를 제공하지 않아 처음 보는 사람도 있을 텐데, 일반적으로 큰 행렬 및 배열 계산, 선형대수, 데이터 처리 및 분석, 머신러닝 등에서 자주 사용된다고 한다.
ndarray
파이썬의 list와는 별도로 NumPy는 ndarray 배열을 제공한다. (편의상 list는 리스트, ndarray는 배열로 지칭한다.)
import numpy as np
lst = [1, 2, 3]
arr = np.array(lst)
print(lst) # [1, 2, 3]
print(arr) # [1 2 3]
print(type(lst)) # <class 'list'>
print(type(arr)) # <class 'numpy.ndarray'>
리스트와 비슷해 보이지만, ndarray 배열은 원소별 계산이 가능하다는 아주 큰 차이점이 있다.
import numpy as np
lst = [1, 2, 3]
arr = np.array(lst)
print(lst * 2) # [1, 2, 3, 1, 2, 3]
print(arr * 2) # [2 4 6]
import numpy as np
lst1 = [1, 2, 3]
lst2 = [4, 5, 6]
arr1 = np.array(lst1)
arr2 = np.array(lst2)
print(lst1 + lst2) # [1, 2, 3, 4, 5, 6]
print(arr1 + arr2) # [5 7 9]
왜 리스트보다 빠를까?
반복문은 원소를 하나씩 꺼내면서 파이썬의 인터프리터가 일일이 작업한다.
import time
start_time = time.perf_counter()
# --------------------------------
lst = list(range(10**6))
result = []
for x in lst:
result.append(x * 2)
# --------------------------------
end_time = time.perf_counter()
elapsed_time = end_time - start_time
print(f"실행 시간: {elapsed_time:.6f} 초")
# 실행 시간: 0.056084 초
반면 NumPy는 내부적으로 C 언어 수준에서 배열 전체에 연산을 처리한다. 이러한 방식을 벡터화(Vectorization)라고 한다.
import numpy as np
import time
start_time = time.perf_counter()
# --------------------------------
arr = np.arange(10**6)
result = arr * 2
# --------------------------------
end_time = time.perf_counter()
elapsed_time = end_time - start_time
print(f"실행 시간: {elapsed_time:.6f} 초")
# 실행 시간: 0.003629 초
배열 만들기
np.array(): 리스트를 배열로 변환
import numpy as np
a = np.array([1, 2, 3])
b = np.array([[1, 2], [3, 4]])
print(a)
# [1 2 3]
print(b)
# [[1 2]
# [3 4]]
np.zeros(),np.ones(): 각각 0, 1로 초기화된 배열 생성
import numpy as np
a = np.zeros(5)
print(a)
# [0. 0. 0. 0. 0.]
b = np.ones((2, 3))
print(b)
# [[1. 1. 1.]
# [1. 1. 1.]]
# 기본 자료형은 float이므로 정수 배열이 필요하면 dtype 설정 필요
c = np.zeros(5, dtype=int)
print(c)
# [0 0 0 0 0]
np.arange(): 파이썬의range()와 비슷함
import numpy as np
a = np.arange(5)
print(a)
# [0 1 2 3 4]
b = np.arange(2, 10, 2)
print(b)
# [2 4 6 8]
np.linspace(): 일정한 간격으로 실수 구간을 나눔
import numpy as np
# 0부터 1까지를 포함해 총 5개로 나눔
a = np.linspace(0, 1, 5)
print(a)
# [0. 0.25 0.5 0.75 1. ]
차원과 모양
ndim: 차원 수shape: 각 차원의 크기size: 전체 원소 수
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])
print(a.ndim) # 2
print(a.shape) # (2, 3)
print(a.size) # 6
- 2차원 배열이므로
ndim == 2 shape == (2, 3)은 행 2개 열 3개를 의미한다.- 전체 원소 수가 6개이므로
size == 6
import numpy as np
a = np.array([[[1, 2, 3, 4], [5, 6, 7, 8]], [[11, 12, 13, 14], [15, 16, 17, 18]]])
print(a.ndim) # 3
print(a.shape) # (2, 2, 4)
print(a.size) # 16
- 3차원 배열이므로
ndim == 3 shape == (2, 2, 4): 2행 4열인 2차원 배열이 2개 있다.size == 16: 전체 원소 수가 16개

인덱싱과 슬라이싱
- 1차원 배열: 리스트와 거의 같다.
import numpy as np
a = np.array([10, 20, 30, 40, 50])
print(a[0]) # 10
print(a[-1]) # 50
print(a[1:4]) # [20 30 40]
- 2차원 배열: 리스트에서
a[0][1]처럼 썼던 것을 배열에서는a[0, 1]처럼 쓴다.
import numpy as np
a = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(a[0, 1]) # 2
print(a[1, 2]) # 6
- 행과 열 가져오기
import numpy as np
a = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(a[0]) # 첫 번째 행: [1 2 3]
print(a[:, 0]) # 첫 번째 열: [1 4 7]
print(a[1, :]) # 두 번째 행: [4 5 6]
- 부분 행렬 자르기
import numpy as np
a = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(a[0:2, 1:3])
# [[2 3]
# [5 6]]

배열 연산
여기서는 크기가 같은 배열끼리의 연산만 다룬다. 크기가 다른 배열끼리의 연산은 후술한다.
- NumPy 배열에서는 같은 위치의 원소끼리 계산한다.
import numpy as np
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
print(a + b) # [11 22 33]
print(a - b) # [ -9 -18 -27]
print(a * b) # [10 40 90]
print(b / a) # [10. 10. 10.]
- 스칼라 연산: 각 원소에 같은 연산이 적용된다.
import numpy as np
a = np.array([1, 2, 3])
print(a + 10) # [11 12 13]
print(a * 2) # [2 4 6]
print(a ** 2) # [1 4 9]
집계 함수
import numpy as np
a = np.array([1, 5, 2, 9, 3])
print(a.sum()) # 20
print(a.max()) # 9
print(a.min()) # 1
print(a.mean()) # 4.0
print(a.std()) # 2.8284271247461903 (표준편차)
print(a.argmax()) # 3 (최댓값 9의 인덱스)
print(a.argmin()) # 0 (최솟값 1의 인덱스)
# Numpy 함수 형태도 가능
np.sum(a)
np.max(a)
np.min(a)
축 연산
axis=0: 행을 없애고 열을 남긴다 (열별 연산)axis=1: 열을 없애고 행을 남긴다 (행별 연산)
import numpy as np
a = np.array([
[1, 2, 3],
[4, 5, 6]
])
# 행 방향으로 모아서 열별 계산 (열별 합)
print(a.sum(axis=0)) # [5 7 9]
# 열 방향으로 모아서 행별 계산 (행별 합)
print(a.sum(axis=1)) # [ 6 15]
# 열별 최댓값
print(a.max(axis=0)) # [4 5 6]
# 행별 최댓값
print(a.max(axis=1)) # [3 6]
💡 a[0, 1] → 0은 행이므로 행 방향으로 모아서 열별 계산 / 1은 열이므로 열 방향으로 모아서 행별 계산
배열 모양 바꾸기
- 1차원 배열 → 2차원 배열 변환
import numpy as np
a = np.arange(12)
print(a) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
# 3행 4열 배열로 변환
b = a.reshape(3, 4)
print(b)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# 전체 원소 수가 같아야 함
a.reshape(2, 6) # 가능
a.reshape(4, 3) # 가능
# a.reshape(5, 2) # 불가능: 원소 수가 10개여야 함
# ValueError: cannot reshape array of size 12 into shape (5,2)
-1를 사용해 한 차원의 크기 계산 자동 가능
import numpy as np
a = np.arange(12)
# 12개 원소를 3행으로 만들었으니 열은 자동으로 4개가 됨
print(a.reshape(3, -1))
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
조건 필터링
import numpy as np
a = np.array([1, 2, 3, 4, 5])
print(a > 3)
# [False False False True True]
print(a[a > 3])
# [4 5]
print(a[a % 2 == 0])
# [2 4]
b= np.array([[1, 2, 3], [4, 5, 6]])
print(b[b >= 3])
# [3 4 5 6]
조건에 따라 값 바꾸기
import numpy as np
a = np.array([1, 2, 3, 4, 5])
a[a % 2 == 0] = 0
print(a)
# [1 0 3 0 5]
# np.where(조건, 조건이 참일 때 값, 거짓일 때 값)
result = np.where(a == 0, 100, -1)
print(result)
# # [ -1 100 -1 100 -1]
크기가 다른 배열끼리의 연산
- 브로드캐스팅(Broadcasting): 크기가 다른 배열을 연산할 수 있도록 작은 배열을 확장
import numpy as np
a = np.array([
[1, 2, 3],
[4, 5, 6]
])
b = np.array([10, 20, 30])
print(a + b)
# [[11 22 33]
# [14 25 36]]
- 행마다 다른 값을 더하려면?
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([10, 20])
# 그냥 더하면 에러 발생
# print(a+b)
# ValueError: operands could not be broadcast together with shapes (2,3) (2,)
print(a + b.reshape(2, 1))
# [[11 12 13]
# [24 25 26]]
print(a + b[:, np.newaxis])

행렬 곱셈
import numpy as np
a = np.array([
[1, 2],
[3, 4]
])
b = np.array([
[5, 6],
[7, 8]
])
# 원소별 곱
print(a * b)
# [[ 5 12]
# [21 32]]
# 행렬 곱
print(a @ b)
# [[19 22]
# [43 50]]
np.matmul(a, b)
전치 행렬
import numpy as np
a = np.array([
[1, 2, 3],
[4, 5, 6]
])
print(a.T)
# [[1 4]
# [2 5]
# [3 6]]
행렬 복사
- 배열을 그냥 slice하면 슬라이싱이 원본을 공유한다.
import numpy as np
a = np.array([1, 2, 3, 4])
b = a[1:3]
b[0] = 999
print(a)
# [ 1 999 3 4]
# b를 수정했는데 원본 a도 바뀌어 버렸다.
- 원본과 완전히 분리하려면
copy()를 사용한다.
import numpy as np
a = np.array([1, 2, 3, 4])
b = a[1:3].copy()
b[0] = 999
print(a)
# [1 2 3 4]
자주 발생하는 실수
and,or대신&,|를 사용해야 함
import numpy as np
a = np.array([1, 2, 3, 4])
# print(a[a > 1 and a < 4]) # 오류
# print(a[a > 1 & a < 4]) # 오류
print(a[(a > 1) & (a < 4)]) # [2 3]
- 리스처럼
if 배열:사용 불가: 배열 전체가 참인지, 하나라도 참인지 모호하기 때문
import numpy as np
a = np.array([1, 2, 3])
#print(a if a else None) # 오류
print((a > 2).any()) # True (하나라도 참인 경우)
print((a > 0).all()) # True (모두 참인 경우)
- 자료형 오버플로우: NumPy의
int형은 큰 정수를 자동으로 처리하지 않음
import numpy as np
a = np.array([2_000_000_000], dtype=np.int32)
print(a * 2) # [-294967296] (오버플로우)
a = np.array([2_000_000_000], dtype=np.int64)
print(a * 2) # [4000000000]'CS > Python' 카테고리의 다른 글
| 파이썬으로 데이터 시각화 찍먹하기 (0) | 2026.08.06 |
|---|---|
| 파이썬의 Pandas 라이브러리에 대해 알아보기 (0) | 2026.08.06 |
| 파이썬으로 플로이드-워셜 알고리즘 구현하기 (0) | 2025.04.01 |
| 파이썬으로 MST 알고리즘 구현하기 (0) | 2025.04.01 |
| 파이썬으로 DFS, BFS 구현하기 (0) | 2025.04.01 |