06_Numpy

01_넘파이(Numpy)_기초

chuuvelop 2025. 3. 10. 17:55
728x90
넘파이(Numpy)

선형대수학 벡터와 다차원 배열

머신러닝엔지니어의 홈그라운드

행렬이나 대규모 다차원 배열을 쉽게 처리할 수 있도록 지원하는 파이썬의 라이브러리

import numpy as np

넘파이 배열의 구조

  • 넘파이 모듈의 기본 배열은 ndarray(n-dimension array, 다차원 배열)

 

다차원배열 변환

  • np.array() : 다차원 배열을 만드는 함수
li = [1, 2, 3, 4]
li_arr = np.array(li)
li_arr
array([1, 2, 3, 4])

 

type(li_arr)
numpy.ndarray

 

# 튜플을 다차원배열로 변환
tu = (1, 2, 3, 4)
tu_arr = np.array(tu)
tu_arr
array([1, 2, 3, 4])

 

type(tu_arr)
numpy.ndarray
  • 배열도 mutable 의 특징을 가짐
    • 변수를 다른 변수에 복사한 뒤 데이터를 수정하면 모든 변수의 데이터가 수정됨
# mutable데이터이므로 co_arr를 수정해도 li_arr값이 바뀜
co_arr = li_arr
id(co_arr), id(li_arr)
(2199139204912, 2199139204912)

 

co_arr
array([1, 2, 3, 4])

 

co_arr[0] = 100
co_arr
array([100,   2,   3,   4])

 

li_arr
array([100,   2,   3,   4])

 

# 다차원 배열의 값을 복사해서 새로운 배열을 만들면
# 동일한 값을 가진 새로운 배열이 만들어지기 때문에 서로 다른 데이터가 됨
new_arr = np.array(li_arr)
# 따라서 서로 다른 id값을 가짐
id(new_arr), id(li_arr)
(2199139205008, 2199139204912)

 

id(new_arr), id(li_arr)
(2199139205008, 2199139204912)

 

new_arr[0] = 99
new_arr
array([99,  2,  3,  4])

 

li_arr
array([100,   2,   3,   4])

 

  • 파이썬 리스트는 다양한 자료형을 사용할 수 있지만 넘파이 배열은 같은 자료형만 넣을 수 있음
    • 다차원 배열을 생성할 때 자료형을 지정하지 않으면 내부의 원소를 보고 자동으로 추론해서 만듦
# float 자료형을 지정해서 다차원배열 만들기
fl_arr = np.array(li, dtype = float)
fl_arr
array([1., 2., 3., 4.])
# 판다스가 내부적으로 넘파이를 쓰기 때문에 명령어가 비슷한 경우가 많음
fl_arr.dtype
dtype('float64')

 

  • 2차원배열이란
    • 2개의 축을 가지는 배열(행, 열)
      • 두 개의 1차원 배열이 쌓여서 하나의 2차원 배열이 만들어짐
arr2d = np.array([[1, 2, 3], [4, 5, 6]])
# 각 축의 원소의 개수
arr2d.shape
(2, 3)

 

# 축의 개수
arr2d.ndim
2

 

arr2d.dtype
dtype('int32')

 

arr2d
array([[1, 2, 3],
       [4, 5, 6]])

 

# 다차원 배열을 1차원 배열로 조회
arr2d.flatten()
array([1, 2, 3, 4, 5, 6])

 

 

배열에서 원소 조회하기

# 1차원 배열
li_arr
array([100,   2,   3,   4])

 

li
[1, 2, 3, 4]

 

li[0]
1

 

# 다차원 배열에서 원소 조회
list2d = arr2d.tolist() # 배열을 리스트로 변환
list2d
[[1, 2, 3], [4, 5, 6]]

 

arr2d
array([[1, 2, 3],
       [4, 5, 6]])

 

 

# 리스트는 정수만 이용해서 원소 조회 가능
# 따라서 리스트 내 리스트의 원소를 조회할 때는 인덱싱으르 두 번 사용해야함
list2d[0][1]
2
list2d[0, 1]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[81], line 1
----> 1 list2d[0, 1]

