Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

NumPy

import numpy as np
data = [[1,2,3], [4,5,6], [7,8,9]]
data * 2

산술연산 수행을 위해서는 제어 구문이 필요합니다.

data2 = []
for row in data:
    data2.append([n*2 for n in row])
data2

행 선택은 쉽지만, 열 선택은 어렵습니다.

data2[0]
data2[0][0], data2[1][0], data2[2][0]
import array
x = array.array('d', [1, 2, 3])
x.append(4.5)
x
try:
    x.append('오')
except TypeError as e:
    print('TypeError:', e)
try:
    x - x
except TypeError as e:
    print('TypeError:', e)
import numpy as np
arr2d = np.array(data)
type(arr2d)
arr2d

데이터의 자료형

arr2d.dtype

NumPy 배열은 한 가지 자료형의 데이터만 저장할 수 있습니다.

np.array([1, '2', 3.14])

파이썬의 표준 자료구조보다 훨씬 빠르게 연산을 수행합니다.

arr = np.arange(1e7)
nums = arr.tolist()
%timeit [n*1.1 for n in nums]
%timeit arr * 1.1

1산술연산

산술연산이 기본적으로 원소별로 수행됩니다.

arr2d * 2
arr2d - arr2d

2색인과 슬라이스

행열 단위 접근이 가능합니다.

arr2d[0]
arr2d[:, 0]
arr2d[0:2]
arr2d[:, 0:2]
arr2d[1:, 1:]

3팬시 색인

data
data[0], data[2]
arr2d[[0, 2]]
arr2d[[2, 0]]
arr2d[[-1, -2]]
arr2d[:, [0,2]]
arr2d[:, [2, 0]]

4불리언 색인

arr = np.array([0, 1, 2, 3, -4, -5])
arr[[True, True, True, False, False, False]]
arr > 0
arr[arr > 0]

원소별 논리연산

~(arr > 0)
(arr > 0) & (arr < 3)
(arr <  0) | (arr > 1)

5배열 전치

arr2d
arr2d.T

6유니버설 함수

data = list(range(1, 11))
data
from math import sqrt
[sqrt(n) for n in data]
arr = np.array(data)
np.sqrt(arr)

이항 유니버설 함수

x = np.array([0, 1])
x[::-1]
np.maximum(x, x[::-1])

7벡터 단위 연산

data = [[1, -1], [-2, 2]]
data2 = []
for row in data:
    new_row = []
    for n in row:
        n2 = n if n > 0 else 0
        new_row.append(n2)
    data2.append(new_row)
    
data2
arr = np.array(data)
np.where(arr > 0, arr, 0)

7.1산술통계

arr = np.random.randn(3,2)
arr
arr.mean()
np.mean(arr)
arr.sum()
arr.sum(axis=0)
arr.sum(axis=1)

7.2집합 함수

arr = np.array([0, 1, 2, 3] * 2)
arr
np.unique(arr)
np.in1d(arr, [1, 2])
arr[np.in1d(arr, [1,2])]
x = np.array([0,1,2,3])
y = np.array([1,2,3,4])
np.intersect1d(x, y)
np.union1d(x, y)
np.setdiff1d(x, y)
np.setxor1d(x, y)

7.3선형대수

x = np.array([[1, 1], [2, 2]])
x
y = np.array([[1, -1], [-1, 1]])
y
x*y
np.dot(x, y)

행렬로 방정식 해 구하기

3x+6y5z=12x3y+2z=25xy+4z=103x+6y-5z = 12\\x-3y+2z=-2\\5x-y+4z=10
A = np.array([[3, 6, -5], [1, -3, 2], [5, -1, 4]])
B = np.array([12, -2, 10])
np.linalg.inv(A)
np.dot(np.linalg.inv(A), B)

8난수 생성

import random
random.randint(0, 10)
np.random.seed(0)
np.random.randint(0, 11, size=10)
np.random.randint(0, 11, size=(4,4))
%timeit [random.randint(0, 10) for _ in range(10**6)]
%timeit np.random.randint(0, 11, size=10**6)

배열 섞기

x = np.arange(10)
x
np.random.shuffle(x)
x
np.random.permutation(x)
x

난수 생성 확률 모델

np.random.rand(5)
np.random.randn(5)
np.random.normal(0, 1, size=5)
from scipy.stats import norm, uniform
from pandas import Series
%matplotlib inline
x = np.linspace(-3, 3, 100)
Series(norm.pdf(x), x).plot()
x = np.linspace(-1, 2, 100)
Series(uniform.pdf(x), x).plot()

9BLAS

import numpy as np

print(np.__config__.show())
Output
import numpy as np
import time