TypeError: list indices must be integers or slices, not tuple

 

 

# 다차원 배열은 행과 열의 인덱스를 튜플로 조회할 수 있음
arr2d[0, 1]
2

 

arr2d[0]
array([1, 2, 3])

 

arr2d[0][1]
2

 

 

 

슬라이싱

arr1 = np.array([[0, 1, 2, 3], [4, 5, 6, 7]])
arr1
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])

 

arr1.shape
(2, 4)

 

# 첫 번째 행 전체
arr1[0, :]
array([0, 1, 2, 3])

 

# 두 번째 열 전체
arr1[:, 1]
array([1, 5])

 

# 두 번째 행의 두 번째 열부터 끝 열까지
arr1[1, 1:]
array([5, 6, 7])

 

# 각 행의 두 번째 열까지
arr1[:, :2]
array([[0, 1],
       [4, 5]])

 

 


연습문제

  1. 아래의 행렬에서 값 7을 인덱싱
  2. 아래의 행렬에서 값 14를 인덱싱
  3. 아래의 행렬에서 배열 [6, 7] 을 슬라이싱
  4. 아래의 행렬에서 배열 [7, 12] 를 슬라이싱
  5. 아래의 행렬에서 배열 [[3, 4], [8, 9]]를 슬라이싱
m = np.arange(15).reshape(3, -1)
m
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

 

 

# 1. 값 7을 인덱싱

m[1, 2]
7

 

# 2. 값 14를 인덱싱

m[2, 4]
14

 

# 3. 배열 [6, 7] 을 슬라이싱
m[1, 1:3]
m[1, (1,2)]
array([6, 7])

 

 

# 4. 배열 [7, 12] 를 슬라이싱
m[1:3, 2]
array([ 7, 12])

 

 

# 5. 배열 [[3, 4], [8, 9]]를 슬라이싱
m[:2, 3:]
array([[3, 4],
       [8, 9]])

 

 

  1. 아래의 행렬에서 배열 [8, 14]를 슬라이싱
  2. 아래의 행렬에서 배열 [[11, 12], [17, 18]]을 슬라이싱
  3. 아래의 행렬에서 배열[3, 9, 15, 21]을 슬라이싱
m = np.arange(1, 25).reshape(4, 2, 3)
# 4, 2, 3배열
m
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [10, 11, 12]],

       [[13, 14, 15],
        [16, 17, 18]],

       [[19, 20, 21],
        [22, 23, 24]]])

 

#1 배열 [8, 14]를 슬라이싱
m[1:3, 0, 1]
array([ 8, 14])

 

#2 배열 [[11, 12], [17, 18]]을 슬라이싱
m[1:3, 1, 1:3]
array([[11, 12],
       [17, 18]])

 

#3 배열[3, 9, 15, 21]을 슬라이싱
m[:, 0, 2]
array([ 3,  9, 15, 21])

 

 

파이썬 리스트와 넘파이 배열의 차이

  • 넘파이 배열은 배열끼리 연산이 가능하지만 파이썬 리스트는 값의 추가만 가능
  • 넘파이 배열은 숫자와의 연산도 가능하지만 파이썬 리스트는 불가능
    • 파이썬 리스트 : 리스트 요소를 반복하는 것은 가능
  • 사용 용도
    • 파이썬 리스트는 값을 추가하거나 제거하는 일에 사용
    • 넘파이 배열은 수치 계산이 많고 복잡하거나 다차원배열이 필요할 때 사용

 

 

전치 연산(transpose)

  • 2차원 배열의 행과 열을 바꾸는 연산
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr
array([[1, 2, 3],
       [4, 5, 6]])

 

arr.shape
(2, 3)

 

arr.T
array([[1, 4],
       [2, 5],
       [3, 6]])

 

arr.T.shape
(3, 2)

 

 

 

배열 크기 변형

# arange: 파이썬의 range와 같으나, 넘파이의 다차원 배열을 만들어줌
arr1 = np.arange(12)
arr1
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

 