a = np.random.rand(2000, 2000)
b = np.random.rand(2000, 2000)

t = time.time()
c = a @ b
print(f"Time: {(time.time() - t) * 1000:.0f} ms")

10연산 속도

"""
파이썬 리스트 vs NumPy 배열 연산 속도 비교 스크립트

- 1차원: 원소별 덧셈
- 2차원(행렬): 원소별 덧셈 + 행렬 곱

실행 예시:
    python compare_list_numpy.py
"""

import time
import random

import numpy as np


def benchmark_1d(n=5_000_000):
    print(f"\n=== 1차원 벡터 원소별 덧셈: 길이 n = {n:,} ===")

    # 데이터 생성
    py_list_a = [random.random() for _ in range(n)]
    py_list_b = [random.random() for _ in range(n)]

    np_a = np.array(py_list_a)
    np_b = np.array(py_list_b)

    # 파이썬 리스트: for 루프
    start = time.perf_counter()
    py_result = [a + b for a, b in zip(py_list_a, py_list_b)]
    end = time.perf_counter()
    py_time = end - start
    print(f"Python 리스트 덧셈: {py_time:.4f}초")

    # NumPy: 벡터화 연산
    start = time.perf_counter()
    np_result = np_a + np_b
    end = time.perf_counter()
    np_time = end - start
    print(f"NumPy 배열 덧셈: {np_time:.4f}초")

    print(f"→ NumPy가 약 {py_time / np_time:,.1f}배 빠름 (1차원 벡터 덧셈)")


def benchmark_2d_elementwise(n=2000, m=2000):
    print(f"\n=== 2차원 행렬 원소별 덧셈: 크기 {n:,} x {m:,} ===")

    # 데이터 생성 (리스트의 리스트)
    py_mat_a = [[random.random() for _ in range(m)] for _ in range(n)]
    py_mat_b = [[random.random() for _ in range(m)] for _ in range(n)]

    np_a = np.array(py_mat_a)
    np_b = np.array(py_mat_b)

    # 파이썬 리스트: 이중 for 루프
    start = time.perf_counter()
    py_result = [
        [a + b for a, b in zip(row_a, row_b)]
        for row_a, row_b in zip(py_mat_a, py_mat_b)
    ]
    end = time.perf_counter()
    py_time = end - start
    print(f"Python 리스트 행렬 덧셈: {py_time:.4f}초")

    # NumPy: 벡터화 연산
    start = time.perf_counter()
    np_result = np_a + np_b
    end = time.perf_counter()
    np_time = end - start
    print(f"NumPy 배열 행렬 덧셈: {np_time:.4f}초")

    print(f"→ NumPy가 약 {py_time / np_time:,.1f}배 빠름 (행렬 원소별 덧셈)")


def benchmark_2d_matmul(n=800, k=800, m=800):
    """
    n x k · k x m 행렬 곱 연산 비교.
    순수 파이썬 3중 for 루프는 매우 느리므로 크기를 너무 크게 잡지 마세요.
    """
    print(f"\n=== 2차원 행렬 곱: ({n:,} x {k:,}) · ({k:,} x {m:,}) ===")

    # 리스트의 리스트로 행렬 생성
    py_a = [[random.random() for _ in range(k)] for _ in range(n)]
    py_b = [[random.random() for _ in range(m)] for _ in range(k)]

    np_a = np.array(py_a)
    np_b = np.array(py_b)

    # 파이썬 리스트: 3중 for 루프 (전형적인 O(n*k*m) 구현)
    start = time.perf_counter()
    py_c = [[0.0] * m for _ in range(n)]
    for i in range(n):
        for j in range(m):
            s = 0.0
            for t in range(k):
                s += py_a[i][t] * py_b[t][j]
            py_c[i][j] = s
    end = time.perf_counter()
    py_time = end - start
    print(f"Python 리스트 행렬 곱: {py_time:.4f}초")

    # NumPy: 고도로 최적화된 BLAS 루틴 사용
    start = time.perf_counter()
    np_c = np_a @ np_b   # 또는 np.dot(np_a, np_b)
    end = time.perf_counter()
    np_time = end - start
    print(f"NumPy 배열 행렬 곱: {np_time:.4f}초")

    print(f"→ NumPy가 약 {py_time / np_time:,.1f}배 빠름 (행렬 곱)")


if __name__ == "__main__":
    # 필요에 따라 크기를 조절해서 실험해보세요.
    benchmark_1d(n=5_000_000)          # 1D: 5백만 원소 벡터
    benchmark_2d_elementwise(2000, 2000)  # 2D: 2000 x 2000 행렬 원소별 덧셈
    benchmark_2d_matmul(800, 800, 800)    # 2D: 행렬 곱