# reshape(3, 4): 3, 4의 배열로 만들어줌(12개의 요소가 있으므로 빠지는 값이 있으면 안됨 3X4)
arr2 = arr1.reshape(3, 4)
arr2
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

 

 

  • 사용하는 원소의 수가 정해져 있기 때문에 reshape의 형태 원소 중 하나는 -1로 대체할 수 있음
arr1.reshape(3, -1)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

 

arr1.reshape(2, 2, -1)
array([[[ 0,  1,  2],
        [ 3,  4,  5]],

       [[ 6,  7,  8],
        [ 9, 10, 11]]])

 

arr1.reshape(2, -1, 2)
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[ 6,  7],
        [ 8,  9],
        [10, 11]]])

 

 

넘파이 함수의 특징

  • 벡터화 연산
    • 벡터와 행렬의 연산은 구성하는 원소별로 처리하는 것이 보통
    • 동일한 인덱스의 원소끼리 계산을 처리하는 것을 벡터화 연산이라고 함
  • 유니버설 함수(universal function)
    • 벡터화 연산을 지원하는 특별한 함수를 유니버설 함수라고 함
# 일반 파이썬 함수의 타입 확인
def test():
    pass
type(test)
function

 

# 유니버설 함수 확인
type(np.add)
numpy.ufunc

 

# 넘파이 모듈에는 유니버설 함수도 있고 일반 함수도 있음
type(np.sort)
numpy._ArrayFunctionDispatcher

 

list2d + list2d
[[1, 2, 3], [4, 5, 6], [1, 2, 3], [4, 5, 6]]

 

np.add(list2d, list2d)
array([[ 2,  4,  6],
       [ 8, 10, 12]])

 

 

 

벡터화 연산

벡터화 연산이 필요한 이유

  • data 리스트 내의 각 값에 2를 곱해야 하는 경우
# 리스트를 통한 풀이
data = list(range(10))
data
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

data * 2
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

ans = []

for i in data:
    ans.append(i * 2)

ans
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

 

# 넘파이를 통한 풀이
data = np.array(data)
data * 2
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

 

넘파이 벡터화 연산

  • 벡터화 연산은 비교 연산과 논리 연산을 포함한 모든 종류의 수학 연산에 대해 적용됨
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
2 * a + b
array([12, 24, 36])

 

# 넘파이 비교연산: 넘파이 연산에서는 각 요소가 2와 같은지 비교한다
a == 2
array([False,  True, False])

 

# 파이썬에서는 False만 반환
[1, 2, 3] == 2
False

 

b > 10
array([False,  True,  True])

 

(a == 2) & (b > 10)
array([False,  True, False])

 

a[a == 2]
array([2])

 

b[b > 10]
array([20, 30])

 

 

 

  • 배열끼리의 연산
arr1 = np.arange(6, 10)
arr1
array([6, 7, 8, 9])

 

arr2 = np.arange(10, 14)
arr2
array([10, 11, 12, 13])

 

# 두 개의 다차원 배열을 곱하면 동일한 인덱스의 원소끼리 곱셈한 결과인 배열이 반환됨
arr1 * arr2
array([ 60,  77,  96, 117])

 

# arr1에 차원 추가
new_arr1 = arr1.reshape(-1, 1)
new_arr1
array([[6],
       [7],
       [8],
       [9]])

 

new_arr1.shape
(4, 1)

 

# arr2에 차원 추가
new_arr2 = arr2.reshape(1, -1)
new_arr2
array([[10, 11, 12, 13]])

 

new_arr2.shape
(1, 4)

 

# 행렬 요소별 곱셈 연산
new_arr1 * new_arr2
array([[ 60,  66,  72,  78],
       [ 70,  77,  84,  91],
       [ 80,  88,  96, 104],
       [ 90,  99, 108, 117]])

 

# 행렬곱 연산
new_arr1 @ new_arr2
array([[ 60,  66,  72,  78],
       [ 70,  77,  84,  91],
       [ 80,  88,  96, 104],
       [ 90,  99, 108, 117]])

 

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

 

#  * 연산자는 요소별 곱셈을 수행
result = a * b
result
array([[ 5, 12],
       [21, 32]])

 

# @ 연산자는 행렬곱을 수행
a @ b
array([[19, 22],
       [43, 50]])

 

 

브로드캐스팅(broadcasting)

  • 배열끼리 연산을 하기 위해서는 두 배열의 크기가 같아야 함
  • 서로 다른 크기를 가진 배열의 사칙 연산을 하는 경우 넘파이에서는 크기가 작은 배열을 자동으로 반복 확장하여 크기가 큰 배열에 맞춤
new_arr2
array([[10, 11, 12, 13]])

 

new_arr1 + new_arr2
array([[16, 17, 18, 19],
       [17, 18, 19, 20],
       [18, 19, 20, 21],
       [19, 20, 21, 22]])

 

 

문제

  1. 아래의 배열에서 3의 배수를 찾기
  2. 아래의 배열에서 4로 나누면 1이 남는 수를 찾기
  3. 아래의 배열에서 3으로 나누면 나누어지고 4로 나누면 1이 남는 수를 찾기
x= np.arange(1, 21)
x
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
       18, 19, 20])

 

# 3의 배수를 찾기
x[x%3==0]
array([ 3,  6,  9, 12, 15, 18])

 

# 4로 나누면 1이 남는 수를 찾기
x[x%4==1]
array([ 1,  5,  9, 13, 17])

 

# 3으로 나누면 나누어지고 4로 나누면 1이 남는 수를 찾기
x[(x%3==0) & (x%4==1)]
array([9])

 

 

축에 따른 연산

  • 동일한 축에 있는 원소들을 계산하기 위해서 필요함

 

1차원배열

arr1 = np.array([1, 2, 3, 4])
arr1
array([1, 2, 3, 4])

 

# 합계
np.sum(arr1)
10

 

arr1.sum()
10

 

# 최댓값
arr1.max()
4

 

np.max(arr1)
4

 

# 최솟값
arr1.min()
1

 

np.min(arr1)
1

 

# 최솟값의 위치
arr1.argmin()
0

 

np.argmin(arr1)
0

 

# 최댓값의 위치
arr1.argmax()
3

 

np.argmax(arr1)
3

 

#memo 어느 컬럼의 값이 최댓값으로 관측이 되는지 (최댓값의 위치)가 중요. 이럴 때 argmax를 사용

 

# 평균
arr1.mean()
2.5

 

np.mean(arr1)
2.5

 

# 중앙값 arr1.median()는 안됨
np.median(arr1)
2.5

 

 

2차원 배열

arr2 = arr1.reshape(2, 2)
arr2
array([[1, 2],
       [3, 4]])
# 모든 원소를 더한 합
np.sum(arr2)
10

 

# 수직방향으로 같은 열에 속한 원소만 합산
np.sum(arr2, axis = 0)
array([4, 6])

 

# 수평방향으로 같은 행에 속한 원소만 합산
np.sum(arr2, axis = 1)
array([3, 7])

 

arr3 = np.random.randint(0, 3, size = (2, 3))
arr3
array([[1, 0, 1],
       [2, 1, 2]])

 

np.sum(arr3, axis = 0)
array([3, 1, 3])

 

arr3.sum(axis = 0)
array([3, 1, 3])

 

np.sum(arr3, axis = 1)
array([2, 5])

 

 

3차원 배열

arr4 = np.arange(1, 9).reshape(2, 2, 2)
arr4
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])

 

# 모든 원소를 더한 합
np.sum(arr4)
36

 

# 두 개의 행렬의 동일한 인덱스의 원소를 합한 값
np.sum(arr4, axis = 0)
array([[ 6,  8],
       [10, 12]])

 

 

# 각 2차원 배열의 열의 합
np.sum(arr4, axis = 1)
array([[ 4,  6],
       [12, 14]])

 

# 각 2차원 배열의 행의 합
np.sum(arr4, axis = 2)
array([[ 3,  7],
       [11, 15]])

 

 

np.sum(arr4, axis = (0, 1))
array([16, 20])

 

np.sum(arr4, axis = (0, 2))
array([14, 22])

 

np.sum(arr4, axis = (1, 2))
array([10, 26])

 

 

arr5 = np.random.randint(0, 3, size = (2, 3, 4))
arr5
array([[[1, 2, 0, 0],
        [0, 0, 1, 2],
        [0, 1, 2, 2]],

       [[0, 1, 1, 1],
        [0, 1, 0, 0],
        [2, 1, 1, 1]]])

 

np.sum(arr5, axis = 0)
array([[1, 3, 1, 1],
       [0, 1, 1, 2],
       [2, 2, 3, 3]])

 

np.sum(arr5, axis = 1)
# 메모: axis에 지정한 인덱스가 없어진다고 생각하면 됨
array([[1, 3, 3, 4],
       [2, 3, 2, 2]])

 

np.sum(arr5, axis = 2)
array([[3, 3, 5],
       [3, 1, 5]])

 

np.sum(arr5, axis = (0, 1))
array([3, 6, 5, 6])

 

 

np.sum(arr5, axis = (0, 2))
array([ 6,  4, 10])

 

 

np.sum(arr5, axis = (1, 2))
array([11,  9])

 

 


연습문제

  1. 전체의 최댓값
  2. 각 행의 합
  3. 각 행의 최댓값
  4. 각 열의 평균
  5. 각 열의 최솟값
x = np.random.random((5, 6))
x
array([[0.20038014, 0.33761304, 0.58716728, 0.80374347, 0.04548007,
        0.84502681],
       [0.60990788, 0.23293953, 0.38783746, 0.39304561, 0.40699699,
        0.2521242 ],
       [0.96355814, 0.74149859, 0.90786554, 0.99431617, 0.29592062,
        0.08274319],
       [0.00170032, 0.19394994, 0.7575594 , 0.71126109, 0.84484799,
        0.25433904],
       [0.97162765, 0.25708811, 0.94897902, 0.07004172, 0.09798152,
        0.31262803]])

 

# 전체의 최댓값
x.max()
0.9943161733732123

 

# 각 행의 합
np.sum(x, axis = 1)
x.sum(axis = 1)
array([2.8194108 , 2.28285167, 3.98590225, 2.76365778, 2.65834605])

 

 

# 각 행의 최댓값
np.max(x, axis = 1)
x.max(axis = 1)
array([0.84502681, 0.60990788, 0.99431617, 0.84484799, 0.97162765])

 

# 각 열의 평균
np.mean(x, axis = 0)
x.mean(axis = 0)
array([0.54943483, 0.35261784, 0.71788174, 0.59448161, 0.33824544,
       0.34937225])

 

# 각 열의 최솟값
np.min(x, axis = 0)
x.min(axis = 0)
array([0.00170032, 0.19394994, 0.38783746, 0.07004172, 0.04548007,
       0.08274319])

 


  1. 각 2차원 배열에서의 최댓값
  2. 각 2차원 배열에서의 행의 합
  3. 각 2차원 배열에서의 각 행의 최댓값
  4. 전체 열의 평균
  5. 각 2차원 배열에서의 열의 평균
  6. 각 2차원 배열에서의 각 열의 최솟값
data_3d = np.random.random((3, 5, 6))
data_3d

 

array([[[0.59962204, 0.75798068, 0.15908062, 0.09949244, 0.60570942,
         0.77421978],
        [0.76711999, 0.78082797, 0.65943262, 0.9854782 , 0.09253896,
         0.14591833],
        [0.94912589, 0.95910646, 0.71513689, 0.13548017, 0.06884772,
         0.87266148],
        [0.5842159 , 0.35157257, 0.91342156, 0.16606104, 0.38627379,
         0.92404421],
        [0.94783183, 0.27094533, 0.7573572 , 0.62587953, 0.14000422,
         0.91606323]],

       [[0.66863074, 0.94329112, 0.50910555, 0.11549423, 0.73525703,
         0.51919633],
        [0.1159518 , 0.04522405, 0.23273843, 0.50271341, 0.33305661,
         0.72748196],
        [0.35930582, 0.60336696, 0.56833567, 0.11338679, 0.74080759,
         0.56089899],
        [0.86964779, 0.64711402, 0.27213468, 0.59641798, 0.08601652,
         0.65814645],
        [0.36824712, 0.57657667, 0.36958634, 0.75077496, 0.45472241,
         0.04145205]],

       [[0.86177556, 0.45171653, 0.56784964, 0.23810024, 0.52237345,
         0.97351832],
        [0.6508929 , 0.67501394, 0.45822788, 0.04328172, 0.56626508,
         0.25245257],
        [0.65974149, 0.69265882, 0.17731165, 0.16655758, 0.91152876,
         0.54121063],
        [0.31211153, 0.92303648, 0.99591766, 0.7289974 , 0.77809139,
         0.73822501],
        [0.48305115, 0.56173461, 0.51174974, 0.66493751, 0.40835996,
         0.05475567]]])

 

# 각 2차원 배열에서의 최댓값
data_3d.max(axis = (1, 2))
array([0.9854782 , 0.94329112, 0.99591766])
# 각 2차원 배열에서의 행의 합
data_3d.sum(axis = 2)
array([[2.99610498, 3.43131607, 3.7003586 , 3.32558907, 3.65808135],
       [3.490975  , 1.95716625, 2.94610181, 3.12947744, 2.56135955],
       [3.61533374, 2.64613409, 3.14900893, 4.47637947, 2.68458864]])

 

# 각 2차원 배열에서의 각 행의 최댓값
data_3d.max(axis = 2)
array([[0.77421978, 0.9854782 , 0.95910646, 0.92404421, 0.94783183],
       [0.94329112, 0.72748196, 0.74080759, 0.86964779, 0.75077496],
       [0.97351832, 0.67501394, 0.91152876, 0.99591766, 0.66493751]])

 

 

# 전체 열의 평균
data_3d.mean(axis = (0, 1))
array([0.61315144, 0.61601108, 0.52449241, 0.39553688, 0.45532353,
       0.58001633])

 

 

# 각 2차원 배열에서의 열의 평균
data_3d.mean(axis = 1)
array([[0.76958313, 0.6240866 , 0.64088578, 0.40247827, 0.25867482,
        0.72658141],
       [0.47635665, 0.56311456, 0.39038013, 0.41575747, 0.46997203,
        0.50143515],
       [0.59351453, 0.66083208, 0.54221131, 0.36837489, 0.63732373,
        0.51203244]])

 

 

# 각 2차원 배열에서의 각 열의 최솟값
data_3d.min(axis = 1)
array([[0.5842159 , 0.27094533, 0.15908062, 0.09949244, 0.06884772,
        0.14591833],
       [0.1159518 , 0.04522405, 0.23273843, 0.11338679, 0.08601652,
        0.04145205],
       [0.31211153, 0.45171653, 0.17731165, 0.04328172, 0.40835996,
        0.05475567]])

 

Inf 와 nan

  • 넘파이에서는 무한대를 표현하기 위한 np.inf 와 정의할 수 없는 숫자를 나타내는 np.nan 이 있음
np.array([0, 1, -1, 0]) / np.array([1, 0, 0, 0])
C:\Users\ITSC\AppData\Local\Temp\ipykernel_6908\2068585620.py:1: RuntimeWarning: divide by zero encountered in divide
  np.array([0, 1, -1, 0]) / np.array([1, 0, 0, 0])
C:\Users\ITSC\AppData\Local\Temp\ipykernel_6908\2068585620.py:1: RuntimeWarning: invalid value encountered in divide
  np.array([0, 1, -1, 0]) / np.array([1, 0, 0, 0])
[560]:
array([  0.,  inf, -inf,  nan])
728x90

'06_Numpy' 카테고리의 다른 글

Numpy(넘파이)_연습문제  (0) 2025.03.11
02_선형대수 기본  (0) 2025.03.10
넘파이(NumPy) documentation 확인 방법  (0) 2025.03.10